5 Using the Geometry Data Type

5 Using the Geometry Data Type

This chapter describes the geometry data type and how it is used in the IRIS Explorer environment. It defines the geometry data type and explains how geometry is created in IRIS Explorer. It also describes the interface between IRIS Explorer and Open Inventor, a 3D graphics development tool, and explains the role of the Open Inventor scene graph in creating more complex geometry.

The API (Application Programming Interface) routines are listed, and examples of user function code for writing modules that produce or manipulate geometry are included.

5.1 Understanding Geometry

The cxGeometry data type is a root data type, which means it can be specified as a data type on a port and will pass data between modules. It enables IRIS Explorer to handle geometry objects by providing a container for Open Inventor scene graph specifications. Open Inventor is an object-oriented graphics library of objects and methods used to create interactive 3D graphics. The IRIS Explorer Render module and other geometry modules draw on Open Inventor technology. The geometry objects that IRIS Explorer uses to visualize numerical data are created by connecting nodes to form an Open Inventor scene graph. The scene graph constitutes the IRIS Explorer geometry object.

IRIS Explorer allows you to manipulate Open Inventor nodes to form geometry objects through the geometry subroutines in the API. You can output most of the geometry you need in IRIS Explorer by using the Geometry Library, which creates Open Inventor objects, in this way. To create geometry in IRIS Explorer, you need not have Open Inventor installed on your system.

To build a module that reads geometry, that is, that accepts the cxGeometry data type on its input port, you need direct access to Open Inventor and its libraries. You also need Open Inventor if you want to obtain more complex rendering effects than IRIS Explorer can provide. To write a scene graph with direct reference to the Open Inventor library, you must have Open Inventor installed on your system.

You can use Open Inventor with C and C++, but not with Fortran. If you are programming in Fortran, you must use the IRIS Explorer API subroutines for all your geometry needs.

This chapter points you toward Open Inventor for creating complex graphical effects that are outside the scope of IRIS Explorer, but it does not tell you how to use Open Inventor. For more information, refer to the The Inventor Mentor: Programming Object-Oriented 3D Graphics with Open Inventor, Release 2. Addison Wesley, 1994. For information on obtaining Open Inventor, contact one of the IRIS Explorer Centers.

5.2 The Geometry Data Type

The cxGeometry data type is a simple data structure that transcribes an Open Inventor scene graph into a linear data stream called the delta protocol, encoded in an Open Inventor binary file format. It is opaque to IRIS Explorer users. The scene graph can then be moved between modules that are capable of processing it. A scene graph is a hierarchical geometrical construction made of nodes that contain specific details about the scene graph, or geometry object. ‘Scene graph’ is the Open Inventor term, and ‘geometry object’ is the IRIS Explorer term for visualized data.

The delta protocol is so called because it is possible to convey information about changes in the composition of the original scene graph. These changes occur when you fire a module that renders, colors, stretches, reads, or writes geometry. For example, you may use a widget on an upstream module that processes geometry to alter the shape or orientation of an object in the Render module. Some other IRIS Explorer modules that process geometry are ReadGeom, WriteGeom, LatToGeom, PyrToGeom, and VolumeToGeom.

The data type definition follows, although as specified by the ‘closed’ keyword, the contents of this structure are not exposed within the Module Builder, as modules do not need direct access. The Geometry API routines manipulate this data structure internally.

closed shared root typedef struct {
    short    id;
    long     count; */ length of the data stream in bytes */
    char     data[count];  /* the delta protocol */
    long        numBuffers;
    cxMemory    buffers[numBuffers]; /* Shared memory data */
} cxGeometry;

Figure 5-1 shows the structure of the cxGeometry data type.

Schematic Structure of the Geometry Data Type

Figure 5-1 Schematic Structure of the Geometry Data Type


5.3 Creating Geometry Modules

A geometry module can be written that accepts non-geometry data but which creates geometry for output using the IRIS Explorer API routines. A simple example follows. These routines let you create geometry shapes and provide a few basic editing functions, such as changing the color of a geometry object, but they do not let you interpret an incoming delta protocol. To write a module that accepts and reads geometry on its input port, you must use Open Inventor. An introduction to Open Inventor scene graphs is given in Section 5.4 and is followed by an example geometry module that illustrates the use of Open Inventor. If you plan to use only the simple IRIS Explorer API to create geometry, you do not need to understand how Inventor represents scenes. However, a rudimentary knowledge of how Inventor works may help you understand how geometry is manipulated in IRIS Explorer.

5.3.1 An Example Geometry Module

To illustrate how to use the IRIS Explorer API to create geometry in a module, here is a simple module, that creates some line segments. It has only a geometry output port. The user function for the module resides in $EXPLORERHOME/src/MWGcode/Geometry/C++/LineSegment.C. The Fortran version is in $EXPLORERHOME/src/MWGcode/Geometry/Fortran/LineSegment.f. Test the module by connecting it to a Render module, and selecting Fire Now on its pop-up menu (as the module has no input ports, it will not fire of its own accord).

