/* readmgf.c: reads a MGF format file */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "readmgf.h"
#include "scene.h"
#include "geom.h"
#include "compound.h"
#include "error.h"
#include "cie.h"
#include "pointoctree.h"
#include "vectoroctree.h"
#include "namedvertex.h"

#include "mgflib/parser.h"

static POINTOCTREE *globalPoints;
static VECTOROCTREE *globalNormals;
static NAMEDVERTEXTREE *globalVertices;

static POINTLIST *currentPointList;
static VECTORLIST *currentNormalList;
static VERTEXLIST *currentVertexList, *autoVertexList;
static PATCHLIST *currentFaceList;
static GEOMLIST	*currentGeomList;
static MATERIAL *currentMaterial;
static int twosided, ignore_sidedness;

#define MAXGEOMSTACKDEPTH	20	/* objects ('o' contexts) can be nested this deep */
static GEOMLIST *geomStack[MAXGEOMSTACKDEPTH], **geomStackPtr;
static VERTEXLIST *autoVertexListStack[MAXGEOMSTACKDEPTH], **autoVertexListStackPtr;

#define MAXFACEVERTICES	       100	/* no face can have more than this vertices */

static RGB Black = {0., 0., 0.};

static int incomplex = FALSE;
static long nrfaces=0;

void MGFSetNrQuartCircDivs(int divs)
{
	if (divs <= 0) {
		Error(NULL, "Number of quarter circle divisions (%d) should be positive", divs);
		return;
	}

	mg_nqcdivs = divs;
}

void MGFSetIgnoreSidedness(int yesno)
{
	ignore_sidedness = yesno;
}

static void do_error(char *errmsg)
{
/*	Error(NULL, "%s line %d: %s", mg_file->fname, mg_file->lineno, errmsg); */
	fprintf(stderr, "%s line %d: %s\n", mg_file->fname, mg_file->lineno, errmsg);
}

static void do_warning(char *errmsg)
{
/*	Warning(NULL, "%s line %d: %s", mg_file->fname, mg_file->lineno, errmsg); */
	fprintf(stderr, "%s line %d: %s\n", mg_file->fname, mg_file->lineno, errmsg);
}

static void NewSurface(void)
{
	currentPointList = PointListCreate();
	currentNormalList = VectorListCreate();
	currentVertexList = VertexListCreate();	
	currentFaceList = PatchListCreate();
}

static void SurfaceDone(void)
{
	GEOM *thegeom;

	if (currentFaceList) {
		thegeom = GeomCreate((void *)SurfaceCreate(currentMaterial, currentPointList, currentNormalList, currentVertexList, currentFaceList), SurfaceMethods()); 
		currentGeomList = GeomListAdd(currentGeomList, thegeom);
	}
}

static POINT *InstallPoint(float x, float y, float z)
{
	POINT thepoint, *p;

	VECTORSET(thepoint, x, y, z);
	if ((p = PointOctreeFind(globalPoints, &thepoint)) == (POINT *)NULL) {
		p = PointCreate(x, y, z);
		globalPoints = PointOctreeAdd(globalPoints, p);
		/* remember the point with the surface where it was used for the first
		   time. So we don't forget to dispose of its memory when it is no
		   longer needed */
		currentPointList = PointListAdd(currentPointList, p); 
	} 
	return p;
}

static VECTOR *InstallNormal(float x, float y, float z)
{
	VECTOR thenormal, *n;

	VECTORSET(thenormal, x, y, z);
	if ((n = VectorOctreeFind(globalNormals, &thenormal)) == (VECTOR *)NULL) {
		n = VectorCreate(x, y, z);
		globalNormals = VectorOctreeAdd(globalNormals, n);
		currentNormalList = VectorListAdd(currentNormalList, n);
	}

	return n;
}

static VERTEX *InstallVertex(POINT *point, VECTOR *normal, char *name)
{
	VERTEX *v;

	if (!normal) {		/* don't link vertices without normals */
		v = VertexCreate(point, normal, PatchListCreate(), &Black);
		currentVertexList = VertexListAdd(currentVertexList, v);
	} else if (!incomplex) {
		if ((v = NamedVertexFind(globalVertices, name, point, normal)) == (VERTEX *)NULL) {
			v = VertexCreate(point, normal, PatchListCreate(), &Black);
			globalVertices = NamedVertexAdd(globalVertices, name, v);
			currentVertexList = VertexListAdd(currentVertexList, v);
		} 
	} else {	/* automatically created vertex of sphere, torus, ... is local to 
			 * the object being defined ... and the name is not significant. */
		if (!(v = VertexListFind(autoVertexList, point, normal))) {
			v = VertexCreate(point, normal, PatchListCreate(), &Black);
			currentVertexList = VertexListAdd(currentVertexList, v);
			autoVertexList = VertexListAdd(autoVertexList, v);
		}
	}

	return v;
}

