Read this chapter if you will be programming in C! If you are unfamiliar with object-oriented programming, you should first read Appendix A, which introduces some of the features of C++ and some of the conceptual underpinnings of the Open Inventor C interface.
Though the C++ syntax is different, the concepts described in earlier chapters of this book still apply to the C implementation of Open Inventor. As you read the rest of this book, you can refer to this appendix as necessary to translate the C++ examples into C. If you have purchased Open Inventor, you can also compare the C version of each online example program to the C++ version in this book. See the release notes for information on finding and using the online example programs and for details on how to compile and link an Inventor program written in C.
The following sections explain the differences between the Open Inventor C and C++ interfaces. They discuss these topics:
The Open Inventor C interface is generated by an automated translation program. This interface defines a C structure for each C++ class, which is actually a direct mapping to the C++ class. This structure is defined in the C include file for the class. Fields within the C structure are either hidden or public. Public fields (member variables) are documented in the reference manual page for each class. Hidden fields (private member variables) are named pad[]. You should not modify hidden fields.
The sample code fragments in these sections are drawn from Chapter 2.
The name of each C function is the C++ member function prefixed by the class name. Suppose there were a class named SoGenericType, with a member function doOp(). The C name for that function would be SoGenericTypeDoOp(). The object to be operated upon by the member function appears as the first argument to the C version of the function. A C prototype for our generic hypothetical function would look something like this:
SoGenericTypeDoOp(SoGenericType *); |
To give a more specific example, the function to add a node to a group looks like this in C++ and C:
C++:
root->addChild(material); C: SoGroupAddChild(root, (SoNode *) material); |
The C function name has the prefix SoGroup. Since addChild() is a member function of SoGroup, the first argument to the C function must be a pointer to an object of type SoGroup. Since addChild() expects a pointer to an object of type SoNode as its second argument, use a cast to SoNode * if you need to.
Now, suppose that you want to apply the function doOp() to a member of a subclass of SoGenericType, like SoGenericSubType. The C subclasses inherit all the member functions of their parent classes in the form of macro functions. The macros simply call the parent class function and cast the object to the parent's object type. The names of the inherited macro functions, though, are prefixed with the name of the subclass, not the parent class. So the C function name for doOp() would be SoGenericSubTypeDoOp(). You could use the parent class funtion, SoGenericTypeDoOp(), if you cast the object to SoGenericType.
In the real Inventor example addChild(), you're more likely to be adding children to subclasses of SoGroup. For example, the C macro function to add a child node to a separator is
C:
SoSepAddChild(separator, (SoNode *) material); |
Here is another example of an Open Inventor C function:
C++:
viewer = new SoXtExaminerViewer(myWindow); viewer->setSceneGraph(root); C: viewer = SoXtExamVwrCreateStd(myWindow, NULL); SoXtExamVwrSetScene(viewer, (SoNode *) root); |
The C++ function setSceneGraph() is a member of the class SoXtRenderArea, but the viewer is a pointer to type SoXtExamVwr. So the C macro function name is prefixed with SoXtExamVwr and has a pointer to an object of that type as its first argument.
But as you can see, the C function name is abbreviated. The function in the preceding example, if it were unabbreviated, would be SoXtExaminer-ViewerSetScene(), which is long and unwieldy. Table B-1 lists the abbreviations used in the C function names. Whenever the C++ function name contains one of the strings listed in the table, the corresponding C function uses the abbreviation in the second column.
Table B-1. C Function Name Abbreviations
C++ Function Name | Abbreviation |
---|---|
Action | Act |
Attenuation | Atten |
Binding | Bind |
BoundingBox | BBox |
Button | Btn |
Callback | CB |
Camera | Cam |
Catalog | Cat |
Character | Char |
Color | Col |
Component | Comp |
Cylinder | Cyl |
Decoration | Decor |
Detail | Dtl |
Direction | Dir |
Directory | Dir |
Editor | Ed |
Environment | Env |
Event | Ev |
Examiner | Exam |
FaceSet | FSet |
Function | Func |
Highlight | HL |
Index | Ind (Indexed->Index) |
Input | In |
Integer | Int |
Keyboard | Key |
Light | Lgt |
LineSet | LSet |
Location | Loc |
Locator | Loc |
Manager | Mgr |
Manipulator | Mnp |
Material | Mtl |
Matrix | Mx |
Normal | Norm |
Orthographic` | Ortho |
Output | Out |
Perspective | Persp |
Plane | Pln |
Point | Pt |
PointSet | PSet |
Pointer | Ptr |
Position | Pos |
Primitive | Prim |
Profile | Prof |
Projector | Proj |
QuadMesh | QMesh |
RenderArea | RA |
Rotate | Rot |
Searching | Search |
Section | Sect |
Slider | Sldr |
String | Str |
Sphere | Sph |
Texture | Tex |
Tolerance | Tol |
Transform | Xf |
Translate | Xlate |
TriangleStripSet | TSet |
Vec | V(Vec3f-> V3f) |
Vertex | Vtx |
Viewer | Vwr |
Visibility | Vis |
Wheel | Whl |
Window | Win |
The C versions of constructors and destructors are named following a convention slightly different from the one for most member functions. The C constructor for an object of our imaginary type SoGenericType would be SoGenericTypeCreate(). This example shows the C++ and C constructors for the SoSeparator type:
C++: |
SoSeparator *root = new SoSeparator(); C: SoSep *root; root = SoSepCreate(); |
Destructors are named similarly. A destructor for an object of type SoGenericType would be SoGenericTypeDelete(). To delete a viewer, for example, you might call SoXtExamVwrDelete(viewer). To delete nodes and groups, you use functions that unreference the node or group, instead of using destructors. See the discussion in Chapter 3 for details on deleting nodes.
The C++ language allows more than one method of a class to have the same method name, as long as the lists of arguments to the methods are different. This cabability is not provided by the C language, so different function names must be made for each identical method name from C++. Here is an example from the SoAction class:
C++
void apply(soNode * node); void apply(SoPath *path); |
C:
void SoActApply(SoAction * act, SoNode * node); void SoActApplyPath(SoAction * act, SoPath * path); |
You should be aware of a few differences between the way C++ calls functions and the way C does. The C++ versions of the Open Inventor methods fill in default values for some arguments. If you omit those arguments, C++ simply calls the function with the defaults. C does not fill in those defaults for you, though. You must supply values for each argument. Here's an example of a C++ call to create an appWindow object, and the equivalent C call:
C++: |
Widget appWindow = SoXt::init(argv[0]); C: widget appWindow; appwindow = SoXtInit(argv[0], "Inventor"); |
There is an online C reference manual page for every Inventor class. For detailed information on a class, see its reference manual page, which lists the functions available to that class and any data structures available. The “Inherits From” section tells you which class the current class is derived from. For many classes, this hierarchy can be several layers deep. If you want to know all the functions available to a given class, you need to know the functions available to the parent class. These are listed as part of the synopsis of the manual page. Each class has a separate include file, named after the class.
The reference manual pages also list the suggested default value for function arguments as follows:
void setSomeValue(x, y, z = 1.0) |
where z = 1.0 means that 1.0 is the recommended default value for the argument z.
The reference manual pages use both the abbreviated and the unabbreviated versions of the C class names. Your programs can use both versions as well.
You now know nearly enough to rewrite Example 2-4 in C. Here's one way you might do so:
main(int argc, char **argv) { Widget myWindow; SoSep *root; SoMtl *myMaterial; SoCone *myCone; SoXtExamVwr *myViewer; myWindow = SoXtInit(argv[0], "Inventor"); if (myWindow == NULL) exit(1); root = SoSepCreate(); SoSepRef(root); myMaterial = SoMtlCreate(); myCone = SoConeCreate(); SoMColSetR_G_B(&(myMaterial->diffuseColor), 1.0, 0.0, 0.0); SoSepAddChild(root, (SoNode *)myMaterial); SoSepAddChild(root, (SoNode *)myCone); /* Set up viewer: */ myViewer = SoXtExamVwrCreateStd(myWindow, NULL); SoXtExamVwrSetScene(myViewer, (SoNode *)root); SoXtExamVwrSetTitle(myViewer, "Examiner Viewer"); SoXtExamVwrShow(myViewer); SoXtShow(myWindow); SoXtMainLoop(); } |