These geometry API routines are introduced in the example:

  • cxGeoInit performs initialisation for the Geometry API and also initialises Open Inventor. It must be called once in a module. This can be done using a static variable as shown or specifying an initialisation hook function, shown in the C example in Section 5.8.1.2.

  • cxGeoNew creates a cxGeometry data structure. The items in the cxGeometry structure are manipulated internally within the Geometry API.

  • cxGeoBufferSelect sets the current cxGeometry structure to be used by the Geometry API, that is its data array contains the delta protocol resulting from the cxGeo calls that follow.

  • cxGeoBufferPortSet must be called after cxGeoBufferSelect and before any further cxGeo calls. It specifies in advance on which port the cxGeometry data will be placed. This allows the geometry API to determine a method of transcription of the geometry objects, dependent on the port's current connections. This is discussed further in Section 5.5.2

  • cxGeoRoot, cxGeoDelete calls are required to delete the previously created objects in the downstream module(s) as each firing of this module recreates all geometry.

  • cxGeoLinesDefine creates a geometry object which is a set of lines, and is identified by id. This is now the current geometry object. Examples of other geometry objects that may be created are polygons, cones, cylinders, spheres, b-spline curves and patches. Attributes such as color, style, transparency and lighting model can be added to the current geometry object using API calls. To change attributes of previously created objects the current geometry object can be set using the call cxGeoFocus.

  • cxGeoBufferClose must be called to indicate the end of the delta protocol

/* This example creates some line segments and */
/* outputs them as geometry   */
/* */
#include <cx/DataTypes.h>
#include <cx/DataAccess.h>
#include <cx/Geometry.h>
#include <cx/PortAccess.h>

static float p[] = {
    0.0, 0.0, 0.0,
    1.0, 0.0, 0.0,
    1.0, 1.0, 0.0,
    0.0, 1.0, 0.0,
    0.0, 1.0, 1.0,
    1.0, 1.0, 1.0,
    1.0, 0.0, 1.0,
    0.0, 0.0, 1.0
};

static long index[] = {
    0, 1, 2, 3, 0, 7, 6, 5, 4, 7, -1,
    1, 6, -1,
    2, 5, -1,
    3, 4, -1
};

extern "C" void line(cxGeometry **geom)
{
/* initialize geometry library once only */
 static int flag = 1;
 if ( flag )
 {
    flag = 0;
    cxGeoInit();
 }

  /* Create a new Geometry data type*/
     *geom = cxGeoNew();

  /* Make the buffer in this Geometry data be used to store the
     geometry created by following geometry creation calls, i.e.,
     the cxGeoLinesDefine call */
     cxGeoBufferSelect(*geom);

  /* Get the port for Geometry output */
     int port = cxOutputPortOpen("Output");

  /* Associate the Geometry data with this port */
     cxGeoBufferPortSet(port);

  /* set the current geometry object to be the root object and then
     delete all geometry created by previous module firings */
     cxGeoRoot();
     cxGeoDelete();

  /* Create a new geometry object */
     cxGeo id = cxGeoLinesDefine(8, p, 20, index);

  /* Close the buffer in the Geometry data, in preparation for
     it to be output on the port */
     cxGeoBufferClose(*geom);
}

5.4 Understanding Scene Graphs

A scene graph is the Inventor term for a hierarchical, 3D description of scene data. It consists of an ordered collection of nodes, each of which defines a specific shape (or geometry), property, or grouping aspect of the scene. The hierarchy is created by adding sublevels of nodes to the top-level or root node in a scene graph, creating a directed acyclic graph. Figure 5-2 shows a simple scene graph. The heavy line running from the root node down the right-hand branch to the shape node shows the hierarchical path or progression in the graph.

The node[5] is the basic building block for creating scene graphs in Inventor. Each node holds a piece of information, such as a shape description, a geometric transformation, a camera position, or a light source. Each scene graph has a root node, which is the first node you create. You can apply actions to a scene graph, such as rendering, picking, and writing to a file. The IRIS Explorer Render module renders numerical data in the form of a scene graph.

Inventor Scene Graph

Figure 5-2 Inventor Scene Graph


Nodes fall into three categories: shape nodes, which represent 3D geometric objects; property nodes, which represent qualitative values, such as appearance; and group nodes, which collect single nodes into graphs.

Each node has a set of fields that describe the parameters of the node. For example, a node defining a point light source has three fields, intensity, color, and location. Each of these fields can be given a value (location) or range of values (intensity). The order in which nodes are arranged is important because it determines how the values that each node contains are acted upon.

5.5 Using Open Inventor in Geometry Modules

5.5.1 Creating Geometry

Calls to the Geometry API create Inventor nodes and simultaneously transcribe the node data into the delta protocol stored in the cxGeometry data buffer. Inventor classes can be used directly to create a scene graph within an IRIS Explorer module. To move this scene graph to another module it must still be transcribed into the cxGeometry data buffer.

The routine cxGeoInventorDefine is used to perform the transcription of the root node and all nodes beneath it in the hierarchy. This call will also attach the given root node to the scene graph within the geometry API – when cxGeoInit is called a root node is always created. Standard Open Inventor binary transcription is performed using objects of classes SoTranSender and SoOutput.