static VERTEX *GetVertex(char *name)
{
	FVECT	vert, norm;
	VECTOR *thenormal;
	POINT *thepoint;
	C_VERTEX *vp;

	if ((vp = c_getvert(name)) == NULL)
		return (VERTEX *)NULL;

	xf_xfmpoint(vert, vp->p);
	thepoint = InstallPoint(vert[0], vert[1], vert[2]);

	if (is0vect(vp->n)) {
		thenormal = (VECTOR *)NULL;
	} else {
		xf_xfmvect(norm, vp->n);
		thenormal = InstallNormal(norm[0], norm[1], norm[2]);
	}

	return InstallVertex(thepoint, thenormal, name);
}

static void NewFace(VERTEX *v1, VERTEX *v2, VERTEX *v3, VERTEX *v4, VECTOR *normal)
{
	PATCH *theface, *thebackface;

	thebackface = (PATCH *)NULL;
	if (xf_context && xf_context->rev) {
		theface = PatchCreate(v4 ? 4 : 3, v3, v2, v1, v4, twosided && !ignore_sidedness);
	} else {
		theface = PatchCreate(v4 ? 4 : 3, v1, v2, v3, v4, twosided && !ignore_sidedness);
	}

	if (theface)
		currentFaceList = PatchListAdd(currentFaceList, theface);
/*
nrfaces++;
if (nrfaces%1000 == 0)
	fprintf(stderr, "%ld faces.\n", nrfaces);
*/
}

static void GetColor(float intensity, C_COLOR color, COLOR *thecolor)
{
	if (!(color.flags & C_CSXY)) {
		do_error("This program can only handle colors converted to CIE xy chromaticities.");
		return;
	}

	CIExyToColor(color.cx, color.cy, thecolor);
	COLORSCALE(intensity, *thecolor, *thecolor);
}

static void GetCurrentMaterial(void)
{
	COLOR Ra, Rd, Rs, Ed;
	float Ns;
	MATERIAL *thematerial;
	char *matname;

	matname = c_cmname;
	if (!matname || *matname == '\0')	/* this would cause strcmp to crash !! */
		matname = "unnamed";

/* is it another material than the one used for the previous face ?? If not, the
 * currentMaterial remains the same. */
	if (strcmp(matname, currentMaterial->name) == 0 && c_cmaterial->clock == 0)
		return;

	twosided = (c_cmaterial->sided == 0);
	if ((thematerial = MaterialLookup(MaterialLib, matname))) {
		if (c_cmaterial->clock == 0) {
#ifdef DEBUG
fprintf(stderr, "Previously used material '%s' -- unchanged\n", matname);
#endif
			currentMaterial = thematerial;
			return;
		} else {
#ifdef DEBUG
fprintf(stderr, "Previously used material '%s' -- changed\n", matname);
#endif
               }
	} else {
#ifdef DEBUG
fprintf(stderr, "New material '%s'\n", matname);
#endif
        }

/* new material, or a material that changed */
	COLORCLEAR(Ra);
	GetColor(c_cmaterial->rd, c_cmaterial->rd_c, &Rd);
	GetColor(c_cmaterial->rs, c_cmaterial->rs_c, &Rs);
	GetColor(c_cmaterial->ed, c_cmaterial->ed_c, &Ed);

/* specular power = (0.6/roughness)^2 */
	if (c_cmaterial->rs_a != 0.0) {
		Ns = 0.6/c_cmaterial->rs_a;    
		Ns *= Ns;
	} else
		Ns = 0.0;
	
	thematerial = MaterialCreate(matname, &Ra, &Rd, &Rs, &Ed, Ns);
	MaterialLib = MaterialListAdd(MaterialLib, thematerial); 
	currentMaterial = thematerial;

/* reset the clock value so we will be aware of changes in future */
	c_cmaterial->clock = 0;
}

/* computes the normal assuming that the polygon is convex. If it is not convex,
 * the normal may be opposite of what it should be. */
