OFF - A 3D Object File Format Randi J. Rost 6-November-1986 Updated 12-October-1989 Digital Equipment Corporation Workstation Systems Engineering 100 Hamilton Ave. Palo Alto, Ca. 94301 This document describes the data format developed by WSE for the interchange and archiving of three-dimensional objects. This format, called OFF (for Object File Format), is general, flexi- ble, and extensible. It supports ASCII text ver- sions of objects for the purpose of interchange, and binary versions for efficiency of reading and writing. It is assumed that applications will develop their own, more efficient format for internal storage and operation on three- dimensional objects. _1. _I_n_t_r_o_d_u_c_t_i_o_n One of the most time-consuming tasks in computer anima- tion projects is designing the 3D models that will be used. Many computer animation houses have found that owning a large number of databases makes it easier for them to take on new projects at a lower cost (time and $$$). The cost of initially creating an object can be amortized over the number of times it can be re-used. It is our intention to promote the use of OFF files within (and perhaps even out- side of) Digital in an effort to build up our collection of useful 3D models. The file format itself is not limiting: OFF files can be used for a wide variety of object types. None of the "policy decisions" are hard-wired in the design of the file format, or in the support library routines that allow read- ing and writing of OFF files. Rather, the policy decisions have been left up to the designers since the format supports "generic" object definitions. We have developed specific conventions for objects that are defined by polygons for use April 26, 1990 - 2 - within WSE, and we'd encourage others to adopt these conven- tions as well in order to promote the interchange of useful object data bases. The _d_x_m_o_d_e_l application (also developed by WSE) is an example of an application that permits reading and writing of OFF files. This paper describes the Object File Format itself, the conventions we've adopted at WSE, and the library of support routines that can be used to read and write OFF files. _2. _D_e_s_i_g_n _G_o_a_l_s _a_n_d _N_o_n-_G_o_a_l_s Design goals for the Object File Format include: 1) Simple. Simple cases should be simple. It should be possible to type in all the data required for a simple object (such as a cube) by hand. 2) Powerful. The Object File Format should be capable of accomodating complicated objects (many vertices, polygons, and a wide range of attributes). 3) Portable. ASCII text file representation required for portability across operating systems and hardware. This also allows operations on OFF files by familiar system utilities (text editors and the like). 4) Efficient. Binary text file representation required to allow efficient reading and writing of OFF files. It is assumed that reading/writing an object is a costly operation, but reading and writing ASCII data is just _t_o_o slow. 5) General. The format should address the general case of the three-dimensional object, not a single particular case. 6) Extensibile. Make sure the format can be easily extended to eventually support other primitives such as bezier patches. 7) No Favors. Avoid hard-wiring policy decisions. Rather, provide generic building blocks capable of sup- porting several styles and document a set of strongly encouraged conventions that we have adopted. There are also things that were specifically non-goals in the design of the Object File Format. 1) Internal Format. The Object File Format is not intended to be forced upon applications as an internal April 26, 1990 - 3 - format that must be used. Rather, it should be con- sidered a means for inputting and outputting object descriptions in a device-, language-, and operating system-independent format. Applications should feel free to develop and maintain the most useful/efficient data format they can, and only convert to/from OFF when input or output of a standardized object is desired. 2) Object Conventions. OFF conventions are documented only for objects in polygonal form at this point. It is anticipated that the Object File Format can be easily extended to handle bezier surface patches and other primitives in the future. _3. _O_b_j_e_c_t_s For the purposes of the Object File Format, we'll adopt a very general definition of an _o_b_j_e_c_t. An _o_b_j_e_c_t is simply a list of properties (name, description, author, copyright information, geometry data, colors, etc.) The most important information about the object can be found in the _h_e_a_d_e_r _f_i_l_e for the object. The header file is always an ASCII text file that, by convention, is named _n_a_m_e.aoff where _n_a_m_e is the object name. See Appendix B for an example of an OFF object header file. A few of these properties (name, description, author, copyright, type) are common to every type of 3D object and are considered standard properties. The standard properties are built into the routines that manipulate 3D objects. The rest of the properties may vary with the type of object, and so are defined by convention only. The _n_a_m_e of an object is used to concisely describe the object itself. For example, we have objects named "x29", "banana" and "vw". By convention, this name also becomes the prefix for OFF data filenames when an object is read or written, so it is best to keep it fairly short. The _d_e_s_c_r_i_p_t_i_o_n is used to more fully describe the object itself. It may contain the time and date of creation or more prose describing the object. The _a_u_t_h_o_r should be the name of the person (or com- pany, or utility) that created the object. We should always try to give credit where credit is due. This field tells you who to thank for spiffy objects or whose cage to rattle when a problem with an OFF file is discovered. The _c_o_p_y_r_i_g_h_t field contains information dealing with the distribution of the object data. Some object databases April 26, 1990 - 4 - will be regarded by a company as proprietary. These objects should not be copied or distributed without consent. Other objects (vw, x29) were developed by companies or individuals and can be copied or used as long as the copyright notice appears and proper credit is given. Still other objects (cube, sphere, etc.) have been placed in the public domain. We have tried to be as careful as possible in preserving copyright and author information for the objects we have collected, but sometimes the information was lost or una- vailable. Be sure and honor copyright notices. If you don't, you (or your company) could end up in big trouble. The _t_y_p_e field contains the type of the object. For now, only one type of object is supported: polygon objects. It is anticipated that polyline and surface patch-type objects will be supported in the future as well. Also contained in the object header file are lines that describe the various properties of the object. Each line in the object header file that describes an attribute of the object other than a standard attribute must contain the pro- perty name, the property type, the data format and either a file name or a string containing default data, depending on the property type. Each of these four items is an ASCII string, separated by white space. The _p_r_o_p_e_r_t_y _n_a_m_e uniquely describes the property. Property names for which we have defined conventions (see Appendix A) include geometry, polygon_colors, vertex_colors, back_faces, vertex_order, polygon_normals, vertex_normals, diffuse_coef, specular_coef, and specular_power. OFF currently supports four _p_r_o_p_e_r_t_y _t_y_p_e_s: _d_e_f_a_u_l_t, _g_e_n_e_r_i_c, _i_n_d_e_x_e_d, and _i_n_d_e_x_e_d__p_o_l_y. If a property is indi- cated to be of type _d_e_f_a_u_l_t, the part of the line after the data format is assumed to contain some default data that will be applied to the entire object. For instance, it may make sense to give the entire object a default color and default diffuse and specular coefficients. If the property type is either generic, indexed, or indexed_poly (described more fully below), the remainder of the line is taken to be a file name that can be opened and read to obtain the information for the property. The data format indicates what type of data will be found on the remainder of the line if the property type is default, otherwise what kind of data will be found in the specified file. The data format is a string of characters (no spaces) that indicate the order and type of the data. Supported primitive data types are: April 26, 1990 - 5 - f - A number stored internally as a 32-bit floating point number d - A number stored internally as a 64-bit double-precision floating point number i - A number stored internally as a 32-bit integer value h - A number stored internally as a 16-bit integer value b - A number stored internally as an 8-bit integer value s - A 32-bit pointer to a null-terminated string of charac- ters If, for instance, you were interested in using 32-bit floating values for r, g, and b default color values, you might have a line in the object header file that reads polygon_colors default fff 1.0 0.8 0.0 It is important to understand that in all cases, the "string" (s) data primitive will indicate a pointer to a string that is stored internally in the data block for an object and not the string itself. _4. _A_S_C_I_I _P_r_o_p_e_r_t_y _F_i_l_e_s OFF supports ASCII text files as a way of providing for language-, hardware-, and operating system-independent object data files. The three types of data files currently supported by OFF are generic, indexed, and indexed_poly. _4._1. _G_e_n_e_r_i_c _F_i_l_e_s Generic files contain only a _c_o_u_n_t value followed by _c_o_u_n_t data items of the type specified by the data format. Each data item can be comprised of some combination of the primitive data types described in Section 3. Generic data files are useful for storing attributes which are unique at every vertex or polygon (such as color or normals). String data items in ASCII generic files may not con- tain spaces or other white space. 8-bit integers must be listed in the range 0-255. See Appendix D for an example of an ASCII generic data file. April 26, 1990 - 6 - _4._2. _I_n_d_e_x_e_d _F_i_l_e_s Indexed files make use of a list of indices in order to reduce the amount of data required to store a property, and to provide a useful level of indirection. For instance, indexed files are commonly used to maintain per-polygon color information. If an object has just five colors, the indexed data file would contain the list of the five colors followed by an index from one to five for each of the polygons in the object. If an application maintains the indirection, it is possible for the user to easily select five different colors to be used on the model. An indexed file begins with two integers separated by white space: the number of data items and the number of indices that will be provided. Following these two values is the list of data items that is to be used. Each data item in this list can be some combination of the primitive types described in Section 3. Following the data items is a list of indices each of which is a pointer to one of the items in the list of data items. The list of data items is assumed to begin starting at one, not zero. _4._3. _I_n_d_e_x_e_d__P_o_l_y _F_i_l_e_s Indexed_poly files take advantage of a connectivity list to reduce the amount of information needed to store a list of polylines, polygons, or normals. The unique geometry items (e.g., vertices) are listed in the first part of the file. Following this list is a connectivity list. Each line in the connectivity list contains a _c_o_u_n_t value followed by _c_o_u_n_t indices (pointers) to information in the geometry list. (Items in the geometry list are indexed starting from one, not zero.) The first line of an indexed_poly data file contains three integers, separated by white space. The first number on this line indicates the number of data items (vertices/normals) that follow, the second number indicates the number of polylines/polygons that follow the data list, and the third indicates the total number of edges that are contained in the polyline/polygon connectivity list. String data items in ASCII indexed_poly files may not contain spaces or other white space. 8-bit integers must be listed in the range 0-255. See Appendix C for an example of an ASCII indexed_poly file. April 26, 1990 - 7 - _5. _B_i_n_a_r_y _O_F_F _F_i_l_e_s The same three types of data files described above are also supported in binary format. There are a few minor differences. _5._1. _G_e_n_e_r_i_c _F_i_l_e_s Binary generic files begin with the first 32-bit word equal to OFF_GENERIC_MAGIC as defined in the include file _o_f_f._h. The second word in the file is the _c_o_u_n_t (number of data items in the file). Following the _c_o_u_n_t is the data itself. The data format in the header file describes the primi- tives that make up each data item in the list. Within each data item, floats, doubles, 32-bit integers, and string pointers will all begin on a word boundary. 16-bit integers will all begin on a half-word boundary. Thus, if your data format for the data items in a generic data file is "bbb" (three byte values), each data item will be stored as three bytes followed by a null byte so that each data item will begin on a word boundary. Strings begin with a 32-bit _c_o_u_n_t followed by _c_o_u_n_t characters followed by a null character. The string is null-padded so it will end on a word boundary. (It is assumed that for strings, the length will be read and then the necessary memory will be allocated and the string read in. This eliminates a problem with having variable-length data in the data files. Anyway, strings in files are really only there for symmetry with default values, where strings are really useful. The performance implications for files containing strings will probably be enough to prevent people from using them.) _5._2. _I_n_d_e_x_e_d _F_i_l_e_s Binary indexed files begin with the first 32-bit word equal to OFF_INDEXED_MAGIC as defined in the include file _o_f_f._h. The second word in the file is the _c_o_u_n_t (number of data items in the file). The third word in the file is _n_u_m__i_n_d_i_c_e_s (number of indices in the index list). Following these two integers is the data for the data item list. Each item in the data item list will begin on a word boundary. The data format in the header file describes the primitives that make up each data item in the list. Within each data item, floats, doubles, 32-bit integers, and string pointers will all begin on a word boundary. 16-bit April 26, 1990 - 8 - integers will all begin on a half-word boundary. Thus, if your data format for the data items in an indexed data file is "bbb" (three byte values), each data item will be stored as three bytes followed by a null byte so that each data item will begin on a word boundary. Strings begin with a 32-bit _c_o_u_n_t followed by _c_o_u_n_t characters followed by a null character. The string is null-padded so it will end on a word boundary. Following the data item list is a list of index values. This list will also begin on a word boundary, however, the index values within this list are short integers and will be packed two to each 32-bit word. _5._3. _I_n_d_e_x_e_d__P_o_l_y _F_i_l_e_s Binary indexed_poly files begin with the first 32-bit word equal to OFF_INDEXED_POLY_MAGIC as defined in the include file off.h. The second word is the number of data items in the vertex list (_n_p_t_s), the third word is the number of polylines/polygons in the list (_n_p_o_l_y_s), and the fourth word is the number of edges contained in the connec- tivity list (_n_c_o_n_n_e_c_t_s). Starting at the fifth word in the file is a list of _n_p_t_s data items, followed by _n_p_o_l_y_s short integers contain- ing polyline/polygon vertex counts, followed by _n_c_o_n_n_e_c_t_s short integers which are indices into the array of data items. (This arrangement is slightly different than that used for indexed_poly files in ASCII format for efficiency reasons.) The same restrictions that apply to the data types for generic binary files apply to indexed_poly binary files as well. In addition, the vertex count array which follows the geometry data in an indexed_poly file will always begin on a word boundary. The connectivity array that follows the ver- tex count array will not necessarily start on a word boun- dary, but will always begin _n_p_o_l_y_s * _s_i_z_e_o_f(_s_h_o_r_t) bytes after the start of the vertex count array. _6. _O_f_f._a _a_n_d _O_b_j_e_c_t_s._h An include file and a library of routines has been pro- vided for UNIX/C programmers to more easily manipulate OFF files. The basic concepts of "reading" and "writing" OFF files are supported in this library of routines. The library is a software layer on top of the operating system file I/O interface, with special knowledge of OFF files. This subroutine library provides a mechanism for accessing April 26, 1990 - 9 - the syntactical elements of an object file, but makes no attempt to understand the semantics. Higher level inter- faces can be layered on top. The subroutine library refers to an object as a pointer to an _O_F_F_O_b_j_D_e_s_c. This structure contains a pointer to the first property in the property list. It is defined as fol- lows: typedef struct { OFFProperty *FirstProp; /* Pointer to first property in list */ } OFFObjDesc; The information that describes the object is contained in a linked list of property structures. The first such structure in the list is pointed at by an _O_F_F_O_b_j_D_e_s_c struc- ture. The property structures have the form: typedef struct _OFFProp { char PropName[40]; int PropType; char PropFileName[256]; int PropCount; char DataFormat[40]; char *PropData; struct _OFFProp *NextProp; } OFFProperty; _P_r_o_p_N_a_m_e contains a string defining one of the property types for which a convention has been defined. This includes the property names "name", "author", "description", "copyright", "comment", "geometry", "polygon_colors", "polygon_normal", etc. For a complete list of property names, see Appendix A. (The special attribute type "com- ment" is supported so that blank lines and comment lines can be preserved if an object file is read and then written.) The _P_r_o_p_T_y_p_e field contains a value equal to _O_F_F__D_E_F_A_U_L_T__D_A_T_A, _O_F_F__G_E_N_E_R_I_C__D_A_T_A, _O_F_F__I_N_D_E_X_E_D__D_A_T_A, or _O_F_F__I_N_D_E_X_E_D__P_O_L_Y__D_A_T_A which defines the basic type for the property. The _P_r_o_p_F_i_l_e_N_a_m_e is required if _P_r_o_p_T_y_p_e is something other than _O_F_F__D_E_F_A_U_L_T__D_A_T_A. It contains a string representing the name of the file to be read/written for this attribute. This file name should _n_o_t contain a path leading up to the file itself, only the actual file name. The object search path mechanism (see Section 7) should be April 26, 1990 - 10 - used instead. The _P_r_o_p_C_o_u_n_t indicates the actual number of data items associated with this particular attribute. After reading in an object, properties of type _O_F_F__D_E_F_A_U_L_T__D_A_T_A will have a _P_r_o_p_C_o_u_n_t of one, properties of type _O_F_F__G_E_N_E_R_I_C__D_A_T_A will have a _P_r_o_p_C_o_u_n_t equal to the number of generic data items in the list, properties of type _O_F_F__I_N_D_E_X_E_D__D_A_T_A will have a _P_r_o_p_C_o_u_n_t equal to the number of items in the data item list, and properties of type _O_F_F__I_N_D_E_X_E_D__P_O_L_Y__D_A_T_A will have a _P_r_o_p_C_o_u_n_t equal to the number of data items in the geometry list. The _D_a_t_a_F_o_r_m_a_t field contains a string of characters corresponding to primitive data items. The composite type of the data for this property can then be deduced by looking at this field and applying the rules for padding to word and half-word boundaries. The _P_r_o_p_D_a_t_a field contains a pointer to a block of memory containing the actual data for this property. This data will have the same data alignment restrictions as a binary file has, with the exception of strings. As strings are read in, memory is malloc'ed to hold them and a pointer to the string is stored in the appropriate field in the data list. This means that all primitive data types will have a fixed size and lengths and alignments can be computed more easily. The _N_e_x_t_P_r_o_p field contains a pointer to the next pro- perty structure in the property list. The routines contained in the subroutine library are defined below. #include "off.h" int OFFReadObj(Obj, FileName) OFFObjDesc *Obj; char *FileName; int OFFWriteObj(Obj, FileName, Directory, FileType); OFFObjDesc *Obj; char *FileName; char *Directory; int FileType; void OFFPackObj(Obj) OFFObjDesc *Obj; void OFFPackProperty(Property) April 26, 1990 - 11 - OFFProperty *Property; int OFFReadGeneric(Property, FileName) OFFProperty *Property; char *FileName; int OFFWriteGeneric(Property, FileName, FileType) OFFProperty *Property; char *FileName; int FileType; int OFFReadIndexed(Property, FileName) OFFProperty *Property; char *FileName; int OFFWriteIndexed(Property, FileName, FileType) OFFProperty *Property; char *FileName; int FileType; int OFFReadIndexedPoly(Property, FileName) OFFProperty *Property; char *FileName; int OFFWriteIndexedPoly(Property, FileName, FileType) OFFProperty *Property; char *FileName; int FileType; OFFObjDesc *OFFCreateObj() int OFFDestroyObj(Obj) OFFObjDesc *Obj; OFFProperty *OFFAddProperty(Obj) OFFObjDesc *Obj; int OFFRemoveProperty(Obj, PropertyName) OFFObjDesc *Obj; char *PropertyName; int OFFFreeProperty(Property) OFFProperty *Property; _O_F_F_R_e_a_d_O_b_j will attempt to open the object header file named _F_i_l_e_N_a_m_e and read the object data it contains. A pointer to the constructed object structure will be returned in _O_b_j when the object has been read. An attempt will be made to open the specified file first as given, then con- catenated in turn with each of the directories specified by the environment search path variable _O_B_J__P_A_T_H. The property April 26, 1990 - 12 - list for the object is built as the file is read. Upon return, the client need only traverse the property list and select the data it needs. This routine calls _O_F_F_R_e_a_d_G_e_n_e_r_i_c and _O_F_F_R_e_a_d_I_n_d_e_x_e_d_P_o_l_y in order to read associated data files. _O_F_F_R_e_a_d_O_b_j will return 0 if the read operation was successful, -1 otherwise. _O_F_F_W_r_i_t_e_O_b_j will attempt to write the object pointed at by _O_b_j using the filename specified by _F_i_l_e_N_a_m_e. The file will be written in the directory indicated by _D_i_r_e_c_t_o_r_y. If _F_i_l_e_T_y_p_e is _O_F_F__A_S_C_I_I, the file will be written as an ASCII text OFF file. If _F_i_l_e_T_y_p_e is _O_F_F__B_I_N_A_R_Y, the file will be written as a binary OFF file. The property list for the object is traversed and each property of the object is writ- ten out in turn. This routine calls _O_F_F_W_r_i_t_e_G_e_n_e_r_i_c and _O_F_F_W_r_i_t_e_I_n_d_e_x_e_d_P_o_l_y in order to write associated data files. _O_F_F_W_r_i_t_e_O_b_j will return 0 if the write operation was suc- cessful, -1 otherwise. _O_F_F_P_a_c_k_O_b_j attempts to _p_a_c_k the object pointed at by _O_b_j. Packing only applies to geometry and normals stored in indexed_polygon format. See _O_F_F_P_a_c_k_P_r_o_p_e_r_t_y below. _O_F_F_P_a_c_k_P_r_o_p_e_r_t_y packs the property pointed at by _P_r_o_- _p_e_r_t_y. Packing can only be applied to indexed_polygon pro- perties with format ``fff''. (This is normally the case for geometry and normals properties.) Packing works by sharing common data (vertex or normal) values. Since each vertex and vertex normal of an object is usually shared by three or more polygons, this can save a great deal of space and rendering time. If _P_r_o_p_e_r_t_y could not be packed, it is not modified; otherwise the property data is changed in-place. _O_F_F_R_e_a_d_G_e_n_e_r_i_c will read the generic data file named _F_i_l_e_N_a_m_e (here _F_i_l_e_N_a_m_e contains the full path name) into the property structure pointed at by _P_r_o_p_e_r_t_y. This routine will allocate the space it needs in order to read in the data. A pointer to this allocated data space will be stored in the _P_r_o_p_D_a_t_a field of the specified _p_r_o_p_e_r_t_y as described earlier. The entire object, including all allocated memory resources can later be deallocated by calling _O_F_F_D_e_s_t_r_o_y_O_b_j. This routine will not typically be called directly by appli- cations. _O_F_F_R_e_a_d_G_e_n_e_r_i_c will return 0 if the read operation was successful, -1 otherwise. _O_F_F_W_r_i_t_e_G_e_n_e_r_i_c will write the generic data associated with _P_r_o_p_e_r_t_y into the file _F_i_l_e_N_a_m_e (here _F_i_l_e_N_a_m_e contains the full path name of the file to be written). If _F_i_l_e_T_y_p_e is _O_F_F__A_S_C_I_I, the file will be written as an ASCII text gen- eric data file. If _F_i_l_e_T_y_p_e is _O_F_F__B_I_N_A_R_Y, the file will be written as a binary generic data file. This routine will not typically be called directly by applications. April 26, 1990 - 13 - _O_F_F_W_r_i_t_e_G_e_n_e_r_i_c will return 0 if the write operation was successful, -1 otherwise. _O_F_F_R_e_a_d_I_n_d_e_x_e_d will read the indexed data file named _F_i_l_e_N_a_m_e (here _F_i_l_e_N_a_m_e contains the full path name) into the property structure pointed at by _P_r_o_p_e_r_t_y. This routine will allocate the space it needs in order to read in the data. A pointer to this allocated data space will be stored in the _P_r_o_p_D_a_t_a field of the specified _p_r_o_p_e_r_t_y as described earlier. The entire object, including all allocated memory resources can later be deallocated by calling _O_F_F_D_e_s_t_r_o_y_O_b_j. This routine will not typically be called directly by appli- cations. _O_F_F_R_e_a_d_I_n_d_e_x_e_d will return 0 if the read operation was successful, -1 otherwise. _O_F_F_W_r_i_t_e_I_n_d_e_x_e_d will write the indexed data associated with _P_r_o_p_e_r_t_y into the file _F_i_l_e_N_a_m_e (here _F_i_l_e_N_a_m_e contains the full path name of the file to be written). If _F_i_l_e_T_y_p_e is _O_F_F__A_S_C_I_I, the file will be written as an ASCII text indexed data file. If _F_i_l_e_T_y_p_e is _O_F_F__B_I_N_A_R_Y, the file will be written as a binary indexed data file. This routine will not typically be called directly by applications. _O_F_F_W_r_i_- _t_e_I_n_d_e_x_e_d will return 0 if the write operation was success- ful, -1 otherwise. _O_F_F_R_e_a_d_I_n_d_e_x_e_d_P_o_l_y will read the indexed_poly data file named _F_i_l_e_N_a_m_e (here _F_i_l_e_N_a_m_e contains the full path name) into the property structure pointed at by _P_r_o_p_e_r_t_y. This routine will allocate the space it needs in order to read in the data. A pointer to this allocated data space will be stored in the _P_r_o_p_D_a_t_a field of the specified _p_r_o_p_e_r_t_y as described earlier. The entire object, including all allo- cated memory resources can later be deallocated by calling _O_F_F_D_e_s_t_r_o_y_O_b_j. This routine will not typically be called directly by applications. _O_F_F_R_e_a_d_I_n_d_e_x_e_d_P_o_l_y will return 0 if the read operation was successful, -1 otherwise. _O_F_F_W_r_i_t_e_I_n_d_e_x_e_d_P_o_l_y will write the indexed_poly data associated with _P_r_o_p_e_r_t_y into the file _F_i_l_e_N_a_m_e (here _F_i_l_e_N_a_m_e contains the full path name of the file to be writ- ten). If _F_i_l_e_T_y_p_e is _O_F_F__A_S_C_I_I, the file will be written as an ASCII text indexed_poly data file. If _F_i_l_e_T_y_p_e is _O_F_F__B_I_N_A_R_Y, the file will be written as a binary indexed_poly data file. This routine will not typically be called directly by applications. _O_F_F_W_r_i_t_e_I_n_d_e_x_e_d_P_o_l_y will return 0 if the write operation was successful, -1 other- wise. _O_F_F_C_r_e_a_t_e_O_b_j allocates and initializes an _O_F_F_O_b_j_D_e_s_c structure A pointer to the newly-created structure is returned. The null pointer is returned if the operation was unsuccessful. April 26, 1990 - 14 - _O_F_F_D_e_s_t_r_o_y_O_b_j deallocates all memory resources associ- ated with the object pointed at by _O_b_j. It works by calling _O_F_F_F_r_e_e_P_r_o_p_e_r_t_y for each property in the property list for the specified object. _O_F_F_A_d_d_P_r_o_p_e_r_t_y adds a property structure to the pro- perty list associated with the object pointed at by _O_b_j, initializes it, and returns a pointer to it. The null pointer is returned if the operation was unsuccessful. _O_F_F_R_e_m_o_v_e_P_r_o_p_e_r_t_y deletes the named property from the object pointed at by _O_b_j. This routine returns -1 if the named property is not found in the property list for the specified object. _O_F_F_F_r_e_e_P_r_o_p_e_r_t_y frees all the memory resources allo- cated to the property structure specified by _P_r_o_p_e_r_t_y as well as the property structure itself. This routine will not typically be called directly by applications. _7. _O_b_j_e_c_t _S_e_a_r_c_h _P_a_t_h It is important to avoid embedding path names in object files. When an object is transported to another system, chances are slim that the same directory structure will exist. The _O_F_F_R_e_a_d_O_b_j routine in libobj.a knows about an environment variable named _O_B_J__P_A_T_H that is used to overcome this problem. When an object is read, an attempt is first made to open it in the current working directory. If that attempt fails, the directories specified in the _O_B_J__P_A_T_H environment variable are tried in turn until the file is successfully opened or the directory list is exhausted. _O_B_J__P_A_T_H con- tains a list of directories, separated by spaces, that are to be searched for the named objects. The name of the directory where a successful open operation occurred is used for opening associated data files as well. This means that all of the data files for a par- ticular object must reside in the same directory. It is hoped that in this way, users will be able to draw on one or more collections of "standard" objects in addition to their own private collections of objects. April 26, 1990 - 15 - _8. _A_p_p_e_n_d_i_x _A: _C_o_n_v_e_n_t_i_o_n_s _f_o_r _P_o_l_y_g_o_n_a_l _O_b_j_e_c_t_s This list contains the conventions we have adopted for describing 3D polygonal objects which are defined in some three-dimensional model coordinate system. Items in regular type are string literal, printed as they would appear in an OFF file, and item in italics indicate data values that will vary from object to object. By convention, the header for an ASCII OFF file is suffixed with ".aoff" and the header for a binary OFF file is suffixed with ".off". There are two choices for how colors may be stored. If they are stored as generic data, the suffixes used are ".{b}pcol" for polygon colors and ".{b}vcol" for vertex colors. If they are stored as indexed data, the suffixes used are ".{b}ipcol" and ".{b}ivcol". box; c|c|c|c|c|c l|l|l|l|l|l. Property Type Format Defaults ASCII filename Binary Filename = name ***** ***** _o_b_j_n_a_m_e ***** ***** author ***** ***** _a_u_t_h_o_r ***** ***** description ***** ***** _d_e_s_c_r_i_p_t_i_o_n ***** ***** copyright ***** ***** _c_o_p_y_r_i_g_h_t ***** ***** type ***** ***** polyline ***** ***** ***** ***** polygon ***** ***** geometry indexed_poly fff ***** _n_a_m_e.geom _n_a_m_e.bgeom polygon_colors generic fff ***** _n_a_m_e.pcol _n_a_m_e.bpcol vertex_colors generic fff ***** _n_a_m_e.vcol _n_a_m_e.bvcol polygon_colors indexed fff ***** _n_a_m_e.ipcol _n_a_m_e.bipcol vertex_colors indexed fff ***** _n_a_m_e.ivcol _n_a_m_e.bivcol back_faces default s cull ***** ***** display ***** ***** reverse ***** ***** vertex_order default s clockwise ***** ***** counter- clockwise ***** ***** counterclock- wise ***** ***** polygon_normals generic fff ***** _n_a_m_e.pnorm _n_a_m_e.bpnorm vertex_normals generic fff ***** _n_a_m_e.vnorm _n_a_m_e.bvnorm diffuse_coef default f _v_a_l_u_e ***** ***** specular_coef default f _v_a_l_u_e ***** ***** specular_power default f _v_a_l_u_e ***** ***** bounding_box default ffffff _v_a_l_u_e ***** ***** April 26, 1990 - 16 - _9. _A_p_p_e_n_d_i_x _B: _O_F_F _H_e_a_d_e_r _F_i_l_e _F_o_r _a _C_u_b_e (_c_u_b_e._a_o_f_f) ; l l. name cube author Randi J. Rost description cube with sides of red, green, blue, cyan, yellow, magenta copyright public domain type polygon ; l c c c l c c c l l l l. # Prop. data type format filename or default data #_______ _________ ______ ________________________ geometry indexed_poly fff cube.geom vertex_order default s clockwise polygon_colors generic fff cube.pcol back_faces default s cull April 26, 1990 - 17 - _1_0. _A_p_p_e_n_d_i_x _C: _L_i_s_t_i_n_g _o_f _c_u_b_e._g_e_o_m ; nw(0.5i) nw(0.5i) nw(0.5i) nw(0.5i) nw(0.5i). 8 6 24 -1.0 -1.0 1.0 -1.0 1.0 1.0 1.0 1.0 1.0 1.0 -1.0 1.0 -1.0 -1.0 -1.0 -1.0 1.0 -1.0 1.0 1.0 -1.0 1.0 -1.0 - 1.0 4 1 2 3 4 4 5 6 2 1 4 3 2 6 7 4 3 7 8 4 4 1 4 8 5 4 8 7 6 5 April 26, 1990 - 18 - _1_1. _A_p_p_e_n_d_i_x _D: _L_i_s_t_i_n_g _o_f _c_u_b_e._p_c_o_l ; l s s nw(0.5i) nw(0.5i) nw(0.5i). 6 1.0 0.0 0.0 0.0 1.0 0.0 0.0 0.0 1.0 0.0 1.0 1.0 1.0 1.0 0.0 1.0 0.0 1.0 April 26, 1990 - 19 - _1_2. _A_p_p_e_n_d_i_x _E: _L_i_s_t_i_n_g _o_f _o_f_f._h #define OFF_INDEXED_POLY_MAGIC 0xFEEDFEEDL #define OFF_GENERIC_MAGIC 0xBEEFBEEFL #define OFF_INDEXED_MAGIC 0xBADBADBAL #define OFF_BIGSTR 256 #define OFF_SMSTR 40 #define OFF_ASCII 0 #define OFF_BINARY 1 /* Types of data for object properties */ #define OFF_UNKNOWN_TYPE_DATA 0 #define OFF_STANDARD_DATA 1 #define OFF_COMMENT_DATA 2 #define OFF_DEFAULT_DATA 3 #define OFF_GENERIC_DATA 4 #define OFF_INDEXED_POLY_DATA 5 #define OFF_INDEXED_DATA 6 typedef struct _OFFProp { char PropName[OFF_SMSTR]; /* Name of property (or attribute) */ int PropType; /* Type of data for property */ char PropFileName[OFF_BIGSTR];/* Name of file that has prop data */ char DataFormat[OFF_SMSTR]; /* Pointer to property data format */ int PropCount; /* Number of data items for property*/ char *PropData; /* Pointer to property data */ struct _OFFProp *NextProp; /* Pointer to next property in list */ } OFFProperty; typedef struct { OFFProperty *FirstProp; /* Pointer to first property in list*/ } OFFObjDesc; April 26, 1990 - 20 - _1_3. _A_p_p_e_n_d_i_x _F: _D_a_t_a _S_t_r_u_c_t_u_r_e _F_o_r_m_a_t The following diagram depicts some of the data struc- tures for the object _c_u_b_e._a_o_f_f after being read by _O_F_F_R_e_a_d_O_b_j() (or just prior to being written by _O_F_F_W_r_i_- _t_e_O_b_j()). move to 0.0, 14.5 Object: [ boxht = 0.3i; boxwid = 2.0i moveht = 0.3i; movewid = 0.0i down A: box "FirstProp" move up 0.3i; move right 2.0i down B: box ] move to 1.0, 13.0 Prop1: [ boxht = 0.3i; boxwid = 1.5i moveht = 0.3i; movewid = 0.0i down A: box "PropName" down B: box "PropType" down C: box "PropFileName" down D: box "DataFormat" down E: box "PropCount" down F: box "PropData" down H: box "NextProp" move up 2.1i; move right 2.25i; boxht = 0.3i; boxwid = 3.0i moveht = 0.3i; movewid = 0.0i down I: box "name" down J: box "OFF_STANDARD_DATA" down K: box "_n_u_l_l _s_t_r_i_n_g" down L: box "_n_u_l_l _s_t_r_i_n_g" down M: box "0" down N: box down O: box ] move to 1.0, 10.0 Prop2: [ boxht = 0.3i; boxwid = 1.5i moveht = 0.3i; movewid = 0.0i down A: box "PropName" down B: box "PropType" down C: box "PropFileName" down D: box "DataFormat" down E: box "PropCount" down F: box "PropData" down H: box "NextProp" move up 2.1i; move right 2.25i; boxht = 0.3i; boxwid = 3.0i moveht = 0.3i; movewid = 0.0i down I: box "author" down J: box "OFF_STANDARD_DATA" down K: box "_n_u_l_l _s_t_r_i_n_g" down L: box "_n_u_l_l _s_t_r_i_n_g" down M: box "0" down N: box down O: box ] move to 1.0, 7.0 Prop3: [ boxht = 0.3i; boxwid = 1.5i moveht = 0.3i; movewid = 0.0i down A: box "PropName" down B: box "PropType" down C: box "PropFileName" down D: box "DataFormat" down E: box "PropCount" down F: box "PropData" down H: box "NextProp" move up 2.1i; move right 2.25i; boxht = 0.3i; boxwid = 3.0i moveht = April 26, 1990 - 21 - 0.3i; movewid = 0.0i down I: box "geometry" down J: box "OFF_INDEXED_POLY_DATA" down K: box "cube.geom" down L: box "fff" down M: box "6" down N: box down O: box ] move to 1.0, 4.0 Prop4: [ boxht = 0.3i; boxwid = 1.5i moveht = 0.3i; movewid = 0.0i down A: box "PropName" down B: box "PropType" down C: box "PropFileName" down D: box "DataFormat" down E: box "PropCount" down F: box "PropData" down H: box "NextProp" move up 2.1i; move right 2.25i; boxht = 0.3i; boxwid = 3.0i moveht = 0.3i; movewid = 0.0i down I: box "polygon_colors" down J: box "OFF_GENERIC_DATA" down K: box "cube.pcol" down L: box "fff" down M: box "6" down N: box down O: box ] move to 1.0, 1.0 Prop5: [ boxht = 0.3i; boxwid = 1.5i moveht = 0.3i; movewid = 0.0i down A: box "PropName" down B: box "PropType" down C: box "PropFileName" down D: box "DataFormat" down E: box "PropCount" down F: box "PropData" down H: box "NextProp" move up 2.1i; move right 2.25i; boxht = 0.3i; boxwid = 3.0i moveht = 0.3i; movewid = 0.0i down I: box "back_faces" down J: box "OFF_DEFAULT_DATA" down K: box "_n_u_l_l _s_t_r_i_n_g" down L: box "s" down M: box "0" down N: box down O: box "_n_u_l_l _p_o_i_n_t_e_r" ] line from Prop1.O.c to Prop1.O.c.x, (Prop1.s.y + Prop2.n.y) / 2.0 line to 0.5, (Prop1.s.y + Prop2.n.y) / 2.0 line to 0.5, Prop2.A.c.y arrow to Prop2.A.w line from Prop2.O.c to Prop2.O.c.x, (Prop2.s.y + Prop3.n.y) / 2.0 line to 0.5, (Prop2.s.y + Prop3.n.y) / 2.0 line dashed to 0.5, Prop3.A.c.y arrow to Prop3.A.w line from Prop3.O.c to Prop3.O.c.x, (Prop3.s.y + Prop4.n.y) / 2.0 line to 0.5, (Prop3.s.y + Prop4.n.y) / 2.0 line dashed to 0.5, Prop4.A.c.y arrow to Prop4.A.w line from Prop4.O.c to Prop4.O.c.x, (Prop4.s.y + Prop5.n.y) April 26, 1990 - 22 - / 2.0 line to 0.5, (Prop4.s.y + Prop5.n.y) / 2.0 line to 0.5, Prop5.A.c.y arrow to Prop5.A.w line from Object.B.c to Object.B.c.x, (Object.s.y + Prop1.n.y) / 2.0 line to 0.5, (Object.s.y + Prop1.n.y) / 2.0 line to 0.5, Prop1.A.c.y arrow to Prop1.A.w boxht = 0.3i; boxwid = 2.0i P1: box "cube" at Prop1.N.e.x + 1.5i, Prop1.N.e.y arrow from Prop1.N.c to P1.w P2: box "Randi J. Rost" at Prop2.N.e.x + 1.5i, Prop2.N.e.y arrow from Prop2.N.c to P2.w P3: box " 8 6 24 -1.0 -1.0" at Prop3.N.e.x + 1.5i, Prop3.N.e.y P4: box " 1.0 -1.0 1.0 1.0 1.0" with .nw at P3.sw P5: box " 1.0 1.0 1.0 -1.0 1.0" with .nw at P4.sw P6: box "-1.0 -1.0 -1.0 -1.0 1.0" with .nw at P5.sw P7: box "-1.0 1.0 1.0 -1.0 1.0" with .nw at P6.sw P8: box "-1.0 -1.0 4 4 4 4 4 4" with .nw at P7.sw P9: box "1 2 3 4 5 6 2 1" with .nw at P8.sw Pa: box "3 2 6 7 3 7 8 4" with .nw at P9.sw Pb: box "1 4 8 5 8 7 6 5" with .nw at Pa.sw arrow from Prop3.N.c to P3.w Pc: box "6 1.0 0.0 0.0 0.0" at Prop4.N.e.x + 1.5i, Prop4.N.e.y Pd: box "1.0 0.0 0.0 0.0 1.0" with .nw at Pc.sw Pe: box "0.0 1.0 1.0 1.0 1.0" with .nw at Pd.sw Pf: box "0.0 1.0 0.0 1.0 " with .nw at Pe.sw arrow from Prop4.N.c to Pc.w boxht = 0.3i; boxwid = 0.5i Pg: box at Prop5.N.e.x + 1.0i, Prop5.N.e.y Ph: box "cull" at Pg.e.x + 1.0i, Prop5.N.e.y arrow from Pg.c to Ph.w arrow from Prop5.N.c to Pg.w "Object" at Object.A.n above April 26, 1990 - 23 - _1_4. _A_c_k_n_o_w_l_e_d_g_e_m_e_n_t_s OFF is a derivative of an object file format used at Ohio State University. Special thanks to Allen Akin of WSE for helpful ideas and suggestions. Thanks also to Jeff Friedberg of Digital's High-Performance Workstation (HPWS) group and Shaun Ho of WSE who also contributed to the design. Danny Shapiro of WSE provided suggestions for addi- tional enhancements and conventions. April 26, 1990