home *** CD-ROM | disk | FTP | other *** search
/ Encyclopedia of Graphics File Formats Companion / GFF_CD.ISO / formats / sgiinv / spec / trans2_0.txt < prev   
Encoding:
Text File  |  1994-06-03  |  37.5 KB  |  1,499 lines

  1. How to Write an Open Inventor File Translator
  2. ---------------------------------------------
  3.  
  4.  
  5. CONTRIBUTORS
  6.  
  7. Written by Josie Wernecke and Eleanor Bassler
  8.  
  9. Engineering contributions by Rikk Carey and Paul Strauss
  10.  
  11.  Copyright 1994, Silicon Graphics, Inc.- All Rights Reserved
  12.  
  13. This document contains proprietary and confidential information of Silicon 
  14. Graphics, Inc. The contents of this document may not be
  15. disclosed to third parties, 
  16. copied, or duplicated in any form, in whole or in part, 
  17. without the prior written permission of Silicon Graphics, Inc.
  18.  
  19. RESTRICTED RIGHTS LEGEND
  20.  
  21. Use, duplication, or disclosure of the technical 
  22. data contained in this document by 
  23. the Government is subject to restrictions as set forth 
  24. in subdivision (c) (1) (ii) of the 
  25. Rights in Technical Data and Computer Software clause 
  26. at DFARS 52.227-7013 and/or in similar or successor clauses in the FAR,
  27. or in the DOD or NASA FAR Supplement. Unpublished rights reserved
  28. under the Copyright Laws of the United States. Contractor/manufacturer
  29. is Silicon Graphics, Inc., 2011 N. Shoreline Blvd., 
  30. Mountain View, CA 94039-7311.
  31.  
  32. Silicon Graphics and IRIS are registered trademarks and IRIX 
  33. and Open Inventor are trademarks of Silicon Graphics, Inc. Apollo
  34. is a registered trademark of Apollo Computer, Inc. FrameMaker is a
  35. registered trademark of Frame technology, Inc. Hewlett-Packard is a
  36. registered trademark of Hewlett-Packard Company. IBM is a registered
  37. trademark of International Business Machines Corporation. Macintosh is
  38. a registered trademark of Apple Computer, Inc.
  39.  
  40.  
  41.  
  42. Table of Contents
  43.  
  44. 1.    Introduction    1
  45.  
  46. What Is in This Document?    2
  47. Things You Need to Know about Inventor    2
  48. Scene Database    2
  49. Nodes and Fields    3
  50. Node Icons    4
  51. Scene Graphs    4
  52. Group Nodes    5
  53.  
  54. 2.    Translating Files into Inventor Format    7
  55.  
  56. General Steps    7
  57. SGO File Format    8
  58. SGO Quadrilateral List    9
  59. SGO Triangle List    9
  60. SGO Triangle Mesh    10
  61. Reading the File Header    10
  62. Initializing the Inventor Database    11
  63. Initializing the Database    11
  64. Creating the Root Node    11
  65. Setting Up Default Attributes    11
  66. Entering the Object Read Loop    13
  67. Writing the Database to a File    15
  68. Complete Sample Program    16
  69. Sample Results    24
  70. Using the File Translator in Another Program    25
  71.  
  72. 3.    Tips and Guidelines    27
  73.  
  74. Tips    27
  75. Testing the Results    27
  76. Creating an Efficient Scene Graph    28
  77. Verifying Values    28
  78. Automatic Normal Generation    29
  79. Guidelines for Writing an Inventor File Translator    29
  80. File Suffix    29
  81. Application Name and Command Line Syntax    29
  82. Error Handling    30
  83. Manual Page    30
  84. Conventions Used in Inventor Files    31
  85.  
  86.  
  87.  
  88. Chapter 1
  89.  
  90. 1.    Introduction
  91.  
  92. An important feature in Open Inventor is its 3D Interchange File Format, 
  93. which provides a much-needed standard for exchanging 3D data among 
  94. applications. Inventor's file format supports both ASCII and binary files.
  95.  
  96. o    The ASCII file format is a simple, human-readable representation of a 
  97. 3D scene database. 
  98.  
  99. o    Binary files are written in a machine-independent format. Although the 
  100. binary format is not public, tools are readily available for converting an 
  101. Inventor ASCII file to binary format.
  102.  
  103. Translator programs make possible the flow of 3D data between application 
  104. programs using Open Inventor and non-Inventor. Figure 1-1 illustrates this 
  105. data flow.
  106.  
  107. ED. NOTE:  Figures are not available in this text version of the
  108. specification.
  109.  
  110. Figure 1-1    Exchanging 3D data between Inventor and non-Inventor applications
  111.  
  112. Other types of data have been converging on standard formats such as TIFF, 
  113. GIF, PICT, and PostScript, but there is currently no clear winner for a 
  114. standard 3D format. Once a standard emerges, all 3D developers and users 
  115. can benefit from a common file format that allows data exchange among 
  116. members of a world-wide audience. With a shared file format, users can cut 
  117. and paste among a variety of applications on the desktop, and developers 
  118. have access to a wide range of tools and code to boost their productivity. In 
  119. addition, developers need only write one translator to and from Inventor 
  120. format, rather than a whole collection of file translators for each 3D file 
  121. format in the industry.
  122.  
  123. What Is in This Document?
  124.  
  125. This document provides the necessary basic information to enable you to 
  126. write a program that translates existing graphics files into Open Inventor 
  127. format. It includes the following sections:
  128.  
  129. o    Background information on the Inventor scene database
  130.  
  131. o    An example of a simple file translator
  132.  
  133. o    Tips and suggestions for writing file translators
  134.  
  135. For a more complete description of Inventor objects, creating a scene 
  136. database, and applying actions, see The Inventor Mentor, Chapters 1 through 
  137. 5 and Chapter 9.
  138.  
  139. Things You Need to Know about Inventor
  140.  
  141. The following paragraphs outline basic concepts of an Inventor scene 
  142. database.
  143.  
  144. Scene Database
  145.  
  146. An Inventor scene database is a collection of 3D objects and properties 
  147. arranged appropriately to represent a 3D scene or data set. Inventor 
  148. programs create or read their own copies of scene databases each time they 
  149. execute. A scene database resides in the program's memory while the 
  150. program is running, unlike a traditional database that resides on disk and is 
  151. shared by many running programs.
  152.  
  153. Nodes and Fields
  154.  
  155. A node is an object that represents a 3D shape, property, or group. Shape 
  156. nodes represent 3D geometric objects. Property nodes represent appearance 
  157. and other qualitative characteristics of the scene. Group nodes are containers 
  158. that collect nodes into graphs. Other important nodes include camera, light, 
  159. and callback nodes. 
  160.  
  161. Nodes contain both data and functions. The data elements contained in a 
  162. node are called fields. When you create a node, all the fields within that node 
  163. are created as well, and each field automatically contains a default value. For 
  164. example, a point light node contains these fields:
  165.  
  166. Field        Default     Meaning
  167.         Value
  168.  
  169. on        TRUE        whether light is active 
  170.  
  171. intensity    1.0        value between 0.0 and 1.0 (maximum illumination)
  172.  
  173. color        1.0 1.0 1.0    red, green, blue color of light
  174.  
  175. location    0.0 0.0 1.0    position in x, y, z
  176.  
  177. See the Open Inventor Nodes Quick Reference for a complete alphabetical 
  178. listing of all Inventor nodes as well as their fields and default values.
  179.  
  180.  
  181.  
  182. Node Icons
  183.  
  184. Figure 1-2 contains the legend for nodes used in the diagrams in this 
  185. document.
  186.  
  187. ED. NOTE:  Figures cannot be included in this plain text
  188. specification.
  189.  
  190. Figure 1-2    Node icons
  191.  
  192.  
  193. Scene Graphs
  194.  
  195. A scene graph is an ordered collection of nodes. Hierarchical scene graphs are 
  196. created by adding nodes as children of group nodes. Figure 1-3 shows a 
  197. simple scene graph. The top node of the graph (in this figure, "top") is called 
  198. the root node. The Inventor scene database can contain any number of scene 
  199. graphs, each consisting of a related set of 3D objects and attributes. Typically, 
  200. a 3D scene or a set of object files contains only one scene graph. However, 
  201. this is not a restriction.
  202.  
  203.  
  204.  
  205. Figure 1-3    Simple scene graph
  206.  
  207. Group Nodes
  208.  
  209. A group node contains an ordered list of children that are traversed from left 
  210. to right. A separator group, shown in Figure 1-4, is a special type of group 
  211. node. Nodes under the separator group do not affect nodes in the graph after 
  212. the separator.
  213.  
  214. When you write a scene database, the objects in the database are written 
  215. from top to bottom and from left to right. Objects lower (and to the right) in 
  216. the scene graph inherit the attributes and values set by objects that precede 
  217. them. If you do not want subsequent objects to inherit certain properties or 
  218. values, use a separator group, which pushes and pops properties during 
  219. traversal (see Figure 1-4; the red material in the separator A group does not 
  220. affect the cone in the separator B group). The root node of a scene graph is 
  221. usually a separator.
  222.  
  223.  
  224.  
  225. Figure 1-4    Example of separator groups
  226.  
  227.  
  228.  
  229.  
  230.  
  231. Chapter 2
  232.  
  233. 2.    Translating Files into Inventor Format 
  234.  
  235. This section outlines a general methodology for writing an Inventor file 
  236. translator and presents a sample file translation program. The sample 
  237. program translates Silicon Graphics Object (SGO) data files into Open 
  238. Inventor files. This example is presented as one possible way to write a file 
  239. translator. There are, of course, many other possible solutions, depending on 
  240. your needs, the type of data you are working with, and the structure of the 
  241. files you are translating. Chapter 11 in The Inventor Mentor describes the 
  242. Inventor file format in great detail.
  243.  
  244. General Steps
  245.  
  246. The basic steps in an Inventor file translation program can be summarized 
  247. as follows:
  248.  
  249. 1.    Read and verify the file header of the input file (if applicable).
  250.  
  251. 2.    Initialize the Inventor database. This step includes creating the root 
  252. node or nodes of the database and setting up nodes containing any 
  253. default attributes that will be used by other nodes in the scene.
  254.  
  255. 3.    Enter the object read loop. Read the first object from the input file, 
  256. generate an Inventor object, and put it into the database. Continue 
  257. reading objects from the input file until all objects are read and 
  258. translated.
  259.  
  260. 4.    Clean up the Inventor database. Reorganize it for maximum efficiency.
  261.  
  262. 5.    Write the Inventor database to a file.
  263.  
  264. The following subsections discuss each step in more detail. The complete 
  265. program is shown in "Complete Sample Program" on page 16. 
  266.  
  267. SGO File Format
  268.  
  269. Since the program example translates files from SGO file format, you'll need 
  270. to know something about this format before you look at the example in 
  271. detail. 
  272.  
  273. Figure 2-1 shows the basic structure of the SGO file format. The first word in 
  274. the file must be the SGO magic number, a code number used to identify the 
  275. file (0x5424). The magic number is 4 bytes long.
  276.  
  277. The objects in SGO files are constructed from three data types: quadrilateral 
  278. lists, triangle lists, and triangle meshes. One SGO file can contain any 
  279. number of objects of differing type. An identifying token (4 bytes) precedes 
  280. the data for each object:
  281.  
  282. OBJ_QUADLIST    (= 1)    quadrilateral list
  283.  
  284. OBJ_TRILIST    (= 2)    triangle list
  285.  
  286. OBJ_TRIMESH    (= 3)    triangle mesh
  287.  
  288. The end of data token is OBJ_END (= 4). This token is placed after the data 
  289. for the last object in the file.
  290.  
  291.  
  292.  
  293. Figure 2-1    SGO file format
  294.  
  295. SGO Quadrilateral List
  296.  
  297. Figure 2-2 shows the structure of an SGO quadrilateral list object. The object 
  298. begins with the object token, followed by the size (in 4-byte words) of the 
  299. data for this object. Next follow nine words of data for each vertex in the 
  300. object. As shown at the bottom of Figure 2-2, the first three words are the xyz 
  301. components of the normal vector at the vertex. The next three words are the 
  302. RGB color components at the vertex. The last three words are the xyz 
  303. coordinates of the vertex itself.
  304.  
  305.  
  306.  
  307. Figure 2-2    SGO quadrilateral list object
  308.  
  309. SGO Triangle List
  310.  
  311. Figure 2-3 shows the structure of an SGO triangle list object. The object 
  312. begins with the object token, followed by the size (in 4-byte words) of the 
  313. data for this object. Next follow nine words of data for each vertex in the 
  314. object. As shown at the bottom of Figure 2-3, the first three words are the xyz 
  315. components of the normal vector at the vertex. The next three words are the 
  316. RGB color components at the vertex. The last three words are the xyz values 
  317. of the vertex itself.
  318.  
  319.  
  320.  
  321. Figure 2-3    SGO triangle list object
  322.  
  323. SGO Triangle Mesh
  324.  
  325. In the interest of brevity, the SGO triangle mesh object is not discussed in this 
  326. document or illustrated in the example.
  327.  
  328. Reading the File Header
  329.  
  330. The first step in the file translation program is to check that the header of the 
  331. input file is in the expected format. SGO files, used in our example, are 
  332. identified by a special code number, 0x5424, known as the SGO "magic 
  333. number." If the file header does not contain the magic number, the function 
  334. returns FALSE.
  335.  
  336. static SbBool
  337.  
  338. readHeader()
  339.  
  340. {
  341.  
  342.    long   magic;
  343.  
  344.    return (fread(&magic, sizeof(long), 1, stdin) == 1 && 
  345.            magic == SGO_MAGIC);
  346.  
  347. }
  348.  
  349. Initializing the Inventor Database
  350.  
  351. This step comprises three parts:
  352.  
  353. o    initializing the Inventor database
  354.  
  355. o    creating a root node for the database
  356.  
  357. o    creating nodes with default attributes that will be used by other nodes 
  358. in the database
  359.  
  360. Initializing the Database
  361.  
  362. The following code initializes the Inventor database:
  363.  
  364. SoDB::init();
  365.  
  366. Creating the Root Node
  367.  
  368. The root node for the database is typically a separator node (see"Group 
  369. Nodes" on page 5). The root node is always referenced, as shown below. This 
  370. ensures that the root node is not accidentally deleted. In most cases, you will 
  371. use a separator as your root node. If in doubt, use a separator.
  372.  
  373. root = new SoSeparator;
  374.  
  375. root->ref();
  376.  
  377. Setting Up Default Attributes
  378.  
  379. This step involves setting up default or global attributes that are the same for 
  380. all objects in the scene graph. 
  381.  
  382. SGO objects include values for normals and colors along with each index. In 
  383. Inventor, you need to specify how this information is applied to the shape 
  384. objects in the scene graph. For example, a color can be applied, or "bound," 
  385. to an entire shape, to each face in the shape, or to each vertex in the shape. 
  386.  
  387. In our example, we want the normals and materials to be bound to each 
  388. vertex in the shape object. This is termed per-vertex binding. The sample 
  389. program creates the following two Inventor nodes and adds them to the 
  390. scene graph:
  391.  
  392. SoMaterialBinding
  393.  
  394. tells how to bind the specified materials to the 
  395. shape node
  396.  
  397. SoNormalBinding
  398.  
  399.     tells how to bind the specified normals to the 
  400. shape node
  401.  
  402. Note:  While the sample program does not translate SGO triangle mesh 
  403. objects, your program may require this translation. If so, you should note 
  404. that this object presents a special case because it lists colors and normals in 
  405. an arbitrary order and then indexes into the list. For triangle mesh objects, 
  406. you should use Inventor's per-vertex indexed binding. When no indices are 
  407. present, this binding defaults to per-vertex binding (normals and materials 
  408. are used in order). 
  409.  
  410. See The Inventor Mentor, Chapter 5, for a detailed description of binding 
  411. nodes.
  412.  
  413. Here is the code that creates the material and normal binding nodes and 
  414. specifies per-vertex binding for both nodes:
  415.  
  416. mtlBind = new SoMaterialBinding;
  417.  
  418. normBind = new SoNormalBinding;
  419.  
  420. mtlBind->value = SoMaterialBinding::PER_VERTEX;
  421.  
  422. normBind->value = SoNormalBinding::PER_VERTEX;
  423.  
  424. root->addChild(mtlBind);
  425.  
  426. root->addChild(normBind);
  427.  
  428. At this point, the Inventor database looks like Figure 2-4.
  429.  
  430.  
  431.  
  432. Figure 2-4    Nodes created during initialization
  433.  
  434. Entering the Object Read Loop
  435.  
  436. The object read loop is where the majority of the database objects are created. 
  437. This loop reads the first object from the input file. It determines which type 
  438. of object follows and calls one of three functions: readQuadList(), 
  439. readTriList(), or readTriMesh(). Then it generates Inventor nodes to 
  440. represent the corresponding data and puts those objects into the database 
  441. that was initialized in step 1. This loop continues reading objects from the 
  442. input file until all objects have been read and translated into Inventor nodes.
  443.  
  444. The SGO objects OBJ_QUADLIST and OBJ_TRILIST are translated into the 
  445. Inventor shape node SoFaceSet. The OBJ_TRIMESH object is not 
  446. implemented for this example.
  447.  
  448. As described earlier, SGO objects contain the following nine words of data 
  449. for each vertex:
  450.  
  451. o    Normals (x, y, z)
  452.  
  453. o    Colors (r, g, b)
  454.  
  455. o    Coordinates (x, y, z)
  456.  
  457. In Inventor, each of these three sets of information is contained in a separate 
  458. node as follows:
  459.  
  460. o    SoNormal - contains all vertex normals for a shape
  461.  
  462. o    SoBaseColor - contains the red/green/blue values for the base color of 
  463. the vertices (An SoMaterial node could be used here, but SoBaseColor 
  464. is more efficient since only the diffuse color is changing.)
  465.  
  466. o    SoCoordinate3 - contains the coordinates for the vertices
  467.  
  468. The sample program reads an SGO object, checks the number of vertices, 
  469. and then makes the appropriate amount of room in the corresponding 
  470. Inventor nodes to hold all the data for that object. Note that the SGO object 
  471. groups the normals, colors, and coordinates for each vertex. The sample 
  472. program unpacks this combined vertex data and reorganizes it into three 
  473. separate Inventor nodes (all normals for the shape go into the normal node, 
  474. all colors go into the base color node, and so on).
  475.  
  476. For example, the readQuadList() function in the sample program creates the 
  477. four Inventor nodes shown in Figure 2-5. These nodes are then added as 
  478. children of an SoSeparator node. The readTriList() function in the sample 
  479. program translates an SGO triangle list into the same group of nodes shown 
  480. in Figure 2-5.
  481.  
  482.  
  483.  
  484. Figure 2-5    Inventor nodes for a face set object
  485.  
  486. Each group of nodes (called a subgraph) is added to the database. Figure 2-6 
  487. shows the scene graph after reading the SGO object TRILIST.
  488.  
  489.  
  490.  
  491. Figure 2-6    Inventor database after reading the SGO object TRILIST
  492.  
  493. Here is the code for the object read loop.
  494.  
  495. // Keep reading objects until we are done or we have an error
  496.  
  497. while (! endFound) {
  498.  
  499.     switch (readObjectType()) {
  500.  
  501.         case SGO_OBJ_QUADLIST:
  502.  
  503.             obj = readQuadList();
  504.  
  505.             break;
  506.  
  507.         case SGO_OBJ_TRILIST:
  508.  
  509.             obj = readTriList():
  510.  
  511.             break;
  512.  
  513.         case SGO_OBJ_TRIMESH:
  514.  
  515.             // Not implemented in this example
  516.  
  517.             break;
  518.  
  519.         case SGO_OBJ_END:
  520.  
  521.             endFound = TRUE;
  522.  
  523.             break;
  524.  
  525.         default:
  526.  
  527.             error("Missing or invalid object type");
  528.  
  529.             break;
  530.  
  531.     }
  532.  
  533.     if (! endFound) {
  534.  
  535.         if (obj == NULL)
  536.  
  537.             error("Bad object data");
  538.  
  539.         root->addChild(obj);
  540.  
  541.     }
  542.  
  543. }
  544.  
  545. Writing the Database to a File
  546.  
  547. Now you are ready to write the database to a file. This is the easy part:
  548.  
  549. SoWriteAction     wa;    // default writes in ASCII to stdout
  550.  
  551. wa.apply(root);
  552.  
  553. To write to a filename in binary:
  554.  
  555. SoWriteAction    wa;
  556.  
  557. SoOutput  *out = wa.getOutput();
  558.  
  559. if (out->openFile(filename)!=NULL) {
  560.  
  561.      out->setBinary(TRUE);
  562.  
  563.      wa.apply(root)
  564.  
  565. }
  566.  
  567. See Chapter 3, "Tips and Guidelines" for ways to test the resulting Inventor 
  568. file.
  569.  
  570. Complete Sample Program
  571.  
  572. Here is the complete sample program that translates SGO files into Open 
  573. Inventor files.
  574.  
  575. // usage: SgoToIv file.sgo > file.iv
  576.  
  577. //
  578.  
  579. #include <Inventor/SoDB.h>
  580.  
  581. #include <Inventor/actions/SoSearchAction.h>
  582.  
  583. #include <Inventor/actions/SoWriteAction.h>
  584.  
  585. #include <Inventor/nodes/SoBaseColor.h>
  586.  
  587. #include <Inventor/nodes/SoCoordinate3.h>
  588.  
  589. #include <Inventor/nodes/SoFaceSet.h>
  590.  
  591. #include <Inventor/nodes/SoMaterialBinding.h>
  592.  
  593. #include <Inventor/nodes/SoNormal.h>
  594.  
  595. #include <Inventor/nodes/SoNormalBinding.h>
  596.  
  597. #include <Inventor/nodes/SoSeparator.h>
  598.  
  599. #include <Inventor/SoPath.h>
  600.  
  601. // SGO format codes
  602.  
  603. #define SGO_MAGIC               0x5424
  604.  
  605. #define SGO_OBJ_QUADLIST        1
  606.  
  607. #define SGO_OBJ_TRILIST         2
  608.  
  609. #define SGO_OBJ_TRIMESH         3
  610.  
  611. #define SGO_OBJ_END             4
  612.  
  613. #define SGO_OBJ_ERROR         (-1)  /* No such object type */
  614.  
  615. #define SGO_OP_BGNTMESH         1
  616.  
  617. #define SGO_OP_SWAPTMESH        2
  618.  
  619. #define SGO_OP_ENDBGNTMESH      3
  620.  
  621. #define SGO_OP_ENDTMESH         4
  622.  
  623. ////////////////////////////////////////////////////////////
  624.  
  625. // Prints an error message to stderr and exits.
  626.  
  627. ////////////////////////////////////////////////////////////
  628.  
  629. static void
  630.  
  631. error(const char *message)
  632.  
  633. {
  634.  
  635.    fprintf(stderr, "SgoToIv: %s\n", message);
  636.  
  637.    exit(1);
  638.  
  639. }
  640.  
  641. /////////////////////////////////////////////////////////////
  642.  
  643. // Reads header (magic number) from file. Returns FALSE on
  644.  
  645. // error.
  646.  
  647. /////////////////////////////////////////////////////////////
  648.  
  649. static SbBool
  650.  
  651. readHeader(FILE *file)
  652.  
  653. {
  654.  
  655.    long        magic;
  656.  
  657.    return (fread(&magic, sizeof(long), 1, file)
  658.  
  659.                           == 1 && magic == SGO_MAGIC);
  660.  
  661. }
  662.  
  663. /////////////////////////////////////////////////////////////
  664.  
  665. // Reads SGO object type from file and returns it.
  666.  
  667. /////////////////////////////////////////////////////////////
  668.  
  669. static long
  670.  
  671. readObjectType(FILE *file)
  672.  
  673. {
  674.  
  675.    long        type;
  676.  
  677.    if (fread(&type, sizeof(long), 1, file) != 1)
  678.  
  679.       return SGO_OBJ_ERROR;
  680.  
  681.    return type;
  682.  
  683. }
  684.  
  685. /////////////////////////////////////////////////////////////
  686.  
  687. // Reads N SGO vertices into the passed array of floats,
  688.  
  689. // which should be big enough to hold the data 
  690.  
  691. // (N * 9 floats). Returns FALSE on a bad read.
  692.  
  693. /////////////////////////////////////////////////////////////
  694.  
  695. static SbBool
  696.  
  697. readVertices(int numVerts, float verts[], FILE *file)
  698.  
  699. {
  700.  
  701.    // Read vertices (9 floats each)
  702.  
  703.    return (fread(verts, sizeof(float), numVerts * 9, file)
  704.  
  705.                           == numVerts * 9);
  706.  
  707. }
  708.  
  709. /////////////////////////////////////////////////////////////
  710.  
  711. // Reads a quadrilateral list SGO object while creating an
  712.  
  713. // Inventor graph to represent it. Returns the root of the
  714.  
  715. / graph, or NULL if there's an error.
  716.  
  717. /////////////////////////////////////////////////////////////
  718.  
  719. static SoNode *
  720.  
  721. readQuadList(FILE *file)
  722.  
  723. {
  724.  
  725.    long                numWords;
  726.  
  727.    int                 numQuads, quad, vert, fieldIndex,
  728.  
  729.                                  vertIndex;
  730.  
  731.    float               verts[36];
  732.  
  733.    SoSeparator         *root;
  734.  
  735.    SoCoordinate3       *coord;
  736.  
  737.    SoNormal            *norm;
  738.  
  739.    SoBaseColor         *color;
  740.  
  741.    SoFaceSet           *faceSet;
  742.  
  743.    fprintf(stderr, "Reading a quad list\n");
  744.  
  745.    // Read number of words in data
  746.  
  747.    if (fread(&numWords, sizeof(long), 1, file) != 1)
  748.  
  749.       return NULL;
  750.  
  751.    // There are 36 words (4 vertices of 9 words each) per
  752.  
  753.    // quadrilateral
  754.  
  755.    numQuads = (int) numWords / 36;
  756.  
  757.    // Create nodes to hold all the vertex and shape info
  758.  
  759.    coord = new SoCoordinate3;
  760.  
  761.    norm  = new SoNormal;
  762.  
  763.    color = new SoBaseColor;
  764.  
  765.    faceSet  = new SoFaceSet;
  766.  
  767.    // Because we know how many vertices there are, we can
  768.  
  769.    // make the appropriate amount of room in the fields of
  770.  
  771.    // the nodes.
  772.  
  773.    coord->point.setNum(4 * numQuads);
  774.  
  775.    color->rgb.setNum(4 * numQuads);
  776.  
  777.    norm->vector.setNum(4 * numQuads);
  778.  
  779.    faceSet->numVertices.setNum(numQuads);
  780.  
  781.    // Process each quadrilateral
  782.  
  783.    for (quad = 0; quad < numQuads; quad++) {
  784.  
  785.       // Read 4 vertices
  786.  
  787.       if (! readVertices(4, verts, file))
  788.  
  789.          return NULL;
  790.  
  791.       // Store vertex info in fields
  792.  
  793.       for (vert = 0; vert < 4; vert++) {
  794.  
  795.          // Get index into appropriate place in field and
  796.  
  797.          // vertex
  798.  
  799.          fieldIndex = 4 * quad + vert;
  800.  
  801.          vertIndex  = 9 * vert;
  802.  
  803.          norm->vector.set1Value(fieldIndex, 
  804.  
  805.                    &verts[vertIndex + 0]);
  806.  
  807.          color->rgb.set1Value(fieldIndex,  
  808.  
  809.                    &verts[vertIndex + 3]);
  810.  
  811.          coord->point.set1Value(fieldIndex, 
  812.  
  813.                    &verts[vertIndex + 6]);
  814.  
  815.       }
  816.  
  817.       // Store number of vertices of quadrilateral
  818.  
  819.       faceSet->numVertices.set1Value(quad, 4);
  820.  
  821.    }
  822.  
  823.    // Create a root separator to hold the subgraph. We don't
  824.  
  825.    // need to ref() it because we aren't doing anything to
  826.  
  827.    // the subgraph until it is added to the main graph
  828.  
  829.    // (after this returns).
  830.  
  831.    root = new SoSeparator(4);
  832.  
  833.    // Add the nodes to the root
  834.  
  835.    root->addChild(coord);
  836.  
  837.    root->addChild(norm);
  838.  
  839.    root->addChild(color);
  840.  
  841.    root->addChild(faceSet);
  842.  
  843.    return root;
  844.  
  845. }
  846.  
  847. /////////////////////////////////////////////////////////////
  848.  
  849. // Reads a triangle list SGO object while creating an
  850.  
  851. // Inventor graph to represent it. Returns the root of the
  852.  
  853. //  graph, or NULL if there's an error.
  854.  
  855. /////////////////////////////////////////////////////////////
  856.  
  857. static SoNode *
  858.  
  859. readTriList(FILE *file)
  860.  
  861. {
  862.  
  863.    long                numWords;
  864.  
  865.    int                 numTris, tri, vert, fieldIndex,
  866.  
  867.                                  vertIndex;
  868.  
  869.    float               verts[27];
  870.  
  871.    SoSeparator         *root;
  872.  
  873.    SoCoordinate3       *coord;
  874.  
  875.    SoNormal            *norm;
  876.  
  877.    SoBaseColor         *color;
  878.  
  879.    SoFaceSet           *faceSet;
  880.  
  881.    fprintf(stderr, "Reading a tri list\n");
  882.  
  883.    // Read number of words in data
  884.  
  885.    if (fread(&numWords, sizeof(long), 1, file) != 1)
  886.  
  887.       return NULL;
  888.  
  889.    // There are 27 words (3 vertices of 9 words each) per
  890.  
  891.    // triangle
  892.  
  893.    numTris = (int) numWords / 27;
  894.  
  895.    // Create nodes to hold all the vertex and shape info
  896.  
  897.    coord = new SoCoordinate3;
  898.  
  899.    norm  = new SoNormal;
  900.  
  901.    color = new SoBaseColor;
  902.  
  903.    faceSet  = new SoFaceSet;
  904.  
  905.    // Because we know how many vertices there are, we can
  906.  
  907.    // make the appropriate amount of room in the fields of
  908.  
  909.    // the nodes.
  910.  
  911.    coord->point.setNum(3 * numTris);
  912.  
  913.    color->rgb.setNum(3 * numTris);
  914.  
  915.    norm->vector.setNum(3 * numTris);
  916.  
  917.    faceSet->numVertices.setNum(numTris);
  918.  
  919.    // Process each triangle
  920.  
  921.    for (tri = 0; tri < numTris; tri++) {
  922.  
  923.       // Read 3 vertices
  924.  
  925.       if (! readVertices(3, verts, file))
  926.  
  927.          return NULL;
  928.  
  929.       // Store vertex info in fields
  930.  
  931.       for (vert = 0; vert < 3; vert++) {
  932.  
  933.          // Get index into appropriate place in field and
  934.  
  935.          // vertex
  936.  
  937.          fieldIndex = 3 * tri + vert;
  938.  
  939.          vertIndex  = 9 * vert;
  940.  
  941.          norm->vector.set1Value(fieldIndex, 
  942.  
  943.                   &verts[vertIndex + 0]);
  944.  
  945.          color->rgb.set1Value(fieldIndex,   
  946.  
  947.                   &verts[vertIndex + 3]);
  948.  
  949.          coord->point.set1Value(fieldIndex, 
  950.  
  951.                   &verts[vertIndex + 6]);
  952.  
  953.       }
  954.  
  955.       // Store number of vertices of triangle
  956.  
  957.       faceSet->numVertices.set1Value(tri, 3);
  958.  
  959.    }
  960.  
  961.    // Create a root separator to hold the subgraph. We don't
  962.  
  963.    // need to ref() it because we aren't doing anything to
  964.  
  965.    // the subgraph until it is added to the main graph
  966.  
  967.    // (after this returns).
  968.  
  969.    root = new SoSeparator(4);
  970.  
  971.    // Add the nodes to the root
  972.  
  973.    root->addChild(coord);
  974.  
  975.    root->addChild(norm);
  976.  
  977.    root->addChild(color);
  978.  
  979.    root->addChild(faceSet);
  980.  
  981.    return root;
  982.  
  983. }
  984.  
  985. /////////////////////////////////////////////////////////////
  986.  
  987. // Checks color values for correct range.
  988.  
  989. /////////////////////////////////////////////////////////////
  990.  
  991. static void
  992.  
  993. verifyColors(SoNode *root)
  994.  
  995. {
  996.  
  997.    SoBaseColor         *color;
  998.  
  999.    int                 i, j;
  1000.  
  1001.    SoPath              *path;
  1002.  
  1003.    SoPathList          paths;
  1004.  
  1005.    float               r, g, b;
  1006.  
  1007.    const SbColor       *rgb;
  1008.  
  1009.    SoSearchAction      sa;
  1010.  
  1011.    sa.setType(SoBaseColor::getClassTypeId());
  1012.  
  1013.    sa.setInterest(SoSearchAction::ALL);
  1014.  
  1015.    sa.apply(root);
  1016.  
  1017.    paths = sa.getPaths();
  1018.  
  1019.    for (i = 0; i < paths.getLength(); i++) {
  1020.  
  1021.       path = paths[i];
  1022.  
  1023.       color = (SoBaseColor *) path->getTail();
  1024.  
  1025.       rgb = color->rgb.getValues(0);
  1026.  
  1027.       for (j = 0; j < color->rgb.getNum(); j++) {
  1028.  
  1029.          rgb[j].getValue(r, g, b);
  1030.  
  1031.          if (r < 0.0)
  1032.  
  1033.             r = 0.0;
  1034.  
  1035.          else if (r > 1.0)
  1036.  
  1037.             r = 1.0;
  1038.  
  1039.          if (g < 0.0)
  1040.  
  1041.             g = 0.0;
  1042.  
  1043.          else if (g > 1.0)
  1044.  
  1045.             g = 1.0;
  1046.  
  1047.          if (b < 0.0)
  1048.  
  1049.             b = 0.0;
  1050.  
  1051.          else if (b > 1.0)
  1052.  
  1053.             b = 1.0;
  1054.  
  1055.          color->rgb.set1Value(j, r, g, b);
  1056.  
  1057.       }
  1058.  
  1059.    }
  1060.  
  1061. }
  1062.  
  1063. /////////////////////////////////////////////////////////////
  1064.  
  1065. // Mainline.
  1066.  
  1067. /////////////////////////////////////////////////////////////
  1068.  
  1069. main(int argc, char *argv[])
  1070.  
  1071. {
  1072.  
  1073.    SbBool              endFound = FALSE;
  1074.  
  1075.    FILE                *file;
  1076.  
  1077.    SoSeparator         *root;
  1078.  
  1079.    SoMaterialBinding   *mtlBind;
  1080.  
  1081.    SoNormalBinding     *normBind;
  1082.  
  1083.    SoNode              *obj;
  1084.  
  1085.    // Check command line syntax and open sgo file
  1086.  
  1087.    if (argc != 2) {
  1088.  
  1089.       fprintf(stderr, 
  1090.  
  1091.                  "usage: SgoToIv file.sgo [> file.iv]\n");
  1092.  
  1093.       exit(1);
  1094.  
  1095.    }
  1096.  
  1097.    if ((file = fopen(argv[1], "r")) == NULL)
  1098.  
  1099.       error("SGO file could not be opened");
  1100.  
  1101.    // Initialize the Inventor database
  1102.  
  1103.    SoDB::init();
  1104.  
  1105.    // Read header of SGO file and check it for validity
  1106.  
  1107.    if (! readHeader(file))
  1108.  
  1109.       error("Invalid SGO header");
  1110.  
  1111.    // Set up a root group to add all the objects to
  1112.  
  1113.    root = new SoSeparator;
  1114.  
  1115.    root->ref();
  1116.  
  1117.    // Set up per-vertex material and normal bindings
  1118.  
  1119.    mtlBind = new SoMaterialBinding;
  1120.  
  1121.    normBind = new SoNormalBinding;
  1122.  
  1123.    mtlBind->value = SoMaterialBinding::PER_VERTEX;
  1124.  
  1125.    normBind->value = SoNormalBinding::PER_VERTEX;
  1126.  
  1127.    root->addChild(mtlBind);
  1128.  
  1129.    root->addChild(normBind);
  1130.  
  1131.    // Keep reading objects until we are done or we have an
  1132.  
  1133.    // error
  1134.  
  1135.    while (! endFound) {
  1136.  
  1137.       SbBool readError = FALSE;
  1138.  
  1139.       switch (readObjectType(file)) {
  1140.  
  1141.         case SGO_OBJ_QUADLIST:
  1142.  
  1143.          obj = readQuadList(file);
  1144.  
  1145.          if (obj == NULL)
  1146.  
  1147.             readError = TRUE;
  1148.  
  1149.          break;
  1150.  
  1151.         case SGO_OBJ_TRILIST:
  1152.  
  1153.          obj = readTriList(file);
  1154.  
  1155.          if (obj == NULL)
  1156.  
  1157.             readError = TRUE;
  1158.  
  1159.          break;
  1160.  
  1161.         case SGO_OBJ_TRIMESH:
  1162.  
  1163.          // If triangle mesh is to be implemented,
  1164.  
  1165.                  // include the code here.
  1166.  
  1167.          // Otherwise, print an error message
  1168.  
  1169.          fprintf(stderr, "SgoToIv: Can't process triangle
  1170.  
  1171.                      meshes\n");
  1172.  
  1173.          readError = TRUE;
  1174.  
  1175.          break;
  1176.  
  1177.         case SGO_OBJ_END:
  1178.  
  1179.          endFound = TRUE;
  1180.  
  1181.          break;
  1182.  
  1183.         default:
  1184.  
  1185.          error("Missing or invalid object type");
  1186.  
  1187.          readError = TRUE;
  1188.  
  1189.          break;
  1190.  
  1191.       }
  1192.  
  1193.       if (! endFound) {
  1194.  
  1195.          if (readError)
  1196.  
  1197.             error("Bad object data");
  1198.  
  1199.          else if (obj != NULL)
  1200.  
  1201.             root->addChild(obj);
  1202.  
  1203.       }
  1204.  
  1205.    }
  1206.  
  1207.    // Verify color values
  1208.  
  1209.    verifyColors(root);
  1210.  
  1211.    // Write out the resulting graph
  1212.  
  1213.    SoWriteAction wa;
  1214.  
  1215.    wa.apply(root);
  1216.  
  1217.    fprintf(stderr, "sgo->iv conversion done.\n");
  1218.  
  1219.    return 0;
  1220.  
  1221. }
  1222.  
  1223. Sample Results
  1224.  
  1225. The following code shows the results of using the sample program to 
  1226. translate an SGO file with one trilist object into Inventor file format.
  1227.  
  1228. # Inventor V2.0 ascii
  1229.  
  1230. # greetings.iv (edited down)
  1231.  
  1232. #
  1233.  
  1234. Separator {
  1235.  
  1236.    MaterialBinding {
  1237.  
  1238.       value   PER_VERTEX
  1239.  
  1240.    }
  1241.  
  1242.    NormalBinding {
  1243.  
  1244.       value   PER_VERTEX
  1245.  
  1246.    }
  1247.  
  1248.    Separator {
  1249.  
  1250.       Coordinate3 {
  1251.  
  1252.          point       [ -0.455535 0.0715609 0,
  1253.  
  1254.                        -0.456693 0.0780454 0,
  1255.  
  1256.                        ...
  1257.  
  1258.                        0.465511 0.00737759 0.0567392 ]
  1259.  
  1260.       }
  1261.  
  1262.       Normal {
  1263.  
  1264.          vector      [ 0 0 -1,
  1265.  
  1266.                        0 0 -1,
  1267.  
  1268.                        ...
  1269.  
  1270.                        -0.155748 -0.689741 0.707107 ]
  1271.  
  1272.       }
  1273.  
  1274.       BaseColor {
  1275.  
  1276.          rgb         [ 0 0.0715609 0,
  1277.  
  1278.                        0 0.0780454 0,
  1279.  
  1280.                        ...
  1281.  
  1282.                        0.465511 0.00737759 0.0567392 ]
  1283.  
  1284.       }
  1285.  
  1286.       FaceSet {
  1287.  
  1288.          numVertices [ 3, 3, 3, 3, 3, 3, 3, 3,
  1289.  
  1290.                        ...
  1291.  
  1292.                        3, 3, 3, 3, 3, 3, 3, 3 ]
  1293.  
  1294.       }
  1295.  
  1296.    }
  1297.  
  1298. }
  1299.  
  1300. Using the File Translator in Another Program
  1301.  
  1302. The following excerpt shows how you could read another file format from 
  1303. within an Inventor application using an external translator program.
  1304.  
  1305. The program that translates the file into Inventor format is named SgoToIv. 
  1306. The file being translated is myfile.sgo. The translator program writes to stdout 
  1307. which has been piped via popen() to fp. Inventor's file reader is set to read 
  1308. from fp.
  1309.  
  1310.    FILE *fp = popen("SgoToIv myfile.sgo", "r");
  1311.  
  1312.    SoNode *root;
  1313.  
  1314.    SoInput in;
  1315.  
  1316.    in.setFilePointer(fp);
  1317.  
  1318.    if (! SoDB::read(&in, root)) 
  1319.  
  1320.       fprintf(stderr, "Read error\n");
  1321.  
  1322.    in.closeFile();
  1323.  
  1324.    pclose(fp);
  1325.  
  1326.  
  1327.  
  1328.  
  1329. Chapter 3
  1330.  
  1331. 3.    Tips and Guidelines
  1332.  
  1333. This section presents tips for testing the results of your translator program, 
  1334. for creating efficient scene graphs, and for verifying the file translation. It 
  1335. also suggests guidelines for writing an Inventor file translator.
  1336.  
  1337. Tips
  1338.  
  1339. The following sections offer general tips for writing an Inventor file 
  1340. translator.
  1341.  
  1342. Testing the Results
  1343.  
  1344. One way to test the Inventor file produced by your file translator is to 
  1345. perform a read test using the ivcat command:
  1346.  
  1347. ivcat filename.iv
  1348.  
  1349. This command prints the specified Inventor file in ASCII to stdout. If there 
  1350. are any syntax errors in the file, ivcat prints error messages for them. Use the 
  1351. -b option to print the specified Inventor file in binary to stdout.
  1352.  
  1353. As the saying goes, "Seeing is believing." Another way to test the results of 
  1354. your file translator program is to use the ivview application to read your new 
  1355. Inventor scene graph and display the results. To use it type:
  1356.  
  1357. ivview filename.iv
  1358.  
  1359. Creating an Efficient Scene Graph
  1360.  
  1361. You can use ivquicken to improve the rendering performance of the scene 
  1362. graphs created by your translator program. See the reference page for 
  1363. ivquicken to learn more about this utility program.
  1364.  
  1365. You can also attempt to increase performance by having your program make 
  1366. a second pass through the database, condensing redundant nodes into fewer 
  1367. nodes. For example:
  1368.  
  1369. o    If a number of nodes share the same material, for example, you can 
  1370. insert a material node in the scene graph so that multiple nodes will 
  1371. inherit the same material value. In the SgoToIv example program, if all 
  1372. the vertices of an object have the same color, you can isolate the 
  1373. material and use OVERALL material binding.
  1374.  
  1375. o    If you are changing only the diffuse color attribute, use an Inventor 
  1376. SoBaseColor node rather than an SoMaterial node (as shown in the 
  1377. example program SgoToIv).
  1378.  
  1379. o    If you know that a shape is solid or has ordered vertices, specify this 
  1380. information in an SoShapeHints node. In general, the more 
  1381. information you specify with SoShapeHints, the faster the rendering 
  1382. speed. The exception to this rule is that when you specify ordered 
  1383. vertices, but not a solid shape, rendering may be slower because 
  1384. two-sided lighting is automatically turned on and backface culling is 
  1385. turned off.
  1386.  
  1387. Verifying Values
  1388.  
  1389. You may also need to check the resulting Inventor scene graph to be sure that 
  1390. all values fall into the appropriate range. Many SGO files, for example, have 
  1391. color values that are out of range. You might want to add code that resets 
  1392. colors to valid values. The verifyColors() function in the "Complete Sample 
  1393. Program" in Chapter 2 provides an example of this. This code uses an 
  1394. Inventor search action to locate the base color nodes in the scene graph. It 
  1395. obtains the number of values in the base color node, loops through the 
  1396. values, and checks them. If the value is out of range, it resets the value.
  1397.  
  1398. If your translator generates nodes other than base color that have color fields 
  1399. (such as lights and materials), make sure their values are valid as well.
  1400.  
  1401. Automatic Normal Generation
  1402.  
  1403. If your data does not contain normals, Inventor can generate them 
  1404. automatically during rendering (but not in the file format). Inventor 
  1405. generates normals automatically if DEFAULT normal binding is used and 
  1406. you do not specify any normals. You can also use ivquicken to generate 
  1407. normals.
  1408.  
  1409. Guidelines for Writing an Inventor File Translator
  1410.  
  1411. The following guidelines are suggested so that applications can access 
  1412. Inventor translators in a consistent manner. These guidelines include
  1413.  
  1414. o    Inventor file suffix
  1415.  
  1416. o    application name and command line syntax
  1417.  
  1418. o    general conventions for error handling
  1419.  
  1420. o    manual page
  1421.  
  1422. File Suffix
  1423.  
  1424. It is recommended that .iv be used as the filename extension for Inventor 
  1425. files.
  1426.  
  1427. Application Name and Command Line Syntax
  1428.  
  1429. The recommended name for the translator application is
  1430.  
  1431. XxxToIv              or             IvToXxx
  1432.  
  1433. where Xxx represents the name of the non-Inventor file format and Iv 
  1434. represents Inventor file format.
  1435.  
  1436. The command line syntax is:
  1437.  
  1438. XxxToIv    filename     [ > filename.out]
  1439.  
  1440. where filename is the name of the file to be translated. By default, the output 
  1441. is directed to stdout. If desired, filename can default to stdin. However, be 
  1442. sure to implement the explicit filename option to guarantee compatibility 
  1443. with other file translators.
  1444.  
  1445. Error Handling
  1446.  
  1447. The application should return 0 if there are no errors. It should return 1 (or 
  1448. any other nonzero code) if errors occur. Error messages should be sent to 
  1449. stderr.
  1450.  
  1451. Manual Page
  1452.  
  1453. Write a manual page for the translator program, in standard UNIX man page 
  1454. format. Here is an example for the SgoToIv program.
  1455.  
  1456. SgoToIv(1)       Silicon Graphics                 SgoToIv(1)
  1457.  
  1458. NAME
  1459.  
  1460.   SgoToIv - translates an SGO object file to an Inventor file
  1461.  
  1462. SYNOPSIS
  1463.  
  1464.   SgoToIv file.sgo [> file.iv]
  1465.  
  1466. DESCRIPTION
  1467.  
  1468.   SgoToIv reads a single SGO file, translates to Inventor,
  1469.  
  1470.   and writes the result to stdout.
  1471.  
  1472. NOTES
  1473.  
  1474.   SgoToIv does not handle SGO triangle meshes.
  1475.  
  1476.  
  1477.  
  1478. Page 1              Release 1.0                February 1994
  1479.  
  1480. Conventions Used in Inventor Files
  1481.  
  1482. The following conventions are used by the Inventor file format.
  1483.  
  1484. o    The meter is the default unit for all data. (Use the SoUnits node to scale 
  1485. to other units.)
  1486.  
  1487. o    The positive y axis points up. The positive z axis extends towards the 
  1488. viewer's eye (out of the screen).
  1489.  
  1490. o    Colors are expressed as red, green, blue values.
  1491.  
  1492. o    All fields within nodes have default values. See the Open Inventor Nodes 
  1493. Quick Reference for a list of these values.
  1494.  
  1495. o    The vertices of polygons in vertex-based shapes should be specified 
  1496. consistently in either clockwise or counter-clockwise order.
  1497.  
  1498.  
  1499.