static VECTOR *FaceNormal(int nrvertices, VERTEX **v, VECTOR *normal)
{
	float norm, x;
	POINT *prev, *cur, *next;
	VECTOR n;
	int i;

	norm = 0.; 
	cur = v[nrvertices -1]->point;
	next = v[0]->point;
	for (i=0; i<nrvertices; i++) {
		prev = cur;
		cur = next;
		next = v[(i+1)%nrvertices]->point;
		VECTORCROSSPRODDIFF(*prev, *cur, *next, n);
		x = VECTORNORM(n);
		if (x > norm) {
			norm = x;
			*normal = n;
		}
	}

	if (norm < EPSILON) {
		/*
		 * Degenerate normal --> degenerate polygon
		 */
		return (VECTOR *)NULL;
	}
	VECTORSCALEINVERSE(norm, *normal, *normal);

	return normal;	
}

/* Tests whether the polygon is convex or concave. This is accomplished by projecting
 * onto the coordinate plane "most parallel" to the polygon and checking the signs
 * the the cross produtc of succeeding edges (the signs should be all equal) */
static int FaceIsConvex(int nrvertices, VERTEX **v, VECTOR *normal)
{
	VEC2D v2d[MAXFACEVERTICES+1], p, c;
	int i, index, sign;

	index = VectorDominantCoord(normal);
	for (i=0; i<nrvertices; i++)
		VECTORPROJECT(v2d[i], *(v[i]->point), index);

	p.u = v2d[3].u - v2d[2].u;
	p.v = v2d[3].v - v2d[2].v;
	c.u = v2d[0].u - v2d[3].u;
	c.v = v2d[0].v - v2d[3].v;
	sign = (p.u * c.v > c.u * p.v) ? 1 : -1;

	for (i=1; i<nrvertices; i++) {
		p.u = c.u;
		p.v = c.v;
		c.u = v2d[i].u - v2d[i-1].u;
		c.v = v2d[i].v - v2d[i-1].v;
		if (((p.u * c.v > c.u * p.v) ? 1 : -1) != sign)
			return FALSE;
	}

	return TRUE;
}

/* computes the distance between the two points, ie the length of the difference
 * vector. */
float Distance(POINT *v1, POINT *v2)
{
	VECTOR d;

	VECTORSUBSTRACT(*v1, *v2, d);
	return VECTORNORM(d);
}

/* returns TRUE if the 2D point p is inside the 2D triangle p1-p2-p3. */
static int PointInsideTriangle2D(VEC2D *p, VEC2D *p1, VEC2D *p2, VEC2D *p3)
{
	double u0, v0, u1, v1, u2, v2, a, b;

/* from Graphics Gems I, Didier Badouel, An Efficient Ray-Polygon Intersection, p390 */
	u0 = p->u - p1->u;
	v0 = p->v - p1->v;
	u1 = p2->u - p1->u;
	v1 = p2->v - p1->v;
	u2 = p3->u - p1->u;
	v2 = p3->v - p1->v;

	a=10.; b=10.;	/* values large enough so the result would be FALSE */	
	if (fabs(u1) < EPSILON) {
		if (fabs(u2)>EPSILON && fabs(v1)>EPSILON) {
			b = u0/u2;
			if (b<EPSILON || b>1.-EPSILON)
				return FALSE;
			else
				a = (v0 - b*v2)/v1;
		}
	} else {
		b = v2*u1 - u2*v1;
		if (fabs(b)>EPSILON) {
			b = (v0*u1 - u0*v1) / b;
			if (b<EPSILON || b>1.-EPSILON)
				return FALSE;
			else
				a = (u0 - b*u2)/u1;
		}
	}

	return (a>=EPSILON && a<=1.-EPSILON && (a+b)<=1.-EPSILON);	
}