A set of Open Inventor node classes have been implemented for use solely within IRIS Explorer. These classes, whose names are prefixed cxSo, for example cxSoIndexedFaceSet, have identical functionality to the corresponding standard Inventor node classes with So names. The difference is that they use new field classes derived from the standard Open Inventor multi-valued field classes. These new field classes, again prefixed cxSo, for example cxSoMFLong, store their data in the IRIS Explorer shared memory arena, when available. See Section 9.6 for further discussion of shared memory.

These cxSo nodes are used when creating geometry within the Geometry API. To obtain the performance advantage of using shared memory when moving scene graphs between modules you should create nodes with these classes when using Open Inventor directly within IRIS Explorer modules. Simply replace the declaration of an So node class with the corresponding cxSo class.

To illustrate the use of Inventor, including the cxSo nodes, within an IRIS Explorer module, here is an example that creates a set of polygons. The classes cxSoCoordinate3, cxSoMaterial and cxSoNormal are used, therefore the appropriate header files in cx/nodes are included. The module has a geometry output port. The user function for the module resides in $EXPLORERHOME/src/MWGcode/Geometry/C++/FaceSet.C. Test the module by connecting it to a Render module, and selecting Fire Now on its pop-up menu (as the module has no input ports, it will not fire of its own accord).

// This example creates a Face Set using the Open Inventor
// library and the Explorer extension classes whose field
// data is stored in Explorer shared memory

#include <cx/DataAccess.h>
#include <cx/DataTypes.h>
#include <cx/Geometry.h>
#include <cx/PortAccess.h>

#include <cx/nodes/cxSoCoordinate3.h>
#include <cx/nodes/cxSoMaterial.h>
#include <cx/nodes/cxSoNormal.h>

#include <Inventor/nodes/SoFaceSet.h>
#include <Inventor/nodes/SoNormalBinding.h>
#include <Inventor/nodes/SoSeparator.h>

//  Eight polygons. The first four are triangles
//  The second four are quadrilaterals for the sides.
static const float vertices[28][3] =
{
   { 0, 9, 0}, {-2,6, 2}, { 2,6, 2},            //front tri
   { 0, 9, 0}, {-2,6,-2}, {-2,6, 2},            //left  tri
   { 0, 9, 0}, { 2,6,-2}, {-2,6,-2},            //rear  tri
   { 0, 9, 0}, { 2,6, 2}, { 2,6,-2},            //right tri
   {-2, 6, 2}, {-4,0, 4}, { 4,0, 4}, { 2,6, 2},  //front quad
   {-2, 6,-2}, {-4,0,-4}, {-4,0, 4}, {-2,6, 2},  //left  quad
   { 2, 6,-2}, { 4,0,-4}, {-4,0,-4}, {-2,6,-2},  //rear  quad
   { 2, 6, 2}, { 4,0, 4}, { 4,0,-4}, { 2,6,-2}   //right quad
};

// Number of vertices in each polygon:
static int32_t numvertices[8] = {3, 3, 3, 3, 4, 4, 4, 4};

// Normals for each polygon:
static const float norms[8][3] =
{
   {0, .555,  .832}, {-.832, .555, 0}, //front, left tris
   {0, .555, -.832}, { .832, .555, 0}, //rear, right tris

   {0, .0739,  .9973}, {-.9972, .0739, 0},//front, left quads
   {0, .0739, -.9973}, { .9972, .0739, 0},//rear, right quads
};

extern "C" {
   void makeFaceSet(
    cxGeometry **geo)
{

// initialize geometry api once only
// this creates a root node
   static int first = 1;
   if ( first )
   {
    first = 0;
    cxGeoInit();
   }

// Create a new Geometry data type
   *geo = cxGeoNew();

// Make the buffer in this Geometry data be used to store the
// geometry created
   cxGeoBufferSelect(*geo);

// Get the port for Geometry output and associate
// the Geometry data with this port
   cxGeoBufferPortSet(cxOutputPortOpen("Face Set"));

// set the current geometry object to be the root node and
// delete all geometry created by previous module firings
// below the root node
   cxGeoRoot();
   cxGeoDelete();

// create the Inventor Scene using the Explorer nodes
// so that their field data is stored in shared memory

   SoSeparator *topNode= new SoSeparator();
   topNode->ref();

// Define the normals used:
   cxSoNormal *myNormals = new cxSoNormal;
   myNormals->vector.setValues(0, 8, norms);
   topNode->addChild(myNormals);
   SoNormalBinding *myNormalBinding = new SoNormalBinding;
   myNormalBinding->value = SoNormalBinding::PER_FACE;
   topNode->addChild(myNormalBinding);

// Define material for topNode
   cxSoMaterial *myMaterial = new cxSoMaterial;
   myMaterial->diffuseColor.setValue(.4, .4, .4);
   topNode->addChild(myMaterial);

// Define coordinates for vertices
   cxSoCoordinate3 *myCoords = new cxSoCoordinate3;
   myCoords->point.setValues(0, 28, vertices);
   topNode->addChild(myCoords);

// Define the FaceSet
   SoFaceSet *myFaceSet = new SoFaceSet;
   myFaceSet->numVertices.setValues(0, 8, numvertices);
   topNode->addChild(myFaceSet);

// Add this Scene Graph to the Geometry api's root node
// and add to the Geometry buffer
   cxGeoInventorDefine(topNode);

// Decrement reference count as it is now referenced by root node
   topNode->unref();

// Close the buffer in the Geometry data, in preparation for
// it to be output on the port
   cxGeoBufferClose(*geo);
}
}

