How to Write an Open Inventor File Translator --------------------------------------------- CONTRIBUTORS Written by Josie Wernecke and Eleanor Bassler Engineering contributions by Rikk Carey and Paul Strauss Copyright 1994, 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 94039-7311. Silicon Graphics and IRIS are registered trademarks and IRIX and Open Inventor are trademarks of Silicon Graphics, Inc. Apollo is a registered trademark of Apollo Computer, Inc. FrameMaker is a registered trademark of Frame technology, Inc. Hewlett-Packard is a registered trademark of Hewlett-Packard Company. IBM is a registered trademark of International Business Machines Corporation. Macintosh is a registered trademark of Apple Computer, Inc. Table of Contents 1. Introduction 1 What Is in This Document? 2 Things You Need to Know about Inventor 2 Scene Database 2 Nodes and Fields 3 Node Icons 4 Scene Graphs 4 Group Nodes 5 2. Translating Files into Inventor Format 7 General Steps 7 SGO File Format 8 SGO Quadrilateral List 9 SGO Triangle List 9 SGO Triangle Mesh 10 Reading the File Header 10 Initializing the Inventor Database 11 Initializing the Database 11 Creating the Root Node 11 Setting Up Default Attributes 11 Entering the Object Read Loop 13 Writing the Database to a File 15 Complete Sample Program 16 Sample Results 24 Using the File Translator in Another Program 25 3. Tips and Guidelines 27 Tips 27 Testing the Results 27 Creating an Efficient Scene Graph 28 Verifying Values 28 Automatic Normal Generation 29 Guidelines for Writing an Inventor File Translator 29 File Suffix 29 Application Name and Command Line Syntax 29 Error Handling 30 Manual Page 30 Conventions Used in Inventor Files 31 Chapter 1 1. Introduction An important feature in Open Inventor is its 3D Interchange File Format, which provides a much-needed standard for exchanging 3D data among applications. Inventor's file format supports both ASCII and binary files. o The ASCII file format is a simple, human-readable representation of a 3D scene database. o Binary files are written in a machine-independent format. Although the binary format is not public, tools are readily available for converting an Inventor ASCII file to binary format. Translator programs make possible the flow of 3D data between application programs using Open Inventor and non-Inventor. Figure 1-1 illustrates this data flow. ED. NOTE: Figures are not available in this text version of the specification. Figure 1-1 Exchanging 3D data between Inventor and non-Inventor applications Other types of data have been converging on standard formats such as TIFF, GIF, PICT, and PostScript, but there is currently no clear winner for a standard 3D format. Once a standard emerges, all 3D developers and users can benefit from a common file format that allows data exchange among members of a world-wide audience. With a shared file format, users can cut and paste among a variety of applications on the desktop, and developers have access to a wide range of tools and code to boost their productivity. In addition, developers need only write one translator to and from Inventor format, rather than a whole collection of file translators for each 3D file format in the industry. What Is in This Document? This document provides the necessary basic information to enable you to write a program that translates existing graphics files into Open Inventor format. It includes the following sections: o Background information on the Inventor scene database o An example of a simple file translator o Tips and suggestions for writing file translators For a more complete description of Inventor objects, creating a scene database, and applying actions, see The Inventor Mentor, Chapters 1 through 5 and Chapter 9. Things You Need to Know about Inventor The following paragraphs outline basic concepts of an Inventor scene database. Scene Database An Inventor scene database is a collection of 3D objects and properties arranged appropriately to represent a 3D scene or data set. Inventor programs create or read their own copies of scene databases each time they execute. A scene database resides in the program's memory while the program is running, unlike a traditional database that resides on disk and is shared by many running programs. Nodes and Fields A node is an object that represents a 3D shape, property, or group. Shape nodes represent 3D geometric objects. Property nodes represent appearance and other qualitative characteristics of the scene. Group nodes are containers that collect nodes into graphs. Other important nodes include camera, light, and callback nodes. Nodes contain both data and functions. The data elements contained in a node are called fields. When you create a node, all the fields within that node are created as well, and each field automatically contains a default value. For example, a point light node contains these fields: Field Default Meaning Value on TRUE whether light is active intensity 1.0 value between 0.0 and 1.0 (maximum illumination) color 1.0 1.0 1.0 red, green, blue color of light location 0.0 0.0 1.0 position in x, y, z See the Open Inventor Nodes Quick Reference for a complete alphabetical listing of all Inventor nodes as well as their fields and default values. Node Icons Figure 1-2 contains the legend for nodes used in the diagrams in this document. ED. NOTE: Figures cannot be included in this plain text specification. Figure 1-2 Node icons Scene Graphs A scene graph is an ordered collection of nodes. Hierarchical scene graphs are created by adding nodes as children of group nodes. Figure 1-3 shows a simple scene graph. The top node of the graph (in this figure, "top") is called the root node. The Inventor scene database can contain any number of scene graphs, each consisting of a related set of 3D objects and attributes. Typically, a 3D scene or a set of object files contains only one scene graph. However, this is not a restriction. Figure 1-3 Simple scene graph Group Nodes A group node contains an ordered list of children that are traversed from left to right. A separator group, shown in Figure 1-4, is a special type of group node. Nodes under the separator group do not affect nodes in the graph after the separator. When you write a scene database, the objects in the database are written from top to bottom and from left to right. Objects lower (and to the right) in the scene graph inherit the attributes and values set by objects that precede them. If you do not want subsequent objects to inherit certain properties or values, use a separator group, which pushes and pops properties during traversal (see Figure 1-4; the red material in the separator A group does not affect the cone in the separator B group). The root node of a scene graph is usually a separator. Figure 1-4 Example of separator groups Chapter 2 2. Translating Files into Inventor Format This section outlines a general methodology for writing an Inventor file translator and presents a sample file translation program. The sample program translates Silicon Graphics Object (SGO) data files into Open Inventor files. This example is presented as one possible way to write a file translator. There are, of course, many other possible solutions, depending on your needs, the type of data you are working with, and the structure of the files you are translating. Chapter 11 in The Inventor Mentor describes the Inventor file format in great detail. General Steps The basic steps in an Inventor file translation program can be summarized as follows: 1. Read and verify the file header of the input file (if applicable). 2. Initialize the Inventor database. This step includes creating the root node or nodes of the database and setting up nodes containing any default attributes that will be used by other nodes in the scene. 3. Enter the object read loop. Read the first object from the input file, generate an Inventor object, and put it into the database. Continue reading objects from the input file until all objects are read and translated. 4. Clean up the Inventor database. Reorganize it for maximum efficiency. 5. Write the Inventor database to a file. The following subsections discuss each step in more detail. The complete program is shown in "Complete Sample Program" on page 16. SGO File Format Since the program example translates files from SGO file format, you'll need to know something about this format before you look at the example in detail. Figure 2-1 shows the basic structure of the SGO file format. The first word in the file must be the SGO magic number, a code number used to identify the file (0x5424). The magic number is 4 bytes long. The objects in SGO files are constructed from three data types: quadrilateral lists, triangle lists, and triangle meshes. One SGO file can contain any number of objects of differing type. An identifying token (4 bytes) precedes the data for each object: OBJ_QUADLIST (= 1) quadrilateral list OBJ_TRILIST (= 2) triangle list OBJ_TRIMESH (= 3) triangle mesh The end of data token is OBJ_END (= 4). This token is placed after the data for the last object in the file. Figure 2-1 SGO file format SGO Quadrilateral List Figure 2-2 shows the structure of an SGO quadrilateral list object. The object begins with the object token, followed by the size (in 4-byte words) of the data for this object. Next follow nine words of data for each vertex in the object. As shown at the bottom of Figure 2-2, the first three words are the xyz components of the normal vector at the vertex. The next three words are the RGB color components at the vertex. The last three words are the xyz coordinates of the vertex itself. Figure 2-2 SGO quadrilateral list object SGO Triangle List Figure 2-3 shows the structure of an SGO triangle list object. The object begins with the object token, followed by the size (in 4-byte words) of the data for this object. Next follow nine words of data for each vertex in the object. As shown at the bottom of Figure 2-3, the first three words are the xyz components of the normal vector at the vertex. The next three words are the RGB color components at the vertex. The last three words are the xyz values of the vertex itself. Figure 2-3 SGO triangle list object SGO Triangle Mesh In the interest of brevity, the SGO triangle mesh object is not discussed in this document or illustrated in the example. Reading the File Header The first step in the file translation program is to check that the header of the input file is in the expected format. SGO files, used in our example, are identified by a special code number, 0x5424, known as the SGO "magic number." If the file header does not contain the magic number, the function returns FALSE. static SbBool readHeader() { long magic; return (fread(&magic, sizeof(long), 1, stdin) == 1 && magic == SGO_MAGIC); } Initializing the Inventor Database This step comprises three parts: o initializing the Inventor database o creating a root node for the database o creating nodes with default attributes that will be used by other nodes in the database Initializing the Database The following code initializes the Inventor database: SoDB::init(); Creating the Root Node The root node for the database is typically a separator node (see"Group Nodes" on page 5). The root node is always referenced, as shown below. This ensures that the root node is not accidentally deleted. In most cases, you will use a separator as your root node. If in doubt, use a separator. root = new SoSeparator; root->ref(); Setting Up Default Attributes This step involves setting up default or global attributes that are the same for all objects in the scene graph. SGO objects include values for normals and colors along with each index. In Inventor, you need to specify how this information is applied to the shape objects in the scene graph. For example, a color can be applied, or "bound," to an entire shape, to each face in the shape, or to each vertex in the shape. In our example, we want the normals and materials to be bound to each vertex in the shape object. This is termed per-vertex binding. The sample program creates the following two Inventor nodes and adds them to the scene graph: SoMaterialBinding tells how to bind the specified materials to the shape node SoNormalBinding tells how to bind the specified normals to the shape node Note: While the sample program does not translate SGO triangle mesh objects, your program may require this translation. If so, you should note that this object presents a special case because it lists colors and normals in an arbitrary order and then indexes into the list. For triangle mesh objects, you should use Inventor's per-vertex indexed binding. When no indices are present, this binding defaults to per-vertex binding (normals and materials are used in order). See The Inventor Mentor, Chapter 5, for a detailed description of binding nodes. Here is the code that creates the material and normal binding nodes and specifies per-vertex binding for both nodes: mtlBind = new SoMaterialBinding; normBind = new SoNormalBinding; mtlBind->value = SoMaterialBinding::PER_VERTEX; normBind->value = SoNormalBinding::PER_VERTEX; root->addChild(mtlBind); root->addChild(normBind); At this point, the Inventor database looks like Figure 2-4. Figure 2-4 Nodes created during initialization Entering the Object Read Loop The object read loop is where the majority of the database objects are created. This loop reads the first object from the input file. It determines which type of object follows and calls one of three functions: readQuadList(), readTriList(), or readTriMesh(). Then it generates Inventor nodes to represent the corresponding data and puts those objects into the database that was initialized in step 1. This loop continues reading objects from the input file until all objects have been read and translated into Inventor nodes. The SGO objects OBJ_QUADLIST and OBJ_TRILIST are translated into the Inventor shape node SoFaceSet. The OBJ_TRIMESH object is not implemented for this example. As described earlier, SGO objects contain the following nine words of data for each vertex: o Normals (x, y, z) o Colors (r, g, b) o Coordinates (x, y, z) In Inventor, each of these three sets of information is contained in a separate node as follows: o SoNormal - contains all vertex normals for a shape o SoBaseColor - contains the red/green/blue values for the base color of the vertices (An SoMaterial node could be used here, but SoBaseColor is more efficient since only the diffuse color is changing.) o SoCoordinate3 - contains the coordinates for the vertices The sample program reads an SGO object, checks the number of vertices, and then makes the appropriate amount of room in the corresponding Inventor nodes to hold all the data for that object. Note that the SGO object groups the normals, colors, and coordinates for each vertex. The sample program unpacks this combined vertex data and reorganizes it into three separate Inventor nodes (all normals for the shape go into the normal node, all colors go into the base color node, and so on). For example, the readQuadList() function in the sample program creates the four Inventor nodes shown in Figure 2-5. These nodes are then added as children of an SoSeparator node. The readTriList() function in the sample program translates an SGO triangle list into the same group of nodes shown in Figure 2-5. Figure 2-5 Inventor nodes for a face set object Each group of nodes (called a subgraph) is added to the database. Figure 2-6 shows the scene graph after reading the SGO object TRILIST. Figure 2-6 Inventor database after reading the SGO object TRILIST Here is the code for the object read loop. // Keep reading objects until we are done or we have an error while (! endFound) { switch (readObjectType()) { case SGO_OBJ_QUADLIST: obj = readQuadList(); break; case SGO_OBJ_TRILIST: obj = readTriList(): break; case SGO_OBJ_TRIMESH: // Not implemented in this example break; case SGO_OBJ_END: endFound = TRUE; break; default: error("Missing or invalid object type"); break; } if (! endFound) { if (obj == NULL) error("Bad object data"); root->addChild(obj); } } Writing the Database to a File Now you are ready to write the database to a file. This is the easy part: SoWriteAction wa; // default writes in ASCII to stdout wa.apply(root); To write to a filename in binary: SoWriteAction wa; SoOutput *out = wa.getOutput(); if (out->openFile(filename)!=NULL) { out->setBinary(TRUE); wa.apply(root) } See Chapter 3, "Tips and Guidelines" for ways to test the resulting Inventor file. Complete Sample Program Here is the complete sample program that translates SGO files into Open Inventor files. // usage: SgoToIv file.sgo > file.iv // #include #include #include #include #include #include #include #include #include #include #include // SGO format codes #define SGO_MAGIC 0x5424 #define SGO_OBJ_QUADLIST 1 #define SGO_OBJ_TRILIST 2 #define SGO_OBJ_TRIMESH 3 #define SGO_OBJ_END 4 #define SGO_OBJ_ERROR (-1) /* No such object type */ #define SGO_OP_BGNTMESH 1 #define SGO_OP_SWAPTMESH 2 #define SGO_OP_ENDBGNTMESH 3 #define SGO_OP_ENDTMESH 4 //////////////////////////////////////////////////////////// // Prints an error message to stderr and exits. //////////////////////////////////////////////////////////// static void error(const char *message) { fprintf(stderr, "SgoToIv: %s\n", message); exit(1); } ///////////////////////////////////////////////////////////// // Reads header (magic number) from file. Returns FALSE on // error. ///////////////////////////////////////////////////////////// static SbBool readHeader(FILE *file) { long magic; return (fread(&magic, sizeof(long), 1, file) == 1 && magic == SGO_MAGIC); } ///////////////////////////////////////////////////////////// // Reads SGO object type from file and returns it. ///////////////////////////////////////////////////////////// static long readObjectType(FILE *file) { long type; if (fread(&type, sizeof(long), 1, file) != 1) return SGO_OBJ_ERROR; return type; } ///////////////////////////////////////////////////////////// // Reads N SGO vertices into the passed array of floats, // which should be big enough to hold the data // (N * 9 floats). Returns FALSE on a bad read. ///////////////////////////////////////////////////////////// static SbBool readVertices(int numVerts, float verts[], FILE *file) { // Read vertices (9 floats each) return (fread(verts, sizeof(float), numVerts * 9, file) == numVerts * 9); } ///////////////////////////////////////////////////////////// // Reads a quadrilateral list SGO object while creating an // Inventor graph to represent it. Returns the root of the / graph, or NULL if there's an error. ///////////////////////////////////////////////////////////// static SoNode * readQuadList(FILE *file) { long numWords; int numQuads, quad, vert, fieldIndex, vertIndex; float verts[36]; SoSeparator *root; SoCoordinate3 *coord; SoNormal *norm; SoBaseColor *color; SoFaceSet *faceSet; fprintf(stderr, "Reading a quad list\n"); // Read number of words in data if (fread(&numWords, sizeof(long), 1, file) != 1) return NULL; // There are 36 words (4 vertices of 9 words each) per // quadrilateral numQuads = (int) numWords / 36; // Create nodes to hold all the vertex and shape info coord = new SoCoordinate3; norm = new SoNormal; color = new SoBaseColor; faceSet = new SoFaceSet; // Because we know how many vertices there are, we can // make the appropriate amount of room in the fields of // the nodes. coord->point.setNum(4 * numQuads); color->rgb.setNum(4 * numQuads); norm->vector.setNum(4 * numQuads); faceSet->numVertices.setNum(numQuads); // Process each quadrilateral for (quad = 0; quad < numQuads; quad++) { // Read 4 vertices if (! readVertices(4, verts, file)) return NULL; // Store vertex info in fields for (vert = 0; vert < 4; vert++) { // Get index into appropriate place in field and // vertex fieldIndex = 4 * quad + vert; vertIndex = 9 * vert; norm->vector.set1Value(fieldIndex, &verts[vertIndex + 0]); color->rgb.set1Value(fieldIndex, &verts[vertIndex + 3]); coord->point.set1Value(fieldIndex, &verts[vertIndex + 6]); } // Store number of vertices of quadrilateral faceSet->numVertices.set1Value(quad, 4); } // Create a root separator to hold the subgraph. We don't // need to ref() it because we aren't doing anything to // the subgraph until it is added to the main graph // (after this returns). root = new SoSeparator(4); // Add the nodes to the root root->addChild(coord); root->addChild(norm); root->addChild(color); root->addChild(faceSet); return root; } ///////////////////////////////////////////////////////////// // Reads a triangle list SGO object while creating an // Inventor graph to represent it. Returns the root of the // graph, or NULL if there's an error. ///////////////////////////////////////////////////////////// static SoNode * readTriList(FILE *file) { long numWords; int numTris, tri, vert, fieldIndex, vertIndex; float verts[27]; SoSeparator *root; SoCoordinate3 *coord; SoNormal *norm; SoBaseColor *color; SoFaceSet *faceSet; fprintf(stderr, "Reading a tri list\n"); // Read number of words in data if (fread(&numWords, sizeof(long), 1, file) != 1) return NULL; // There are 27 words (3 vertices of 9 words each) per // triangle numTris = (int) numWords / 27; // Create nodes to hold all the vertex and shape info coord = new SoCoordinate3; norm = new SoNormal; color = new SoBaseColor; faceSet = new SoFaceSet; // Because we know how many vertices there are, we can // make the appropriate amount of room in the fields of // the nodes. coord->point.setNum(3 * numTris); color->rgb.setNum(3 * numTris); norm->vector.setNum(3 * numTris); faceSet->numVertices.setNum(numTris); // Process each triangle for (tri = 0; tri < numTris; tri++) { // Read 3 vertices if (! readVertices(3, verts, file)) return NULL; // Store vertex info in fields for (vert = 0; vert < 3; vert++) { // Get index into appropriate place in field and // vertex fieldIndex = 3 * tri + vert; vertIndex = 9 * vert; norm->vector.set1Value(fieldIndex, &verts[vertIndex + 0]); color->rgb.set1Value(fieldIndex, &verts[vertIndex + 3]); coord->point.set1Value(fieldIndex, &verts[vertIndex + 6]); } // Store number of vertices of triangle faceSet->numVertices.set1Value(tri, 3); } // Create a root separator to hold the subgraph. We don't // need to ref() it because we aren't doing anything to // the subgraph until it is added to the main graph // (after this returns). root = new SoSeparator(4); // Add the nodes to the root root->addChild(coord); root->addChild(norm); root->addChild(color); root->addChild(faceSet); return root; } ///////////////////////////////////////////////////////////// // Checks color values for correct range. ///////////////////////////////////////////////////////////// static void verifyColors(SoNode *root) { SoBaseColor *color; int i, j; SoPath *path; SoPathList paths; float r, g, b; const SbColor *rgb; SoSearchAction sa; sa.setType(SoBaseColor::getClassTypeId()); sa.setInterest(SoSearchAction::ALL); sa.apply(root); paths = sa.getPaths(); for (i = 0; i < paths.getLength(); i++) { path = paths[i]; color = (SoBaseColor *) path->getTail(); rgb = color->rgb.getValues(0); for (j = 0; j < color->rgb.getNum(); j++) { rgb[j].getValue(r, g, b); if (r < 0.0) r = 0.0; else if (r > 1.0) r = 1.0; if (g < 0.0) g = 0.0; else if (g > 1.0) g = 1.0; if (b < 0.0) b = 0.0; else if (b > 1.0) b = 1.0; color->rgb.set1Value(j, r, g, b); } } } ///////////////////////////////////////////////////////////// // Mainline. ///////////////////////////////////////////////////////////// main(int argc, char *argv[]) { SbBool endFound = FALSE; FILE *file; SoSeparator *root; SoMaterialBinding *mtlBind; SoNormalBinding *normBind; SoNode *obj; // Check command line syntax and open sgo file if (argc != 2) { fprintf(stderr, "usage: SgoToIv file.sgo [> file.iv]\n"); exit(1); } if ((file = fopen(argv[1], "r")) == NULL) error("SGO file could not be opened"); // Initialize the Inventor database SoDB::init(); // Read header of SGO file and check it for validity if (! readHeader(file)) error("Invalid SGO header"); // Set up a root group to add all the objects to root = new SoSeparator; root->ref(); // Set up per-vertex material and normal bindings mtlBind = new SoMaterialBinding; normBind = new SoNormalBinding; mtlBind->value = SoMaterialBinding::PER_VERTEX; normBind->value = SoNormalBinding::PER_VERTEX; root->addChild(mtlBind); root->addChild(normBind); // Keep reading objects until we are done or we have an // error while (! endFound) { SbBool readError = FALSE; switch (readObjectType(file)) { case SGO_OBJ_QUADLIST: obj = readQuadList(file); if (obj == NULL) readError = TRUE; break; case SGO_OBJ_TRILIST: obj = readTriList(file); if (obj == NULL) readError = TRUE; break; case SGO_OBJ_TRIMESH: // If triangle mesh is to be implemented, // include the code here. // Otherwise, print an error message fprintf(stderr, "SgoToIv: Can't process triangle meshes\n"); readError = TRUE; break; case SGO_OBJ_END: endFound = TRUE; break; default: error("Missing or invalid object type"); readError = TRUE; break; } if (! endFound) { if (readError) error("Bad object data"); else if (obj != NULL) root->addChild(obj); } } // Verify color values verifyColors(root); // Write out the resulting graph SoWriteAction wa; wa.apply(root); fprintf(stderr, "sgo->iv conversion done.\n"); return 0; } Sample Results The following code shows the results of using the sample program to translate an SGO file with one trilist object into Inventor file format. # Inventor V2.0 ascii # greetings.iv (edited down) # Separator { MaterialBinding { value PER_VERTEX } NormalBinding { value PER_VERTEX } Separator { Coordinate3 { point [ -0.455535 0.0715609 0, -0.456693 0.0780454 0, ... 0.465511 0.00737759 0.0567392 ] } Normal { vector [ 0 0 -1, 0 0 -1, ... -0.155748 -0.689741 0.707107 ] } BaseColor { rgb [ 0 0.0715609 0, 0 0.0780454 0, ... 0.465511 0.00737759 0.0567392 ] } FaceSet { numVertices [ 3, 3, 3, 3, 3, 3, 3, 3, ... 3, 3, 3, 3, 3, 3, 3, 3 ] } } } Using the File Translator in Another Program The following excerpt shows how you could read another file format from within an Inventor application using an external translator program. The program that translates the file into Inventor format is named SgoToIv. The file being translated is myfile.sgo. The translator program writes to stdout which has been piped via popen() to fp. Inventor's file reader is set to read from fp. FILE *fp = popen("SgoToIv myfile.sgo", "r"); SoNode *root; SoInput in; in.setFilePointer(fp); if (! SoDB::read(&in, root)) fprintf(stderr, "Read error\n"); in.closeFile(); pclose(fp); Chapter 3 3. Tips and Guidelines This section presents tips for testing the results of your translator program, for creating efficient scene graphs, and for verifying the file translation. It also suggests guidelines for writing an Inventor file translator. Tips The following sections offer general tips for writing an Inventor file translator. Testing the Results One way to test the Inventor file produced by your file translator is to perform a read test using the ivcat command: ivcat filename.iv This command prints the specified Inventor file in ASCII to stdout. If there are any syntax errors in the file, ivcat prints error messages for them. Use the -b option to print the specified Inventor file in binary to stdout. As the saying goes, "Seeing is believing." Another way to test the results of your file translator program is to use the ivview application to read your new Inventor scene graph and display the results. To use it type: ivview filename.iv Creating an Efficient Scene Graph You can use ivquicken to improve the rendering performance of the scene graphs created by your translator program. See the reference page for ivquicken to learn more about this utility program. You can also attempt to increase performance by having your program make a second pass through the database, condensing redundant nodes into fewer nodes. For example: o If a number of nodes share the same material, for example, you can insert a material node in the scene graph so that multiple nodes will inherit the same material value. In the SgoToIv example program, if all the vertices of an object have the same color, you can isolate the material and use OVERALL material binding. o If you are changing only the diffuse color attribute, use an Inventor SoBaseColor node rather than an SoMaterial node (as shown in the example program SgoToIv). o If you know that a shape is solid or has ordered vertices, specify this information in an SoShapeHints node. In general, the more information you specify with SoShapeHints, the faster the rendering speed. The exception to this rule is that when you specify ordered vertices, but not a solid shape, rendering may be slower because two-sided lighting is automatically turned on and backface culling is turned off. Verifying Values You may also need to check the resulting Inventor scene graph to be sure that all values fall into the appropriate range. Many SGO files, for example, have color values that are out of range. You might want to add code that resets colors to valid values. The verifyColors() function in the "Complete Sample Program" in Chapter 2 provides an example of this. This code uses an Inventor search action to locate the base color nodes in the scene graph. It obtains the number of values in the base color node, loops through the values, and checks them. If the value is out of range, it resets the value. If your translator generates nodes other than base color that have color fields (such as lights and materials), make sure their values are valid as well. Automatic Normal Generation If your data does not contain normals, Inventor can generate them automatically during rendering (but not in the file format). Inventor generates normals automatically if DEFAULT normal binding is used and you do not specify any normals. You can also use ivquicken to generate normals. Guidelines for Writing an Inventor File Translator The following guidelines are suggested so that applications can access Inventor translators in a consistent manner. These guidelines include o Inventor file suffix o application name and command line syntax o general conventions for error handling o manual page File Suffix It is recommended that .iv be used as the filename extension for Inventor files. Application Name and Command Line Syntax The recommended name for the translator application is XxxToIv or IvToXxx where Xxx represents the name of the non-Inventor file format and Iv represents Inventor file format. The command line syntax is: XxxToIv filename [ > filename.out] where filename is the name of the file to be translated. By default, the output is directed to stdout. If desired, filename can default to stdin. However, be sure to implement the explicit filename option to guarantee compatibility with other file translators. Error Handling The application should return 0 if there are no errors. It should return 1 (or any other nonzero code) if errors occur. Error messages should be sent to stderr. Manual Page Write a manual page for the translator program, in standard UNIX man page format. Here is an example for the SgoToIv program. SgoToIv(1) Silicon Graphics SgoToIv(1) NAME SgoToIv - translates an SGO object file to an Inventor file SYNOPSIS SgoToIv file.sgo [> file.iv] DESCRIPTION SgoToIv reads a single SGO file, translates to Inventor, and writes the result to stdout. NOTES SgoToIv does not handle SGO triangle meshes. Page 1 Release 1.0 February 1994 Conventions Used in Inventor Files The following conventions are used by the Inventor file format. o The meter is the default unit for all data. (Use the SoUnits node to scale to other units.) o The positive y axis points up. The positive z axis extends towards the viewer's eye (out of the screen). o Colors are expressed as red, green, blue values. o All fields within nodes have default values. See the Open Inventor Nodes Quick Reference for a list of these values. o The vertices of polygons in vertex-based shapes should be specified consistently in either clockwise or counter-clockwise order.