/* returns TRUE if the 2D segments p1-p2 and p3-p4 intersect */
static int SegmentsIntersect2D(VEC2D *p1, VEC2D *p2, VEC2D *p3, VEC2D *p4)
{
	double a, b, c, du, dv, r1, r2, r3, r4;
	int colinear = FALSE;

/* from Graphics Gems II, Mukesh Prasad, Intersection of Line Segments, p7 */
	du = fabs(p2->u - p1->u);
	dv = fabs(p2->v - p1->v);
	if (du > EPSILON || dv > EPSILON) {
		if (dv > du) {
			a = 1.0;
			b = - (p2->u - p1->u) / (p2->v - p1->v);
			c = - (p1->u + b * p1->v);
		} else {
			a = - (p2->v - p1->v) / (p2->u - p1->u);
			b = 1.0;
			c = - (a * p1->u + p1->v);
		}

		r3 = a * p3->u + b * p3->v + c;
		r4 = a * p4->u + b * p4->v + c;

		if (fabs(r3) < EPSILON && fabs(r4) < EPSILON)
			colinear = TRUE;
		else if ((r3 > -EPSILON && r4 > -EPSILON) || (r3 < EPSILON && r4 < EPSILON))
			return FALSE;
	}

	if (!colinear) {
		du = fabs(p4->u - p3->u);
		dv = fabs(p4->v - p3->v);
		if (du > EPSILON || dv > EPSILON) {
			if (dv > du) {
				a = 1.0;
				b = - (p4->u - p3->u) / (p4->v - p3->v);
				c = - (p3->u + b * p3->v);
			} else {
				a = - (p4->v - p3->v) / (p4->u - p3->u);
				b = 1.0;
				c = - (a * p3->u + p3->v);
			}

			r1 = a * p1->u + b * p1->v + c;
			r2 = a * p2->u + b * p2->v + c;

			if (fabs(r1) < EPSILON && fabs(r2) < EPSILON)
				colinear = TRUE;
			else if ((r1 > -EPSILON && r2 > -EPSILON) || (r1 < EPSILON && r2 < EPSILON))
				return FALSE;		
		}
	}

	if (!colinear)
		return TRUE;

	return FALSE;	/* colinear segments never intersect: do as if they are always
			 * a little bit apart from each other */
#ifdef NEVER
	if (dv > du) {
		min1 = (p1->v < p2->v) ? p1->v : p2->v;
		max1 = (p1->v > p2->v) ? p1->v : p2->v;
		min2 = (p3->v < p4->v) ? p3->v : p4->v;
		max2 = (p3->v > p4->v) ? p3->v : p4->v;
	} else {
		min1 = (p1->u < p2->u) ? p1->u : p2->u;
		max1 = (p1->u > p2->u) ? p1->u : p2->u;
		min2 = (p3->u < p4->u) ? p3->u : p4->u;
		max2 = (p3->u > p4->u) ? p3->u : p4->u;
	}

	return !(min2 >= max1-EPSILON || min1 >= max2-EPSILON);
#endif /*NEVER*/
}

/* handles concave faces and faces with >4 vertices. This routine started as an
 * ANSI-C version of mgflib/face2tri.C, but I changed it a lot to be more robust. 
 * Inspiration comes from Burger and Gillis, Interactive Computer Graphics and
 * the (really helpful) Graphics Gems books. */
static int do_complex_face(int n, VERTEX **v, VECTOR *normal)
{
	int i, j, max, p0, p1, p2, corners, start, good, index;
	float maxd, d, a;
	POINT center;
	char out[MAXFACEVERTICES+1];
	VEC2D q[MAXFACEVERTICES+1];
	VECTOR nn;

	VECTORSET(center, 0., 0., 0.);
	for (i=0; i<n; i++) 
		VECTORSUM(center, *(v[i]->point), center);
	VECTORSCALEINVERSE((float)n, center, center);

	maxd = Distance(&center, v[0]->point);
	max = 0;
	for (i=1; i<n; i++) {
		if ((d = Distance(&center, v[i]->point)) > maxd) {
			maxd = d;
			max = i;
		}
	}

	for (i=0; i<n; i++)
		out[i] = FALSE;

	p1 = max;
	p0 = p1 - 1;
	if (p0 < 0)
		p0 = n-1;
	p2 = (p1+1) % n;
	VECTORCROSSPRODDIFF(*(v[p0]->point), *(v[p1]->point), *(v[p2]->point), *normal);
	VECTORNORMALIZE(*normal);
	index = VectorDominantCoord(normal);

	for (i=0; i<n; i++)
		VECTORPROJECT(q[i], *(v[i]->point), index);

	corners = n;
	p0 = -1;
	a = 0.;	/* to make gcc -Wall not complain */
	
	while (corners >= 3) {
		start = p0;

		do {
			p0 = (p0 + 1) % n;
			while (out[p0])
				p0 = (p0 + 1) % n;
				
			p1 = (p0 + 1) % n;
			while (out[p1])
				p1 = (p1 + 1) % n;
				
			p2 = (p1 + 1) % n;
			while (out[p2])
				p2 = (p2 + 1) % n;

			if (p0 == start)
				break;

			VECTORCROSSPRODDIFF(*(v[p0]->point), *(v[p1]->point), *(v[p2]->point), nn);
			a = VECTORNORM(nn);
			VECTORSCALEINVERSE(a, nn, nn);
			d = Distance((POINT *)&nn, (POINT *)normal);
			
			good = TRUE;
			if (d <= 1.0) {
				for (i=0; i<n && good; i++) {
					if (out[i] || v[i]==v[p0] || v[i]==v[p1] || v[i]==v[p2]) 
						continue;

					if (PointInsideTriangle2D(&q[i], &q[p0], &q[p1], &q[p2])) 
						good = FALSE;

					j = (i+1) % n;
					if (out[j] || v[j]==v[p0]) 
						continue;

					if (SegmentsIntersect2D(&q[p2], &q[p0], &q[i], &q[j])) 
						good = FALSE;
				}
			}
		} while (d > 1.0 || !good);
		
		if (p0 == start) {
			do_error("misbuilt polygonal face");
			return MG_EILL;
		}

		if (fabs(a) > EPSILON) 	/* avoid degenerate faces */
			NewFace(v[p0], v[p1], v[p2], (VERTEX *)NULL, normal);

		out[p1] = TRUE;
		corners--;
	}

	return MG_OK;
}