5.5.2 IRIS Explorer Node Classes

The cxSo node classes that are available are listed below, together with the cxSo field class used for the attribute data of the node.

The transcription of the Inventor Scene Graph into the Geometry data buffer differs for cxSo nodes. It is not necessary to transcribe all the field data into the binary data stream, only the pointer to memory is added when shared memory is appropriate for the module to module connection. It is this that gives the performance advantage when moving scene graphs between modules. For this reason it is necessary to identify which port the cxGeometry data will be placed on before making further cxGeo calls, so that the correct transcription method can be used.

Table 5-1 lists the cxSo node classes and referenced cxSo field classes.

Table 5-1 IRIS Explorer Shared Memory Node Classes

cxSo Node Attribute Name cxSo Field
cxSoColorIndex index cxSoMFLong
cxSoCoordinate3 point cxSoMFVec3f
cxSoCoordinate4 point cxSoMFVec4f
cxSoIndexedFaceSet    
cxSoIndexedLineSet    
cxSoIndexedShape materialIndex cxSoMFLong
  normalIndex cxSoMFLong
  textureCoordIndex cxSoMFLong
cxSoIndexedTriangleStripSet    
cxSoMaterial ambientColor cxSoMFColor
  diffuseColor cxSoMFColor
  specularColor cxSoMFColor
  emissiveColor cxSoMFColor
  shininess cxSoMFFloat
  transparency cxSoMFFloat
cxSoMaterialIndex ambientIndex cxSoMFLong
  diffuseIndex cxSoMFLong
  specularIndex cxSoMFLong
  shininess cxSoMFFloat
  transparency cxSoMFFloat
cxSoNormal vector cxSoMFVec3f
cxSoPackedColor rgba cxSoMFULong

5.5.3 Receiving Geometry

Open Inventor must be used in modules that receive geometry on input ports. An Inventor receiver object is required to decode the incoming data from a specific upstream module and create the resulting Scene Graph. The API function cxGeoReceiverNew creates this object, and cxGeoReceive performs the decoding from the given cxGeometry object. The incoming scene graph is attached to a parent node that has been previously created.

A different receiver object should be used for each geometry connection. This prevents data updates from one module being passed to nodes that contain data from another module. The parent node/receiver object pairs control the distribution of new data to the correct nodes.

5.6 Data Type Declaration

The cxGeometry data type, though defined in the IRIS Explorer typing language, can be considered as a C structure. However the cxGeometry data type is usually accessed internally via the geometry API functions. It should not be necessary for module user code to access the data structure directly. The type declaration resides in the header file $EXPLORERHOME/include/cx/cxGeometry.h.

The cxGeometry data type structure is defined as follows:

typedef struct cxMemory {
    cxDataCtlr       ctlr;
    long              n;
    unsigned char    *mem;
} cxMemory;

typedef struct cxGeometry {
    cxDataCtlr       ctlr;
    short             id;
    long              count;
    unsigned char    *data;
    long              numBuffers;
    cxMemory         **buffers;
} cxGeometry;

5.7 The Geometry API Routines

The cxGeometry data type has its own API subroutine library that enables you to write simple IRIS Explorer modules that output scene graphs without manipulating Open Inventor code. You can create and manipulate the geometry objects by using the geometry subroutines listed below. To create more complex modules, you can use Open Inventor directly.

The geometry subroutines are described in the IRIS Explorer Reference Pages. Table 5-2 lists the subroutines and briefly defines each one.

Table 5-2 Geometry Subroutines

Subroutine Purpose
cxGeoNew Creates a new IRIS Explorer geometry data structure
cxGeoInit Initializes the IRIS Explorer Geometry Library
cxGeoPointsDefine Creates a set of points
cxGeoLinesDefine Creates a set of unclosed polylines
cxGeoPolysDefine Creates a set of polygons
cxGeoSplineDefine Creates a NURB spline
cxGeoTrisDefine Creates a set of triangle meshes
cxGeoPatchDefine Creates a NURBS patch
cxGeoSpheresDefine Creates a set of spheres
cxGeoConesDefine Creates a set of cones
cxGeoCylindersDefine Creates a set of cylinders
cxGeoGridDefine Creates a grid geometry object
cxGeoTextDefine Creates a text object
cxGeoOctreeDefine Creates a volume object rendered as an octree
cxGeoInventorDefine Creates an entire Open Inventor scene graph
cxGeoNormalAdd Adds normals to the current geometry object
cxGeoColorAdd Adds colors to the current geometry object
cxGeoABGRAdd Adds packed (32-bit) colors to the current geometry object
cxGeoComplexityAdd Specifies the level of rendering detail
cxGeoLabelAdd Adds a label to the current geometry object
cxGeoStyleAdd Adds a drawing style to the current geometry object
cxGeoTransparencyAdd Adds transparency to the current geometry object
cxGeoLightModelAdd Specifies the lighting model for the current geometry object
cxGeoXformPop Makes the current object the parent of the current transform group
cxGeoXformPush Groups objects under a parent hierarchy object
cxGeoIdentity Resets the transform on the current object
cxGeoTranslate Translates the current geometry object
cxGeoRotate Rotates the current geometry object
cxGeoScale Scales the current geometry object
cxGeoMatrixCat Applies an arbitrary 4x4 transform to the object
cxGeoReceive Receives IRIS Explorer input data for transcription
cxGeoFocus Specifies the current object or transform for editing
cxGeoRoot Makes the root object the current object
cxGeoDelete Makes the root object the current object
cxGeoBufferClose Prepares a geometry buffer to be sent
cxGeoBufferPortSet Associates a port with the current geometry buffer
cxGeoBufferSelect Specifies a geometry buffer for subsequent scene edits

