mgfparse.html
The Materials and Geometry Format
Version 1.0, May 1995
Greg Ward, Lawrence Berkeley Laboratory, [email protected]Introduction
What makes MGF special?
What does MGF look like?
MGF's place in the world of standards
MGF Basics
Entities and Contexts
Hierarchical Contexts and Transformations
Detailed MGF Example
MGF Entity Reference
MGF Translators
MGF Parser Library
The principal motivation for creating a standard parser library for MGF is to make it easy for software developers to offer some base level of compliance. The key to making MGF easy to support in fact is the parser, which has the ability to express higher order entities in terms of lower order ones. For example, tori are part of the MGF specification, but if a given program or translator does not support them, the parser will convert them to cones. If cones are not supported either, it will convert them further into smoothed polygons. If smoothing (vertex normal information) is not supported, it will be ignored and the program will just get flat polygons. This is done in such a way that future versions of the standard may include new entities that old software does not even have to know about, and they will be converted appropriately. Forward compatibility is thus built right into the parser loading mechanism itself -- the programmer simply links to the new code and the new standard is supported without any further changes.
Language
The provided MGF parser is written in ANSI-C. This language was chosen for reasons of portability and efficiency. Almost all systems support some form of ANSI-compatible C, and many languages can cross-link to C libraries without modification. Backward compatibility to Kernighan and Ritchie C is achieved by compiling with the -DNOPROTO flag.All of the data structures and prototypes needed for the library are in the header file "parser.h". This file is the best resource for the parser and is updated with each MGF release.
Mechanism
The parser works by a simple callback mechanism to routines that actually interpret the individual entities. Some of these routines will belong to the calling program, and some will be entity support routines included in the library itself. There is a global array of function pointers, called mg_ehand. It is defined thus:
extern int (*mg_ehand[MG_NENTITIES])(int argc, char **argv);Before parsing begins, this dispatch table is initialized to point to the routines that will handle each supported entity. Every entity handler has the same basic prototype, which is the same as the main function, i.e:
extern int handler(int argc, char **argv);The first argument is the number of words in the MGF entity (counting the entity itself) and the second argument is an array of nul-terminated strings with the entity and its arguments. The function should return zero or one of the error codes defined in "parser.h". A non-zero return value causes the parser to abort, returning the error up through its call stack to the entry function, usually mg_load.
A special function pointer for undefined entities is defined as follows:
extern int (*mg_uhand)(int argc, char **argv);By default, this points to the library function mg_defuhand, which prints an error message on the first unknown entity and keeps a count from then on, which is stored in the global unsigned integer mg_nunknown. If the mg_uhand pointer is assigned a value of NULL instead, parsing will abort at the first unrecognized entity. The reason this is not the default action is that ignoring unknown entities offers a certain base level of forward compatibility. Ignoring things one does not understand is not the best approach, but it is usually better than quitting with an error message if the input is in fact valid, but is a later version of the standard. The real solution is to update the interpreter by linking to a new version of the parser, or use a new version of the mgfilt command to convert the new MGF input to an older standard.
The mg_uhand pointer may also be used to customize the language for a particular application by adding entities, though this is discouraged because it tends to weaken the standard.
The skeletal framework for an MGF loader or translator is to assign function pointers to the mg_ehand array, call the parser initialization function mg_init, then call the file loader function mg_load once for each input file. This will in turn make calls back to the functions assigned to mg_ehand. To give a simple example, let us look at a translator that understands only flat polygonal faces, putting out vertex locations immediately after each "face" keyword:
#include < stdio.h > #include "parser.h" int myfaceh(ac, av) /* face handling routine */ int ac; char **av; { C_VERTEX *vp; /* vertex structure pointer */ FVECT vert; /* vertex point location */ int i; if (ac < 4) /* check # arguments */ return(MG_EARGC); printf("face\n"); /* begin face output */ for (i = 1; i < ac; i++) { if ((vp = c_getvert(av[i])) == NULL) /* vertex from name */ return(MG_EUNDEF); xf_xfmpoint(vert, vp -> p); /* apply transform */ printf("%15.9f %15.9f %15.9f\n", vert[0], vert[1], vert[2]); /* output vertex */ } printf(";\n"); /* end of face output */ return(MG_OK); /* normal exit */ } main(argc, argv) /* translate MGF file(s) */ int argc; char **argv; { int i; /* initialize dispatch table */ mg_ehand[MG_E_FACE] = myfaceh; /* ours */ mg_ehand[MG_E_VERTEX] = c_hvertex; /* parser lib */ mg_ehand[MG_E_POINT] = c_hvertex; /* parser lib */ mg_ehand[MG_E_XF] = xf_handler; /* parser lib */ mg_init(); /* initialize parser */ for (i = 1; i < argc; i++) /* load each file argument */ if (mg_load(argv[i]) != MG_OK) /* and check for error */ exit(1); exit(0); /* all done! */ }Hopefully, this example demonstrates just how easy it is to write an MGF translator. Of course, translators get more complicated the more entity types they support, but the point is that one does not have to support every entity -- the parser handles what the translator does not. Also, the library includes many general entity handlers, further reducing the burden on the programmer. This same principle means that it is not necessary to modify an existing program to accommodate a new version of MGF -- one need only link to the new parser library to comply with the new standard.
Division of Labor
As seen in the previous example, there are two parser routines that are normally called directly in an MGF translator or loader program. The first is mg_init, which takes no arguments but relies on the program having initialized those parts of the global mg_ehand array it cares about. The second routine is mg_load, which is called once on each input file. (A third routine, mg_clear, may be called to free the parser data structures after each file or after all files, if the program plans to continue rather than exit.)The rest of the routines in a translator or loader program are called indirectly through the mg_ehand dispatch table, and they are the ones that do the real work of supporting the MGF entities. In addition to converting or discarding entities that the calling program does not know or care about, the parser library includes a set of context handlers that greatly simplify the translation process. There are three handlers for each of the three named contexts and their constituents, and two handlers for the two hierarchical context entities. To use these handlers, one simply sets the appropriate positions in the mg_ehand dispatch table to point to these functions. Additional functions and global data structures provide convenient access to the relevant contexts, and all of these are detailed in the following manual pages.
Library Routines
- mg_init, mg_ehand, mg_uhand
- Initialize MGF entity handlers
- mg_load, mg_clear, mg_file, mg_err
- Load MGF file, clear data structures
- mg_open, mg_read, mg_parse, mg_close
- MGF file loading subroutines
- mg_fgetpos, mg_fgoto
- Get current file position and seek to pointer
- mg_handle, mg_entity, mg_ename, mg_nqcdivs
- Entity assistance and control
- isint, isflt, isname
- Determine if string fits integer or real format, or is legal identifier
- c_hvertex, c_getvert, c_cvname, c_cvertex
- Vertex entity support
- c_hcolor, c_getcolor, c_ccname, c_ccolor, c_ccvt, c_isgrey
- Color entity support
- c_hmaterial, c_getmaterial, c_cmname, c_cmaterial
- Material entity support
- obj_handler, obj_clear, obj_nnames, obj_name
- Object name support
- xf_handler, xf_clear, xf_context, xf_argend
- Transformation support
- xf_xfmpoint, xf_xfmvect, xf_rotvect, xf_scale
- Apply current transformation