static int do_face(int argc, char **argv)
{
	VERTEX *v[MAXFACEVERTICES+1];
	VECTOR normal;
	int i, errcode;

	if (argc < 4) 
		return MG_EARGC;

	if (!incomplex) {
		GetCurrentMaterial();
		NewSurface();
	}

	if (argc-1 > MAXFACEVERTICES) {
		do_warning("too many vertices (change MAXFACEVERTICES in mgf.c)");
		return MG_OK;	/* no reason to stop parsing the input */
	}

	for (i=0; i<argc-1; i++) {
		if ((v[i] = GetVertex(argv[i+1])) == (VERTEX *)NULL)
			return MG_EUNDEF;
	}

	if (!FaceNormal(argc-1, v, &normal)) {
		do_warning("degenerate face");
		return MG_OK;
	}

	errcode = MG_OK;
	if (argc == 4) {		/* triangles */
		NewFace(v[0], v[1], v[2], (VERTEX *)NULL, &normal);
	} else if (argc == 5) {		/* quadrilaterals */
		if (incomplex || FaceIsConvex(argc-1, v, &normal)) 
			NewFace(v[0], v[1], v[2], v[3], &normal);
		else 
			errcode = do_complex_face(argc-1, v, &normal);
	} else  			/* more than 4 vertices */
		errcode = do_complex_face(argc-1, v, &normal);

	if (!incomplex && errcode==MG_OK) 
		SurfaceDone();

	return errcode;
}

/* the mgf parser already contains some good routines for discretizing spheres, ...
 * into polygons. In the official release of the parser library, these routines
 * are internal (declared static in parse.c and no reference to them in parser.h).
 * The parser was changed so we can call them in order not to have to duplicate
 * the code. */
static int do_discretize(int argc, char **argv)
{
	int en = mg_entity(argv[0]);

	switch (en) {
	case MG_E_SPH: 		return e_sph(argc, argv);
	case MG_E_TORUS: 	return e_torus(argc, argv);
	case MG_E_CYL: 		return e_cyl(argc, argv);
	case MG_E_RING: 	return e_ring(argc, argv);
	case MG_E_CONE: 	return e_cone(argc, argv);
	case MG_E_PRISM: 	return e_prism(argc, argv);
	default:
		Fatal(4, "mgf.c: do_discretize", "Unknown geometry entity number %d", en);
	}

	return MG_EILL;	/* definitely illegal when this point is reached */
}

static int do_surface(int argc, char **argv)
{
	int errcode;

	if (incomplex) /* e_sph calls e_cone ... */
		return do_discretize(argc, argv);

	else {
		incomplex = TRUE;
		NewSurface();
		GetCurrentMaterial();

		errcode = do_discretize(argc, argv);

		SurfaceDone();
		incomplex = FALSE;

		return errcode;
	}
}