5.8 Code Examples

The examples that follow show the use of both IRIS Explorer subroutines and Open Inventor classes to create and manipulate geometry in modules.

5.8.1 Modules using the Geometry API

5.8.1.1 Creating Line Segments

This example is the Fortran version of the previous C++ example that creates some line segments and outputs them as geometry. The code is in $EXPLORERHOME/src/MWGcode/Geometry/Fortran/LineSegment.f. Test the module by connecting it to a Render module, and selecting Fire Now on its pop-up menu (as the module has no input ports, it will not fire of its own accord).

C This module creates some line segments and outputs them as geometry

      subroutine linef ( h_geom )

#if defined(IS_64BIT)
      integer*8 h_geom
      integer*8 index(20)
#else
      integer   h_geom
      integer   index(20)
#endif

C
C Please complete the user function by adding subprogram lines here.
C

      include '/usr/explorer/include/cx/DataAccess.inc'
      include '/usr/explorer/include/cx/Geometry.inc'
      include '/usr/explorer/include/cx/PortAccess.inc'
      integer   flag
      real  p(24)

      integer port, id

      data flag/1/
      data p/0.0, 0.0, 0.0,
     +  1.0, 0.0, 0.0,
     +  1.0, 1.0, 0.0,
     +  0.0, 1.0, 0.0,
     +  0.0, 1.0, 1.0,
     +  1.0, 1.0, 1.0,
     +  1.0, 0.0, 1.0,
     +  0.0, 0.0, 1.0/
      data index/0, 1, 2, 3, 0, 7, 6, 5, 4, 7, -1,
     +           1, 6, -1,
     +           2, 5, -1,
     +           3, 4, -1/

C  initialize geometry library once only
      if ( flag .eq. 1 ) then
          flag = 0
      call cxGeoInit()
      endif
C
C  Create a new Geometry data type
      h_geom = cxGeoNew()
C
C  Make the buffer in this Geometry data be used to store the
C  geometry created by following geometry creation calls, i.e.,
C  the cxGeoLinesDefine call
      call cxGeoBufferSelect(h_geom)
C
C  Get the port for Geometry output
      port = cxOutputPortOpen("Output")
C
C  Associate the Geometry data with this port
      call cxGeoBufferPortSet(port)
C
C  set the current geometry object to be the root object and then
C  delete all geometry created by previous module firings
      call cxGeoRoot()
      call cxGeoDelete()
C
C  Create a new geometry object
      id = cxGeoLinesDefine(8,p,20,index)
C
C  Close the buffer in the Geometry data, in preparation for
C  it to be output on the port
      call cxGeoBufferClose(h_geom)
C
      RETURN
      END

5.8.1.2 Creating More Geometry Objects

The following example creates spheres, lines and polygons. The code is in $EXPLORERHOME/src/MWGcode/Geometry/C/PolySegment.c and $EXPLORERHOME/src/MWGcode/Geometry/Fortran/PolySegment.f. Test it by attaching GenerateColormap to its input port, and Render to its output port.

Example 5-1 C Version of the PolySegment Module:

/* This example uses the API to create geometry and attach attributes
 * to the geometry
 * The module data wrapper is not used in this module,
   port acces is therefore via api calls */

#include <stdio.h>

#include <cx/DataTypes.h>
#include <cx/DataAccess.h>
#include <cx/PortAccess.h>
#include <cx/UserFuncs.h>
#include <cx/Geometry.h>
#include <cx/Lookup.h>

float lins[] = {0.,0.,0., 0.,0.,1., 0.,1.,1., 0.,1.,0.,
     1.,0.,0., 1.,0.,1., 1.,1.,1., 1.,1.,0. };

long conn[] = {0,1,2,3,0,4,5,6,7,4,-1,1,5,-1,2,6,-1,3,7,-1};
long conn2[] = {0,1,5,4,-1,2,3,7,6,-1};

