The Materials and Geometry Format

Version 1.0, May 1995

Greg Ward, Lawrence Berkeley Laboratory, GJWard@Lbl.Gov

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

Application Notes

Relation to Standard Practices in Computer Graphics

Relation to IESNA LM-63 and Luminaire Catalogs

Credits