static void PushCurrentGeomList(void)
{
	if (geomStackPtr - geomStack >= MAXGEOMSTACKDEPTH) {
		do_error("Objects are nested too deep for this program. Change MAXGEOMSTACKDEPTH in readmgf.c.");
		return;
	} else {
		*geomStackPtr = currentGeomList;
		geomStackPtr ++;
		currentGeomList = GeomListCreate();

		*autoVertexListStackPtr = autoVertexList;
		autoVertexListStackPtr ++;
		autoVertexList = VertexListCreate();
	}
}

static void PopCurrentGeomList(void)
{
	if (geomStackPtr < geomStack) {
		do_error("Object stack underflow ... unbalanced 'o' contexts ?");
		return;
	} else {
		geomStackPtr --;
		currentGeomList = *geomStackPtr;

		VertexListDestroy(autoVertexList);
		autoVertexListStackPtr --;
		autoVertexList = *autoVertexListStackPtr;
	}
}

static int do_object(int argc, char **argv)
{
	int i;

	if (argc > 1) { 	/* beginning of a new object */
		for (i=0; i<geomStackPtr - geomStack; i++)
			fprintf(stderr, "\t");
		fprintf(stderr, "%s ...\n", argv[1]);

		PushCurrentGeomList();
	} else {
		GEOM *thegeom = (GEOM *)NULL;
		if (GeomListCount(currentGeomList) > 0) 
			thegeom = GeomCreate((void *)CompoundCreate(currentGeomList), CompoundMethods());
		
		PopCurrentGeomList();

		if (thegeom)
			currentGeomList = GeomListAdd(currentGeomList, thegeom); 
	}

	return obj_handler(argc, argv);      
}

static void InitMGF(void)
{
	mg_ehand[MG_E_FACE] = do_face;

	mg_ehand[MG_E_VERTEX] = c_hvertex;
	mg_ehand[MG_E_POINT] = c_hvertex;
	mg_ehand[MG_E_NORMAL] = c_hvertex;

	mg_ehand[MG_E_COLOR] = c_hcolor;
	mg_ehand[MG_E_CXY] = c_hcolor;
	mg_ehand[MG_E_CMIX] = c_hcolor;
/* we don't use spectra.... let the mgf parser library convert to tristimulus
 * values itself 
	mg_ehand[MG_E_CSPEC] = c_hcolor;
	mg_ehand[MG_E_CCT] = c_hcolor;
*/

	mg_ehand[MG_E_MATERIAL] = c_hmaterial;
	mg_ehand[MG_E_ED] = c_hmaterial;
	mg_ehand[MG_E_IR] = c_hmaterial;
	mg_ehand[MG_E_RD] = c_hmaterial;
	mg_ehand[MG_E_RS] = c_hmaterial;
	mg_ehand[MG_E_SIDES] = c_hmaterial;
	mg_ehand[MG_E_TD] = c_hmaterial;
	mg_ehand[MG_E_TS] = c_hmaterial;

	mg_ehand[MG_E_OBJECT] = do_object;	

	mg_ehand[MG_E_XF] = xf_handler;

	mg_ehand[MG_E_SPH] = do_surface;
	mg_ehand[MG_E_TORUS] = do_surface;
	mg_ehand[MG_E_RING] = do_surface;
	mg_ehand[MG_E_CYL] = do_surface;
	mg_ehand[MG_E_CONE] = do_surface;
	mg_ehand[MG_E_PRISM] = do_surface;

	mg_init();
}

void ReadMGF(char *filename)
{
	MG_FCTXT fctxt;
	int err;

	InitMGF();

	globalPoints = PointOctreeCreate();
	globalNormals = VectorOctreeCreate();
	globalVertices = NamedVertexTreeCreate();	
	currentGeomList = GeomListCreate();
	currentMaterial = &defaultMaterial;  

	geomStackPtr = geomStack;
	autoVertexListStackPtr = autoVertexListStack;
	autoVertexList = VertexListCreate();

	incomplex = FALSE;
	nrfaces = 0;
	
	err = mg_open(&fctxt, filename);
	if (err) 
		do_error(mg_err[err]);
	else {
		while (mg_read() > 0 && !err) {
			err = mg_parse();
			if (err)
				do_error(mg_err[err]);
		}
		mg_close();
	}
	mg_clear();
	World = currentGeomList;

	VertexListDestroy(autoVertexList);
	PointOctreeDestroy(globalPoints);
	VectorOctreeDestroy(globalNormals);
	NamedVertexTreeDestroy(globalVertices);
}