void display()
{
 cxLattice *cmap;    /* Color map */
 cxGeometry *geo;
 cxLookup *clut;
 float col[4],sphs[10][3],rads[10],x;
 int i,j;

 /* Get lattice for colormap from port and create lookup table */
 cmap = (cxLattice*)cxInputDataGet(cxInputPortOpen("colormap"));
 if(cmap == NULL){
   printf("geometry: no color map present\n");
   return;
 }
 clut = cxLookupCreate(cmap,1);

 /* Create Geometry data type and set its buffer for the
    output of geometry */
 geo = cxGeoNew();
 if( cxDataAllocErrorGet() ) goto cleanupGeo;
 cxGeoBufferSelect(geo);
 /* set the port on which this geometry will be sent */
 cxGeoBufferPortSet(cxOutputPortOpen("geom"));

 /* Delete all geometry created by previous module firing */
 cxGeoRoot();
 if( cxDataAllocErrorGet() ) goto cleanupSelected;
 cxGeoDelete();
 if( cxDataAllocErrorGet() ) goto cleanupSelected;

 /* Set radii and positions of spheres  and create the spheres*/
 for(i=0;i<10;i++){
   for(j=0;j<3;j++){
    sphs[i][j] = i;
   }
   rads[i] = i / 10.0 + 0.01;
 }

 cxGeoSpheresDefine(5,sphs[0],rads);
 if( cxDataAllocErrorGet() ) goto cleanupSelected;
 x = 0.8;
 cxLookupInterp(clut,&x,(void *)col);
 cxGeoColorAdd(1,col,CX_GEO_PER_OBJECT);
 if( cxDataAllocErrorGet() ) goto cleanupSelected;

 /* Create the lines of the cube */
 cxGeoLinesDefine(8,lins,20,conn);
 if( cxDataAllocErrorGet() ) goto cleanupSelected;
 x = 0.5;
 cxLookupInterp(clut,&x,(void *)col);
 cxGeoColorAdd(1,col,CX_GEO_PER_OBJECT);
 if( cxDataAllocErrorGet() ) goto cleanupSelected;

 /* Create the polygons of the cube */
 cxGeoPolysDefine(8,lins,10,conn2);
 if( cxDataAllocErrorGet() ) goto cleanupSelected;
 x = 0.2;
 cxLookupInterp(clut,&x,(void *)col);
 cxGeoColorAdd(1,col,CX_GEO_PER_OBJECT);
 if( cxDataAllocErrorGet() ) goto cleanupSelected;

 /* Close the buffer of the geometry data - ready for
    sending on the port */
 cxGeoBufferClose(geo);
 cxOutputDataSet(cxOutputPortOpen("geom"),geo);

 cxLookupDestroy(clut);

 return;

 /* Handle error conditions */
cleanupSelected:
 cxGeoBufferClose(geo);
cleanupGeo:
 cxDataRefDec(geo);
 cxLookupDestroy(clut);
 return;
}

/* Function specified as an 'Init Hook Function'
   in the Module Builder Build/Hook Functions option */
void geom_init()
{
 /* Initialise the geometry api first time the module fires */
 cxGeoInit();
}

Example 5-2 Fortran Version of the PolySegment Module:

C     This example uses the API to create geometry and attach attributes
C     to the geometry
C
      SUBROUTINE DSPLAY(CMAP,GEO)
      INCLUDE '/usr/explorer/include/cx/DataAccess.inc'
      INCLUDE '/usr/explorer/include/cx/Lookup.inc'
      INCLUDE '/usr/explorer/include/cx/PortAccess.inc'
C
C     .. Parameters ..
      INTEGER           CX_GEO_PER_OBJECT
      PARAMETER         (CX_GEO_PER_OBJECT=1)
C     .. Scalar Arguments ..
#if defined(IS_64BIT)
      INTEGER*8         CMAP, GEO, CLUT
      INTEGER*8         CONN(20), CONN2(10)
#else
      INTEGER           CMAP, GEO, CLUT
      INTEGER           CONN(20), CONN2(10)
#endif
C     .. Local Scalars ..
      REAL              X
      INTEGER           I, IER, J
      LOGICAL           FIRST
C     .. Local Arrays ..
      REAL              COL(4), LINS(3,8), RADS(10), SPHS(3,10)

C     .. External Subroutines ..
      EXTERNAL          CXDATAREFDEC, CXGEOBUFFERCLOSE,
     *                  CXGEOBUFFERPORTSET, CXGEOBUFFERSELECT,
     *                  CXGEOCOLORADD, CXGEODELETE, CXGEOINIT,
     *                  CXGEOLINESDEFINE, CXGEOPOLYSDEFINE, CXGEOROOT,
     *                  CXGEOSPHERESDEFINE, CXLOOKUPDESTROY
C     .. External Functions ..
      EXTERNAL          CXDATAALLOCERRORGET, CXGEONEW, CXLOOKUPCREATE,
     *                  CXLOOKUPINTERP, CXOUTPUTPORTOPEN
C     .. Intrinsic Functions ..
      INTRINSIC         REAL
C     .. Data statements ..
      DATA              LINS/0., 0., 0., 0., 0., 1., 0., 1., 1., 0., 1.,
     *                  0., 1., 0., 0., 1., 0., 1., 1., 1., 1., 1., 1.,
     *                  0./
      DATA              CONN/0, 1, 2, 3, 0, 4, 5, 6, 7, 4, -1, 1, 5, -1,
     *                  2, 6, -1, 3, 7, -1/
      DATA              CONN2/0, 1, 5, 4, -1, 2, 3, 7, 6, -1/
      DATA              FIRST/.TRUE./
C     .. Executable Statements ..
C
C     Initialise the geometry api first time the module fires
      IF (FIRST) THEN
         CALL CXGEOINIT
         FIRST = .FALSE.
      END IF
C
C     Create color lookup table from input lattice
      CLUT = CXLOOKUPCREATE(CMAP,1)
C
C     Create Geometry data type and set its buffer for the
C     output of Geometry
      GEO = CXGEONEW()
      IF (CXDATAALLOCERRORGET().NE.0) GO TO 80
      CALL CXGEOBUFFERSELECT(GEO)
C     set the port on which this geometry will be sent
      CALL CXGEOBUFFERPORTSET(CXOUTPUTPORTOPEN('geom'))
C
C     Delete all geometry created by previous module firing
      CALL CXGEOROOT
      IF (CXDATAALLOCERRORGET().NE.0) GO TO 60
      CALL CXGEODELETE
C
C     Set radii and positions of spheres  and create the spheres
C
      DO 40 I = 1, 10
         DO 20 J = 1, 3
            SPHS(J,I) = I
   20    CONTINUE
         RADS(I) = REAL(I)/10.0
   40 CONTINUE
C
      CALL CXGEOSPHERESDEFINE(5,SPHS,RADS)
      IF (CXDATAALLOCERRORGET().NE.0) GO TO 60
      X = 0.8
      IER = CXLOOKUPINTERP(CLUT,X,COL)
      CALL CXGEOCOLORADD(1,COL,CX_GEO_PER_OBJECT)
      IF (CXDATAALLOCERRORGET().NE.0) GO TO 60
C
C     Create the lines of the cube
C
      CALL CXGEOLINESDEFINE(8,LINS,20,CONN)
      IF (CXDATAALLOCERRORGET().NE.0) GO TO 60
      X = 0.5
      IER = CXLOOKUPINTERP(CLUT,X,COL)
      CALL CXGEOCOLORADD(1,COL,CX_GEO_PER_OBJECT)
      IF (CXDATAALLOCERRORGET().NE.0) GO TO 60
C
C     Create the polygons of the cube
C
      CALL CXGEOPOLYSDEFINE(8,LINS,10,CONN2)
      IF (CXDATAALLOCERRORGET().NE.0) GO TO 60
      X = 0.2
      IER = CXLOOKUPINTERP(CLUT,X,COL)
      CALL CXGEOCOLORADD(1,COL,CX_GEO_PER_OBJECT)
      IF (CXDATAALLOCERRORGET().NE.0) GO TO 60
C
C     Close the buffer of the geometry data - ready for
C     sending on the port
C
      CALL CXGEOBUFFERCLOSE(GEO)
C
      CALL CXLOOKUPDESTROY(CLUT)
      RETURN
C
C     memory allocation error cleanup
C
   60 CONTINUE
      CALL CXGEOBUFFERCLOSE(GEO)
   80 CONTINUE
      CALL CXDATAREFDEC(GEO)
      CALL CXLOOKUPDESTROY(CLUT)
      RETURN
      END

Figure 5-3 shows what the module output looks like.

Output from Geometry Example

Figure 5-3 Output from Geometry Example


5.8.2 Creating Modules with Open Inventor Code

Here are two examples of a user function showing how you can use Open Inventor to create and display geometry objects. Neither example has a Fortran equivalent because Open Inventor has no Fortran API.

5.8.2.1 Texturing a Sphere

This is a simple example of how to use the Open Inventor library, written in C++. The function wraps an image onto a sphere. The code is in $EXPLORERHOME/src/MWGcode/Geometry/C++/Texture.C. Test it by attaching Render to its output port, and typing the name for a texture file in the slot named Texture Filename. One such file is $EXPLORERHOME/lib/reconstruction.bw.

// This example creates a sphere and wraps an image
// onto it, using the Open Inventor library. This
// Inventor scene graph is then added to the Explorer
// Geometry buffer.

#include <cx/DataAccess.h>
#include <cx/DataTypes.h>
#include <cx/Geometry.h>
#include <cx/PortAccess.h>
#include <Inventor/nodes/SoSphere.h>
#include <Inventor/nodes/SoTexture2.h>
#include <Inventor/nodes/SoSeparator.h>
#include <cx/nodes/cxSoMaterial.h>

extern "C" {
  void  makeSphere  (
     float        radius,
     char        *texfile,
     cxGeometry *   * geo )
{
// initialize geometry api once only
// this creates a root node
  static int first = 1;
  if ( first )
  {
    first = 0;
    cxGeoInit();
  }

// Create a new Geometry data type
  *geo = cxGeoNew();

// Make the buffer in this Geometry data be used to store the
// geometry created
  cxGeoBufferSelect(*geo);

// Get the port for Geometry output and associate
// the Geometry data with this port
  cxGeoBufferPortSet(cxOutputPortOpen("Texture Geometry"));

// set the current geometry object to be the root node and
// delete all geometry created by previous module firings
// below the root node
  cxGeoRoot();
  cxGeoDelete();

// create the Inventor Scene
  SoGroup *topNode = new SoSeparator();
  topNode->ref();
  SoSphere *sph = new SoSphere();
  SoTexture2 *tex = new SoTexture2();
// Use the Explorer Material class so that its data is
// stored in shared memory
  cxSoMaterial *m = new cxSoMaterial();
  m->diffuseColor.setValue(1.0,1.0,1.0);
  tex->filename.setValue(texfile);
  sph->radius = radius;
  topNode->addChild(tex);
  topNode->addChild(m);
  topNode->addChild(sph);

// Add this Scene Graph to the Geometry api's root node
// and add to the Geometry buffer
  cxGeoInventorDefine(topNode);

// Decrement reference count as it is now referenced by root node
  topNode->unref();

// Close the buffer in the Geometry data, in preparation for
// it to be output on the port
  cxGeoBufferClose(*geo);
}
}

5.8.2.2 A Simple Renderer

This is a reasonably small module, written in C++, that implements a geometry renderer using the Open Inventor library. The code is in $EXPLORERHOME/src/RenderLite/RenderLite.cpp

For simplicity, several limitations have been accepted:

  • The rendering window does not downsize very gracefully. The real renderer turns off decoration when the window goes below a certain size.

  • Only one geometry input is supported. This module can have fatal namespace collisions with fanned-in inputs, though in practice it will usually work. The proper way to handle multiple geometry streams is to use a different receiver object for each one.

  • The viewer in this module is very simple and does not support any editing or picking facilities.

#ifndef WIN32
#include <X11/Intrinsic.h>
#else
#define NO_STRICT
#endif

#include <Inventor/misc/SoTranscribe.h>
#include <Inventor/Xt/SoXt.h>
#include <Inventor/Xt/viewers/SoXtExaminerViewer.h>
#include <Inventor/nodes/SoDirectionalLight.h>
#include <Inventor/nodes/SoGroup.h>
#include <Inventor/nodes/SoLightModel.h>
#include <Inventor/nodes/SoOrthographicCamera.h>
#include <Inventor/nodes/SoPerspectiveCamera.h>
#include <Inventor/nodes/SoSeparator.h>
#include <Inventor/nodes/SoShapeHints.h>

#include <cx/DataAccess.h>
#include <cx/DataTypes.h>
#include <cx/Geometry.h>
#include <cx/PortAccess.h>
#include <cx/UserFuncs.h>

#ifndef WIN32
#include <cx/XtArea.h>
#endif

Widget top;               /* top level widget for drawing area */
SoGroup *group;           /* root for incoming geometry */
SoTranReceiver *receiver; /* receiver object for incoming geometry */
SoXtViewer *viewer;       /* Inventor viewer */

/*
  This reasonably small module implements a geometry renderer using
  the IRIS Inventor library. For simplicity, several limitations
  have been accepted:

  1. The rendering window does not downsize very gracefully. The
  real renderer turns off decoration when the window goes below a
  certain size.

  2. Only one geometry input is supported. This module can have
  fatal namespace collisions with fanned-in inputs, though in practice
  this will usually work. The proper way to handle multiple geometry
  streams is to use a different receiver object for each one.

  3. The viewer in this module is very simple, and does not support
  any editing or picking facilities.
*/
extern "C" void renderlite(
  cxGeometry *geo,  /* incoming geometry */
  int geoFlag,      /* nonzero if geometry is new */
  long window,      /* window id of drawing area */
  int windowFlag)   /* nonzero if window is new */
{
  static int first = 1;    /* initialization flag */
  if ( first && window )
  {
    /* reset initialization flag */
    first = 0;

    /* initialize Xt and Inventor */
    cxGeoInit();
#ifdef WIN32
    SoXt::init("RenderLite");
#else
    top = cxXtAreaInitialize();
    SoXt::init(top);
#endif

    /* create the environment */
    SoGroup *root = new SoSeparator();
    root-&gt;ref();

    /* camera */
    SoCamera *camera = new SoOrthographicCamera();
    root-&gt;addChild(camera);

    /* shape hints */
    SoShapeHints *n1 = new SoShapeHints();
    n1-&gt;vertexOrdering.setValue(SoShapeHints::COUNTERCLOCKWISE);
    root-&gt;addChild(n1);

    /* create the node for the incoming geometry */
    group = new SoGroup();
    root-&gt;addChild(group);

    /* create the receiver object */
    receiver = new SoTranReceiver(group);

    /* create the viewer */
#ifdef WIN32
    viewer = new SoXtExaminerViewer((Widget)window);
#else
    viewer = new SoXtExaminerViewer(top);
#endif
    viewer-&gt;setSceneGraph(root);
#ifndef WIN32
    viewer-&gt;setSize(SbVec2s(100,100));
#endif
    viewer-&gt;show();

#ifndef WIN32
    /* attach the viewer widget to the Explorer Form widget */
    Widget vw = viewer-&gt;getWidget();
    XtVaSetValues(vw,
      XmNtopAttachment,XmATTACH_FORM,
      XmNbottomAttachment,XmATTACH_FORM,
      XmNrightAttachment,XmATTACH_FORM,
      XmNleftAttachment,XmATTACH_FORM,
      NULL);

    /* attach widget to window */
    cxXtAreaAttach(top,window);
#endif
  }

#ifndef WIN32
  if ( windowFlag )  /* window has changed state or size */
    cxXtAreaResize(top,window);
#endif

  if ( geoFlag )    /* new geometry has arrived */
  {
    cxGeoReceive(geo,receiver);
    viewer-&gt;viewAll();
  }
}

[5] ‘Node’ is used here in a different sense from the nodes in a lattice.