[BACK]Return to pfdBuildASD.c CVS log [TXT][DIR] Up to [Development] / performer / src / lib / libpfdu

File: [Development] / performer / src / lib / libpfdu / pfdBuildASD.c (download)

Revision 1.1, Tue Nov 21 21:39:35 2000 UTC (16 years, 10 months ago) by flynnt
Branch: MAIN
CVS Tags: HEAD

Initial check-in based on OpenGL Performer 2.4 tree.
-flynnt

/*
 * Copyright 2000, Silicon Graphics, Inc.
 * ALL RIGHTS RESERVED
 *
 * This source code ("Source Code") was originally derived from a
 * code base owned by Silicon Graphics, Inc. ("SGI")
 * 
 * LICENSE: SGI grants the user ("Licensee") permission to reproduce,
 * distribute, and create derivative works from this Source Code,
 * provided that: (1) the user reproduces this entire notice within
 * both source and binary format redistributions and any accompanying
 * materials such as documentation in printed or electronic format;
 * (2) the Source Code is not to be used, or ported or modified for
 * use, except in conjunction with OpenGL Performer; and (3) the
 * names of Silicon Graphics, Inc.  and SGI may not be used in any
 * advertising or publicity relating to the Source Code without the
 * prior written permission of SGI.  No further license or permission
 * may be inferred or deemed or construed to exist with regard to the
 * Source Code or the code base of which it forms a part. All rights
 * not expressly granted are reserved.
 * 
 * This Source Code is provided to Licensee AS IS, without any
 * warranty of any kind, either express, implied, or statutory,
 * including, but not limited to, any warranty that the Source Code
 * will conform to specifications, any implied warranties of
 * merchantability, fitness for a particular purpose, and freedom
 * from infringement, and any warranty that the documentation will
 * conform to the program, or any warranty that the Source Code will
 * be error free.
 * 
 * IN NO EVENT WILL SGI BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT
 * LIMITED TO DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES,
 * ARISING OUT OF, RESULTING FROM, OR IN ANY WAY CONNECTED WITH THE
 * SOURCE CODE, WHETHER OR NOT BASED UPON WARRANTY, CONTRACT, TORT OR
 * OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM,
 * OR AROSE OUT OF USE OR RESULTS FROM USE OF, OR LACK OF ABILITY TO
 * USE, THE SOURCE CODE.
 * 
 * Contact information:  Silicon Graphics, Inc., 
 * 1600 Amphitheatre Pkwy, Mountain View, CA  94043, 
 * or:  http://www.sgi.com
 */


#include <Performer/pf.h>
#include <Performer/pr.h>
#include <Performer/pfdu.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <sys/resource.h>

#define MAX_LODS 100
#define SMALL_EPS 0.00000001

#define MIN_TRIS        120
#define MAX_TRIS 	8000

#define BUILD_PAGING 		0
#define PAGING 			0

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
#define OTHERFACE(face,edge) (((edge->f1)==(face))?(edge->f2):(edge->f1))

#define NORMAL 0

typedef struct meshEdge {
    int level;
    unsigned int v1, v2;  /* v1 is expected to be the "newest" vertex */
    struct meshEdge *ch1, *ch2;
    struct meshFace *f1, *f2;    /* Adjacency for same LOD only! */
    struct meshEdge *next;
} meshEdge;

typedef struct  meshFace {
    unsigned int id;
    struct meshEdge *ex, *ey, *eh;  /* horizontal, vertical, hypotenuse */
    struct meshFace *ch1, *ch2, *ch3, *ch4;
    struct meshFace *next;
} meshFace;

static int lodLevel;
static meshFace *meshFaces[MAX_LODS];
static meshEdge *meshEdges[MAX_LODS];
static pfVec3 *srcData;
static pfVec3 *Vd;
void pfdBreakTiles(int numlods, pfASDLODRange *lods, int numverts, pfASDVert *verts, int numfaces, pfASDFace *faces, int numfaces0, char *prename, char *conf);

void pfdPagingLookahead(char *confname, char *pagename, int *lookahead);

void pfdWriteFile(int ***FACE, int **numf, int ***V, int **numv, pfASDFace *faces, pfASDVert *verts, pfBox *facebounds, int lod, int x, int y, char *prename);

pfBox * pfdGetTerrainBounds(pfASDFace *faces, pfASDVert *verts, int numfaces0, int numfaces);

pfASD * pfdLoadConfig(char *fname, char *pagename);

void computeBBox(pfBox *facebounds, pfASDFace *faces, pfASDVert *verts, int _faceid);

static int *dvmap;
static int dvcnt;
static int *vmap;
static int vcnt;
pfASDVert *verts;
pfVec3 *dverts;


int 
finalmap(int vid)
{
    if(vmap[vid] == -1)
	vmap[vid] = vcnt++;
    return(vmap[vid]);
}

int 
map(int vid)
{
    if(dvmap[vid] == -1)
	dvmap[vid] = dvcnt++;
    return(dvmap[vid]);
}

/* this is a rough estimate. 
 * the total number of edges in LODi starting with 1 triangle in LOD0 is
 * Edge[i] = 1.5*(2^i + 4^i) with Edge[0] = 3.
 * Each edge corresponds to one vertex.
 * the total number of vertices in LODi is
 * V[i] = 3*2^i + 2*4^i + 1;
 * We will roughly count the total number of verts starting with
 * numfaces0 triangles in LOD0 as
 * V[i] = numface0*(3*2^i + 2*4^i + 1).
 */

int
maxverts(int lods, int numfaces0, int numverts)
{
    int v;

    if(lods == -1)
	return numverts;
    lods = lods - 2;
    v = 3*(1<<lods) + 2*(1<<(2*lods)) + 1;
    return (numfaces0*v);
}

int
mylog(int n)
{
    int i, j;
    
    i = 0;
    j = 1;
    while(j < n)
    {
	j *= 2;
	i++;
    }
    return ++i;
}

int
maxlod(int lods, int numx, int numy)
{
    if(lods != -1)
	return(lods);
    if(mylog(numx) > mylog(numy))
	return(mylog(numy)+1);
    return(mylog(numx)+1);
}

static ComputeNormal(pfVec3 norm, pfVec3 v, pfVec3 v1, pfVec3 v2,
                     pfVec3 v3, pfVec3 v4)
{
    pfVec3 t1, t2, s1, s2;

    PFSUB_VEC3(t1, v1, v);
    PFSUB_VEC3(t2, v2, v);
    pfCrossVec3(s1, t1, t2);
    pfNormalizeVec3(s1);

    PFSUB_VEC3(t1, v2, v);
    PFSUB_VEC3(t2, v3, v);
    pfCrossVec3(s2, t1, t2);
    pfNormalizeVec3(s2);

    if (v4)
    {
        PFADD_VEC3(s1, s1, s2);

        PFSUB_VEC3(t1, v3, v);
        PFSUB_VEC3(t2, v4, v);
        pfCrossVec3(s2, t1, t2);
        pfNormalizeVec3(s2);

        PFADD_VEC3(s1, s1, s2);

        PFSUB_VEC3(t1, v4, v);
        PFSUB_VEC3(t2, v1, v);
        pfCrossVec3(s2, t1, t2);
        pfNormalizeVec3(s2);
    }

    PFADD_VEC3(norm, s1, s2);
    pfNormalizeVec3(norm);
}


static meshEdge *addEdge(unsigned int v1, unsigned int v2)
{
    meshEdge *newEdge;

    newEdge = (meshEdge *) pfMalloc(sizeof(meshEdge), pfGetSharedArena());
    if(newEdge == NULL)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT, 
	"can't alloc addEdge v1 %d, v2 %d\n", v1, v2);
	exit(0);
    }
    newEdge->level = lodLevel;
    newEdge->v1 = v1;
    newEdge->v2 = v2;
    newEdge->ch1 = newEdge->ch2 = NULL;
    newEdge->f1 = newEdge->f2 = NULL;
    newEdge->next = meshEdges[lodLevel];
    meshEdges[lodLevel] = newEdge;
    return newEdge;
}


static void addFace(meshEdge *ex, meshEdge *ey, meshEdge *eh, meshFace *parent)
{
    meshFace *newFace;

    newFace = (meshFace *) pfMalloc(sizeof(meshFace), pfGetSharedArena());
    if(newFace == NULL)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT, 
		"can't alloc addFace\n");
	exit(0);
    }
    newFace->ex = ex;
    newFace->ey = ey;
    newFace->eh = eh;
    newFace->ch1 = newFace->ch2 = newFace->ch3 = newFace->ch4 = NULL;

    if (parent)
    {
        if (!parent->ch1)
            parent->ch1 = newFace;
        else if (!parent->ch2)
            parent->ch2 = newFace;
        else if (!parent->ch3)
            parent->ch3 = newFace;
        else if (!parent->ch4)
            parent->ch4 = newFace;
    }

    if (ex->level == lodLevel)
	if (ex->f1)
	    ex->f2 = newFace;
        else
	    ex->f1 = newFace;

    if (ey->level == lodLevel)
	if (ey->f1)
	    ey->f2 = newFace;
        else
	    ey->f1 = newFace;

    if (eh->level == lodLevel)
	if (eh->f1)
	    eh->f2 = newFace;
        else
	    eh->f1 = newFace;

    newFace->next = meshFaces[lodLevel];
    meshFaces[lodLevel] = newFace;
}


static void SplitVertex(unsigned int refvertv, unsigned int v1, unsigned int v2)
{
    pfVec3 vect, diff, newpos;

    PFSUB_VEC3(vect, srcData[v2], srcData[v1]);
    PFSUB_VEC3(diff, srcData[refvertv], srcData[v1]);
    PFADD_SCALED_VEC3(newpos, srcData[v1], PFDOT_VEC3(vect, diff) /
                      PFDOT_VEC3(vect, vect), vect);

    PFSUB_VEC3(dverts[map(refvertv)], newpos, srcData[refvertv]);
}

static void     prune_faces(pfASDVert *v, pfASDFace *faces, int numfaces0);
static void     prune_subtree(pfASDVert *v, pfASDFace *faces, int faceId);
static int 	traverse_subtree(
				pfASDVert 	*v, 
				pfASDFace 	*faces,
				int 		faceId, 
				float 		a, 
				float 		b, 
				float 		c, 
				float 		d, 
				float 		eps);


/*=========================================================================*/
static void     prune_faces(pfASDVert *v, pfASDFace *faces, int numfaces0)
/*=========================================================================*/
{
    int	i;

    for (i = 0 ; i < numfaces0 ; i ++ )
	prune_subtree (v, faces, i);
}

/*=========================================================================*/
static void     prune_subtree(pfASDVert *v, pfASDFace *faces, int faceId)
/*=========================================================================*/
{
    pfASDFace		*face;
    pfVec3		v0, v1, v2;
    pfVec3		d0, d1, n;
    float		a, b, c, d;
    int			i;
    float		area;
    int			cnt, children;

    face = &faces[faceId];

    pfCopyVec3 (v0, v[face -> vert[0]].v0);
    pfCopyVec3 (v1, v[face -> vert[1]].v0);
    pfCopyVec3 (v2, v[face -> vert[2]].v0);

    pfSubVec3 (d0, v1, v0);
    pfSubVec3 (d1, v2, v0);
    pfCrossVec3 (n, d0, d1);

    area = pfNormalizeVec3 (n);

    /* 
     *	Face plane equation. 
     */
    a = n[0];
    b = n[1];
    c = n[2];
    d = - (a * v0[0] + b * v0[1] + c * v0[2]);

    traverse_subtree (v, faces, faceId, a, b, c, d, sqrt(area) * 0.001);
}

/*=========================================================================*/
static int traverse_subtree (pfASDVert *v, pfASDFace *faces, int faceId, 
			float _a, float _b, float _c, float _d, float _eps)
/*=========================================================================*/
{
    pfASDFace		*face;
    float		x, y, z;
    int			result;
    int			is_children;
    pfVec3		v0, v1, v2;
    pfVec3		d0, d1, n;
    float		a, b, c, d;
    int			i;
    float		area;

    face = &faces[faceId];
    is_children = 0;

    /*
     *	=============================================
     *	Test parent face against this one. 
     *	=============================================
     */
    result = 1;
    for (i = 0 ; i < 3 ; i ++)
    {
	if (face -> refvert[i] != PFASD_NIL_ID)
	{
	    x = v[face -> refvert[i]].v0[0];
	    y = v[face -> refvert[i]].v0[1];
	    z = v[face -> refvert[i]].v0[2];

	    d = x * _a + y * _b + z * _c + _d;
	    if (d > _eps)
		result = 0;
	}
    }

    /*
     *	=============================================
     *	Prepare for children
     *	=============================================
     */
    pfCopyVec3 (v0, v[face -> vert[0]].v0);
    pfCopyVec3 (v1, v[face -> vert[1]].v0);
    pfCopyVec3 (v2, v[face -> vert[2]].v0);

    pfSubVec3 (d0, v1, v0);
    pfSubVec3 (d1, v2, v0);
    pfCrossVec3 (n, d0, d1);

    area = pfNormalizeVec3 (n);

    /* 
     *	Face plane equation. 
     */
    a = n[0];
    b = n[1];
    c = n[2];
    d = - (a * v0[0] + b * v0[1] + c * v0[2]);

    for (i = 0 ; i < 4 ; i ++)
    {
	if (face -> child[i] != PFASD_NIL_ID)
	{
	    is_children = 1;
	    if (traverse_subtree (v, faces, face -> child[i], 
					a, b, c, d, fsqrt(area) * 0.001) == 0)
		result = 0;
        }
    }

    /*
     *	===========================================
     *	Can we prune here ? 
     *	===========================================
     */
    if ((result == 1) && is_children)
    {
        /*
	 *	Zero out all children. 	
	 */
	for (i = 0 ; i < 4 ; i ++)
	    face -> child[i] = PFASD_NIL_ID;

#if 0
	for (i = 0 ; i < 3 ; i ++)
	    face -> refvert[i] = PFASD_NIL_ID;
#endif
    }

    return (result);
}


pfASD *
pfdBuildASD(unsigned int numx, unsigned int numy, pfVec3 *data, int mode, int inputlods, int buildpaging, char *prename, char *confname, char *pagename, int *lookahead)
{
    int i, j;
    float aspectRatio;
    int numRects;
    int rectWidth;
    meshEdge *edge1, *edge2, *theEdge;
    int edgeSplit;
    unsigned int inc;
    meshEdge *edgeList;
    meshFace *faceList;
    meshEdge *ep, *tep;
    meshFace *fp, *tfp;
    unsigned int dist;
    unsigned int refvertvertex;
    meshEdge *xEdge, *yEdge;
    unsigned int vert0, vert1, vert2;
    unsigned int refvert0, refvert1, refvert2;
    pfVec3 cp0, cp1, cp2;
    unsigned int tempv;
    float switchdist;
    int tsid=0;
    pfASD *asd;
    pfGeoState *gstate;
    pfGeoState **gstates;
    pfMaterial *material;
    pfASDLODRange *lods;
    pfASDFace *faces;
    pfVec3 *v0, *vd, *n0, *nd;
    int numverts, numdata, numnormals, numlods, numfaces, numfaces0;
    unsigned int num_singles=0;
    int tsidlevels;

    float *attrs;
    int stride;

    int nf, tmp_numfaces0, tmp_lods;;
    
    if ((numx < 2) || (numy < 2))
    {
        pfNotify(PFNFY_WARN, PFNFY_ASSERT,
                 "pfdBuildASD unsuccessful; bad data dimensions.");
        return(NULL);
    }

    for (i=0; i<MAX_LODS; ++i)  {
	meshFaces[i] = NULL;
	meshEdges[i] = NULL;
    }

    if(getenv("PFASDLODSIZE")!=NULL)
    {
	inputlods = atoi(getenv("PFASDLODSIZE"));
	pfNotify(PFNFY_INFO, PFNFY_PRINT,
                     "PFASDLODSIZE overriding size to %d\n",
			inputlods);
    }

        /* Total number of posts */
    numdata = numx * numy;

    srcData = data;

    lodLevel = 0;
    
        /****************************************************
        * Construct temporary edges and faces for all lods
        ****************************************************/

    /*
     *  Split the initial data block rectangle up into
     *  rectangles with aspect ratios close to 1.  These 
     *  near-square rectangles are then each divided into
     *  two triangles, and all the triangles will collectively
     *  serve as the lowest level of detail (level 0).
     */

    aspectRatio = (float) (numx-1) / (numy-1);

    if (aspectRatio < 1.0)
    {
        numRects = (int) ((1.0 / aspectRatio) + 0.5);
        rectWidth = numy / numRects;

	theEdge = addEdge(0, numx-1);

        for (i=0, inc=0; i<numRects-1; ++i, inc+=rectWidth)  {
	   edge1 = addEdge((inc+1)*numx - 1, (inc+rectWidth+1)*numx - 1);
	   edge2 = addEdge(inc*numx, (inc+rectWidth+1)*numx - 1);
	   addFace(theEdge, edge1, edge2, NULL);
	   theEdge = addEdge((inc+rectWidth)*numx, (inc+rectWidth+1)*numx - 1);
	   edge1 = addEdge(inc*numx, (inc+rectWidth)*numx);
	   addFace(theEdge, edge1, edge2, NULL);
	}
	edge1 = addEdge((inc+1)*numx - 1, numx*numy - 1);
	edge2 = addEdge(inc*numx, numx*numy - 1);
	addFace(theEdge, edge1, edge2, NULL);
	theEdge = addEdge(numx*(numy-1), numx*numy - 1);
	edge1 = addEdge(inc*numx, numx*(numy-1));
	addFace(theEdge, edge1, edge2, NULL);
    }
    else
    {
        numRects = (int) (aspectRatio + 0.5);
        rectWidth = numx / numRects;

        theEdge = addEdge(0, numx*(numy-1));

        for (i=0, inc=0; i<numRects-1; ++i, inc+=rectWidth)  {
	   edge1 = addEdge((numy-1)*numx+inc, (numy-1)*numx+inc+rectWidth);
	   edge2 = addEdge(inc, (numy-1)*numx + inc + rectWidth); 
	   addFace(edge1, theEdge, edge2, NULL);
	   edge1 = addEdge(inc, inc+rectWidth);
	   theEdge = addEdge(inc+rectWidth, (numy-1)*numx+inc+rectWidth);
	   addFace(edge1, theEdge, edge2, NULL);
	}
	edge1 = addEdge((numy-1)*numx + inc, numx*numy -1);
	edge2 = addEdge(inc, numx*numy - 1);
	addFace(edge1, theEdge, edge2, NULL);
	edge1 = addEdge(inc, numx-1);
	theEdge = addEdge(numx-1, numx*numy - 1);
	addFace(edge1, theEdge, edge2, NULL);
    }

   	tmp_numfaces0 = 2*(numRects+1);
        tmp_lods = maxlod(inputlods, numx, numy);
        nf = tmp_numfaces0*(1<<(tmp_numfaces0*tmp_lods -2));
        pfNotify(PFNFY_DEBUG, PFNFY_PRINT,
                "pfdBuildASD(): need about %d KBytes of memory to store all points\n"
                "and %d KBytes of memory to store all faces,\n"
                "and %dK bytes tmp space\n",
                (sizeof(pfASDVert)*maxverts(tmp_lods, tmp_numfaces0, numdata))/(1<<10),
                (nf*sizeof(pfASDFace))/(1<<10),
                (sizeof(int)*numdata+sizeof(meshFace)*nf+sizeof(meshEdge)*nf)/(1<<10));

        vmap = (int *) pfMalloc(sizeof(int)*numdata, pfGetSharedArena());
        dvmap = (int *) pfMalloc(sizeof(int)*numdata, pfGetSharedArena());
        for(i = 0; i < numdata; i++)
	{
                vmap[i] = -1;
                dvmap[i] = -1;
	}

        vcnt = dvcnt = 0;
        numverts = maxverts(tmp_lods, tmp_numfaces0, numdata);
/* this is the array of generated vertices */
        verts = (pfASDVert *) pfCalloc(
                numverts,
                sizeof(pfASDVert),
                pfGetSharedArena());

/* this is the temp array to hold delta values */
        dverts = (pfVec3 *) pfCalloc(
                numverts,
                sizeof(pfVec3),
                pfGetSharedArena());

    /* level 0 is in the Edge/Face structures now */
printf("level 0 is in the Edge/Face structures now\n");

        /* Now, go do all other lodLevels */
    while ((inputlods == -1)||(lodLevel < inputlods-1))
    {
	edgeSplit = FALSE;
	++lodLevel;
	edgeList = meshEdges[lodLevel-1];
	faceList = meshFaces[lodLevel-1];

    /*
     *  Go to each "straight" edge in the edge list and
     *  refvert it if possible.
     */

printf("process LOD level %d.\n", lodLevel);
                /**************************************************************
                * Split all edges at lodLevel-1 and create edge for lodLevel
                **************************************************************/
    for (ep = edgeList; ep != NULL; ep = ep->next)
    {
        if ((ep->v1 / numx) == (ep->v2 / numx))
	{
	    dist = abs((ep->v1 % numx) - (ep->v2 % numx));
	    if (dist > 1)  {
	    /*******************************************************
            * refvert in the middle and add the new edges as children
             *******************************************************/

	        refvertvertex = MIN(ep->v1, ep->v2) + (dist / 2);
		SplitVertex(refvertvertex, ep->v1, ep->v2);
	        ep->ch1 = addEdge(refvertvertex, ep->v1);
	        ep->ch2 = addEdge(refvertvertex, ep->v2);
		edgeSplit = TRUE;
	    }
        }
	else if ((ep->v1 % numx) == (ep->v2 % numx))
	{
	    dist = abs((ep->v1 / numx) - (ep->v2 / numx));
	    if (dist > 1)  {
		/*******************************************************
                * refvert in the middle and add the new edges as children
                 *******************************************************/
	        refvertvertex = MIN(ep->v1, ep->v2) + numx * (dist / 2);
		SplitVertex(refvertvertex, ep->v1, ep->v2);
	        ep->ch1 = addEdge(refvertvertex, ep->v1);
	        ep->ch2 = addEdge(refvertvertex, ep->v2);
		edgeSplit = TRUE;
            }
	}
    }

    /* If no edges were refverttable, we can quit the loop now. */

    if (!edgeSplit)
	break;

                /*
                * for face list in lodLevel-1, construct new faces for lodLevel
                * add new faces as children to lodLevel-1 faces
                */
    /*
     *  Now visit each face, refvertting "non-straight" edges
     *  only if both of the other two edges (presumably
     *  straight) have been refvert.
     */

    for (fp = faceList; fp != NULL; fp = fp->next)
    {
        if (fp->ex->ch1 && fp->ey->ch1 && !(fp->eh->ch1))  {
            refvertvertex = (fp->ex->ch1->v1 % numx) +
                          (fp->ey->ch1->v1 / numx)*numx;
            SplitVertex(refvertvertex, fp->eh->v1, fp->eh->v2);
	    fp->eh->ch1 = addEdge(refvertvertex, fp->eh->v1);
            fp->eh->ch2 = addEdge(refvertvertex, fp->eh->v2);
        }
    }

    /*
     *  Now visit each face, and derive the new child faces (if any).
     *  Each face should either have 0, 1 or 3 refvert vertices,
     *  and faces with 1 refvert vertex will never have this
     *  vertex on the hypotenuse.
     */

    for (fp = faceList; fp != NULL; fp = fp->next)
    {
	if (fp->ex->ch1 && fp->ey->ch1 && fp->eh->ch1)  {

	    edge1 = addEdge(fp->eh->ch1->v1, fp->ex->ch1->v1);
	    edge2 = addEdge(fp->eh->ch1->v1, fp->ey->ch1->v1);
	    theEdge = addEdge(fp->ex->ch1->v1, fp->ey->ch1->v1);
	    addFace(edge2, edge1, theEdge, fp);

	    tempv = fp->eh->ch1->v2;
	    if (fp->ex->ch1->v2 == tempv)  {
	        addFace(fp->ex->ch1, edge1, fp->eh->ch1, fp);
		xEdge = fp->ex->ch2;
	    }
	    else if (fp->ex->ch2->v2 == tempv)  {
		addFace(fp->ex->ch2, edge1, fp->eh->ch1, fp);
		xEdge = fp->ex->ch1;
	    }
	    else if (fp->ey->ch1->v2 == tempv)  {
		addFace(edge2, fp->ey->ch1, fp->eh->ch1, fp);
		yEdge = fp->ey->ch2;
	    }
	    else  {    /* assuming fp->ey->ch2->v2 == tempv */
		addFace(edge2, fp->ey->ch2, fp->eh->ch1, fp);
		yEdge = fp->ey->ch1;
	    } 

	    tempv = fp->eh->ch2->v2;
	    if (fp->ex->ch1->v2 == tempv)  {
	        addFace(fp->ex->ch1, edge1, fp->eh->ch2, fp);
		xEdge = fp->ex->ch2;
	    }
	    else if (fp->ex->ch2->v2 == tempv)  {
		addFace(fp->ex->ch2, edge1, fp->eh->ch2, fp);
		xEdge = fp->ex->ch1;
	    }
	    else if (fp->ey->ch1->v2 == tempv)  {
		addFace(edge2, fp->ey->ch1, fp->eh->ch2, fp);
		yEdge = fp->ey->ch2;
	    }
	    else  {    /* assuming fp->ey->ch2->v2 == tempv */
		addFace(edge2, fp->ey->ch2, fp->eh->ch2, fp);
		yEdge = fp->ey->ch1;
	    } 

	    addFace(xEdge, yEdge, theEdge, fp);
	}

	else if (fp->ex->ch1)
	{
	    if ((fp->ey->v1 == fp->eh->v1) || (fp->ey->v1 == fp->eh->v2))  {
                theEdge = addEdge(fp->ex->ch1->v1, fp->ey->v1);
		if (fp->ex->ch1->v2 == fp->ey->v2)  {
		    addFace(fp->ex->ch1, fp->ey, theEdge, fp);
		    addFace(fp->ex->ch2, theEdge, fp->eh, fp);
		}
		else  {
		    addFace(fp->ex->ch2, fp->ey, theEdge, fp);
		    addFace(fp->ex->ch1, theEdge, fp->eh, fp);
		}
            }
            else
	    {
                theEdge = addEdge(fp->ex->ch1->v1, fp->ey->v2);
		if (fp->ex->ch1->v2 == fp->ey->v1)  {
		    addFace(fp->ex->ch1, fp->ey, theEdge, fp);
		    addFace(fp->ex->ch2, theEdge, fp->eh, fp);
		}
		else  {
		    addFace(fp->ex->ch2, fp->ey, theEdge, fp);
		    addFace(fp->ex->ch1, theEdge, fp->eh, fp);
		}
	    }

            ++num_singles;
	}

	else if (fp->ey->ch1)
	{
	    if ((fp->ex->v1 == fp->eh->v1) || (fp->ex->v1 == fp->eh->v2))  {
                theEdge = addEdge(fp->ey->ch1->v1, fp->ex->v1);
		if (fp->ey->ch1->v2 == fp->ex->v2)  {
		    addFace(fp->ex, fp->ey->ch1, theEdge, fp);
		    addFace(theEdge, fp->ey->ch2, fp->eh, fp);
		}
		else  {
		    addFace(fp->ex, fp->ey->ch2, theEdge, fp);
		    addFace(theEdge, fp->ey->ch1, fp->eh, fp);
		}
            }
            else
	    {
                theEdge = addEdge(fp->ey->ch1->v1, fp->ex->v2);
		if (fp->ey->ch1->v2 == fp->ex->v1)  {
		    addFace(fp->ex, fp->ey->ch1, theEdge, fp);
		    addFace(theEdge, fp->ey->ch2, fp->eh, fp);
		}
		else  {
		    addFace(fp->ex, fp->ey->ch2, theEdge, fp);
		    addFace(theEdge, fp->ey->ch1, fp->eh, fp);
		}
	    }

            ++num_singles;
	}
    }
    }

        /* Done with all edges and faces */
    numlods = lodLevel+1;

        /* Count numfaces for lod 0 */
    for (fp=meshFaces[0], numfaces0=0; fp != NULL; fp = fp->next)
       ++numfaces0;

        /* Count numfaces for all lods */
    for (i=0, numfaces=0; i<numlods; ++i)
        for (fp=meshFaces[i]; fp != NULL; fp=fp->next, ++numfaces)
	   fp->id = numfaces;

        /**************************************
        * Now, compute normals for vertices
        **************************************/
#if NORMAL
    numnormals = numdata;

    n0 = (pfVec3 *) pfMalloc(sizeof(pfVec3) * numnormals, pfGetSharedArena());
    nd = (pfVec3 *) pfMalloc(sizeof(pfVec3) * numnormals, pfGetSharedArena());

    ComputeNormal(n0[0], data[0], data[1], data[numx+1], data[numx], NULL);
    ComputeNormal(n0[numx*(numy-1)], data[numx*(numy-1)], data[numx*(numy-2)],
                  data[numx*(numy-2)+1], data[numx*(numy-1)+1], NULL);
    ComputeNormal(n0[numx*numy-1], data[numx*numy-1], data[numx*numy-2],
                  data[numx*(numy-1)-2], data[numx*(numy-1)-1], NULL);
    ComputeNormal(n0[numx-1], data[numx-1], data[2*numx-1], data[2*numx-2],
                  data[numx-2], NULL);

    for (i=1; i<numy-1; ++i)
    {
        ComputeNormal(n0[i*numx], data[i*numx], data[(i-1)*numx],
                      data[i*numx+1], data[(i+1)*numx], NULL);
        ComputeNormal(n0[(i+1)*numx-1], data[(i+1)*numx-1],
                      data[(i+2)*numx-1], data[(i+1)*numx-2],
                      data[i*numx-1], NULL);
    }

    for (i=1; i<numx-1; ++i)
    {
        ComputeNormal(n0[i], data[i], data[i+1], data[i+numx], data[i-1],
                      NULL);
        ComputeNormal(n0[numx*(numy-1)+i], data[numx*(numy-1)+i],
                      data[numx*(numy-1)+i-1], data[numx*(numy-2)+i],
                      data[numx*(numy-1)+i+1], NULL);
    }

    for (i=1; i<numx-1; ++i)
        for (j=1; j<numy-1; ++j)
            ComputeNormal(n0[numx*j+i], data[numx*j+i],
                          data[numx*(j+1)+i], data[numx*j+i-1],
                          data[numx*(j-1)+i], data[numx*j+i+1]);

    for (i=0; i<numnormals; ++i)
        PFSET_VEC3(nd[i], 0.0f, 0.0f, 0.0f);

#endif

        /*******************************************************************
        * Now, construct the pfASD faces by packaging the temporary faces
        * constructed above into pfASDFace structures.
        *******************************************************************/
    faces = (pfASDFace *) pfMalloc(sizeof(pfASDFace) * numfaces,
                                 pfGetSharedArena());

        /****************************************************************
        * Create normal, color and texture coord attributes if desired
        ****************************************************************/

#if NORMAL
    stride = 3;   /* normal is pfVec3 */
    attrs = (float *)pfMalloc(numnormals*sizeof(float)*stride*2, pfGetSharedArena());
#endif

    for (i=0; i<numlods; ++i)
    {
	pfNotify(PFNFY_INFO, PFNFY_PRINT,
		"assigning lod %d data\n", i);
	for (fp=meshFaces[i]; fp != NULL; fp=fp->next)
	{
   	    int xx;

	    faces[fp->id].level = i;
	    faces[fp->id].gstateid = 0;
	    faces[fp->id].mask = 0;

            /*
	     *  Assign face vertices and refvert vertices such
	     *  that points are arranged counter-clockwise,
	     *  that appropriate tstrip order is maintained,
	     *  and that refvert points correspond to vertex order.
	     */

            if (MAX(fp->ex->v1, fp->ex->v2) == MAX(fp->eh->v1, fp->eh->v2))
	    {
		/* triangle is the "upper" of a pair */

                vert0 = MIN(fp->ex->v1, fp->ex->v2);
		vert1 = MIN(fp->eh->v1, fp->eh->v2);
		vert2 = MAX(fp->eh->v1, fp->eh->v2);
		refvert0 = fp->ey->ch1 ? fp->ey->ch1->v1 : PFASD_NIL_ID;
		refvert1 = fp->eh->ch1 ? fp->eh->ch1->v1 : PFASD_NIL_ID;
		refvert2 = fp->ex->ch1 ? fp->ex->ch1->v1 : PFASD_NIL_ID;
	    }
	    else    /* triangle is the "lower" of a pair */
	    {
                vert0 = MAX(fp->ey->v1, fp->ey->v2);
		vert1 = MIN(fp->eh->v1, fp->eh->v2);
		vert2 = MIN(fp->ey->v1, fp->ey->v2);
		refvert0 = fp->eh->ch1 ? fp->eh->ch1->v1 : PFASD_NIL_ID;
		refvert1 = fp->ex->ch1 ? fp->ex->ch1->v1 : PFASD_NIL_ID;
		refvert2 = fp->ey->ch1 ? fp->ey->ch1->v1 : PFASD_NIL_ID;
	    }

	    xx = finalmap(vert0);
	    PFCOPY_VEC3(verts[xx].v0, data[vert0]);
	    PFCOPY_VEC3(verts[xx].vd, dverts[map(vert0)]);
	    faces[fp->id].vert[0] = xx;
	    xx = finalmap(vert1);
	    PFCOPY_VEC3(verts[xx].v0, data[vert1]);
	    PFCOPY_VEC3(verts[xx].vd, dverts[map(vert1)]);
	    faces[fp->id].vert[1] = xx;
	    xx = finalmap(vert2);
	    PFCOPY_VEC3(verts[xx].v0, data[vert2]);
	    PFCOPY_VEC3(verts[xx].vd, dverts[map(vert2)]);
	    faces[fp->id].vert[2] = xx;

#if NORMAL
	    attrs[2*finalmap(vert0)*stride+0] = n0[vert0][0];
	    attrs[2*finalmap(vert0)*stride+1] = n0[vert0][1];
	    attrs[2*finalmap(vert0)*stride+2] = n0[vert0][2];
            attrs[(2*finalmap(vert0)+1)*stride+0] = nd[vert0][0];
            attrs[(2*finalmap(vert0)+1)*stride+1] = nd[vert0][1];
            attrs[(2*finalmap(vert0)+1)*stride+2] = nd[vert0][2];

	    attrs[2*finalmap(vert1)*stride+0] = n0[vert1][0];
	    attrs[2*finalmap(vert1)*stride+1] = n0[vert1][1];
	    attrs[2*finalmap(vert1)*stride+2] = n0[vert1][2];
            attrs[(2*finalmap(vert1)+1)*stride+0] = nd[vert1][0];
            attrs[(2*finalmap(vert1)+1)*stride+1] = nd[vert1][1];
            attrs[(2*finalmap(vert1)+1)*stride+2] = nd[vert1][2];

	    attrs[2*finalmap(vert2)*stride+0] = n0[vert2][0];
	    attrs[2*finalmap(vert2)*stride+1] = n0[vert2][1];
	    attrs[2*finalmap(vert2)*stride+2] = n0[vert2][2];
            attrs[(2*finalmap(vert2)+1)*stride+0] = nd[vert2][0];
            attrs[(2*finalmap(vert2)+1)*stride+1] = nd[vert2][1];
            attrs[(2*finalmap(vert2)+1)*stride+2] = nd[vert2][2];
#endif

	    if(refvert0 != PFASD_NIL_ID)
	    {
	        PFCOPY_VEC3(verts[finalmap(refvert0)].v0, data[refvert0]);
	        PFCOPY_VEC3(verts[finalmap(refvert0)].vd, dverts[map(refvert0)]);
		faces[fp->id].refvert[0] = finalmap(refvert0);
	    }
	    else
		faces[fp->id].refvert[0] = refvert0;
	
	    if(refvert1 != PFASD_NIL_ID)
	    {
	    	PFCOPY_VEC3(verts[finalmap(refvert1)].v0, data[refvert1]);
	        PFCOPY_VEC3(verts[finalmap(refvert1)].vd, dverts[map(refvert1)]);
		faces[fp->id].refvert[1] = finalmap(refvert1);
            }
            else
                faces[fp->id].refvert[1] = refvert1;

	    if(refvert2 != PFASD_NIL_ID)
	    {
	    	PFCOPY_VEC3(verts[finalmap(refvert2)].v0, data[refvert2]);
	        PFCOPY_VEC3(verts[finalmap(refvert2)].vd, dverts[map(refvert2)]);
		faces[fp->id].refvert[2] = finalmap(refvert2);
            }
            else
                faces[fp->id].refvert[2] = refvert2;

            faces[fp->id].attr[0] = faces[fp->id].vert[0];
            faces[fp->id].attr[1] = faces[fp->id].vert[1];
            faces[fp->id].attr[2] = faces[fp->id].vert[2];
            faces[fp->id].sattr[0] = faces[fp->id].refvert[0];
            faces[fp->id].sattr[1] = faces[fp->id].refvert[1];
            faces[fp->id].sattr[2] = faces[fp->id].refvert[2];

	    faces[fp->id].child[0] = fp->ch1 ? fp->ch1->id : PFASD_NIL_ID;
	    faces[fp->id].child[1] = fp->ch2 ? fp->ch2->id : PFASD_NIL_ID;
	    faces[fp->id].child[2] = fp->ch3 ? fp->ch3->id : PFASD_NIL_ID;
	    faces[fp->id].child[3] = fp->ch4 ? fp->ch4->id : PFASD_NIL_ID;

#ifdef PRINTVERTS
	    for(xx = 0; xx < 3; xx++)
printf("face[%d].vert[%d], v[%d] %f %f %f, %f %f %f\n", 
	fp->id, xx, faces[fp->id].vert[xx], 
	verts[faces[fp->id].vert[xx]].v0[0],
	verts[faces[fp->id].vert[xx]].v0[1], 
	verts[faces[fp->id].vert[xx]].v0[2],
	verts[faces[fp->id].vert[xx]].vd[0],
	verts[faces[fp->id].vert[xx]].vd[1], 
	verts[faces[fp->id].vert[xx]].vd[2]);
#endif
	}
    }


    /*
     *   Attempt to compute somewhat reasonable default switchin/morph
     *   ranges for each level of detail.
     */

printf("assigning LODRanges\n");
    lods = (pfASDLODRange *) pfMalloc(sizeof(pfASDLODRange) * numlods,
                                    pfGetSharedArena());

    switchdist = PFDISTANCE_PT3(data[0], data[numx*numy-1]) * 50.0;
printf("switchdist %f\n", switchdist);
    for (i=0; i<numlods; ++i, switchdist/=2.0)  {
       lods[i].switchin = switchdist;
       lods[i].morph = lods[i].switchin * 0.25;
    }
    lods[0].morph = 0.0;

    /*
     *   Assign triangle strip IDs.  Each row of triangles within
     *   each level of detail will belong to the same tristrip.
     *   The exception is the finest level of detail of blocks with
     *   non-"n^2 + 1" dimensions, where irregular subdivisions may
     *   have occurred.
     */

printf("assigning triangle tsid\n");
        /***************************************************************
        * The finest level may not be a full level because of possible
        * "single refverts".  Then, output single triangle tstrips only.
        ***************************************************************/

    if (num_singles)
    {
        tsidlevels = numlods-1;
        for (fp=meshFaces[numlods-1]; fp != NULL; fp=fp->next)
            faces[fp->id].tsid = 0;
        tsid += 2;
        
    }
    else
        tsidlevels = numlods;
        
        /*********************************************************************
        * All other levels make up a full tree.  Therefore, tmesh all levels.
        *********************************************************************/

    for (i=0; i < tsidlevels; ++i)  {

        for (tfp=meshFaces[i]; tfp != NULL; tfp=tfp->next)
	    if (tfp->ey->v2 == 0 || tfp->ey->v1 == 0)
		break;

        fp = tfp;

	while (fp)
	{
            while (fp)
	    {
                faces[fp->id].tsid = tsid;  ++tsid;
	        fp = OTHERFACE(fp, fp->eh);
                faces[fp->id].tsid = tsid;  ++tsid;
	        fp = OTHERFACE(fp, fp->ey);
            }
	    tsid += 2;

	    tfp = OTHERFACE(tfp, tfp->ex);
	    if (tfp)
		tfp = OTHERFACE(tfp, tfp->eh);
            fp = tfp;
        }
    }


        /************************************************************
        * Finally, free up the temporary mesh edge/face structures
        ************************************************************/

printf("free memory...\n");
    /* Finally, free up the temporary mesh edge/face structures */

    for (i=0; i<numlods; ++i)  {
	for (ep=meshEdges[i]; ep != NULL;)  {
	    tep = ep->next;
	    pfFree(ep);
	    ep = tep;
        }
	for (fp=meshFaces[i]; fp != NULL;)  {
	    tfp = fp->next;
	    pfFree(fp);
	    fp = tfp;
        }
    }


#if NORMAL
    pfFree(n0);
    pfFree(nd);
#endif

    pfFree(vmap);
    pfFree(dvmap);
    pfFree(dverts);


/* we will get rid off flat faces */

    prune_faces(verts, faces, numfaces0);

    if(buildpaging)
    {
	printf("break data into paging tiles\n");
        pfdBreakTiles(numlods, lods, numverts, verts, numfaces, faces, numfaces0, prename, confname);
        pfdPagingLookahead(confname, pagename, lookahead);
        return NULL;
    }

printf("create ASD node\n");
    asd = pfNewASD();
    pfASDAttr(asd, PFASD_LODS, 0, numlods, lods);
    pfASDAttr(asd, PFASD_COORDS, 0, vcnt, verts);

#ifdef PRINTVERTS
    {
	int xx;
	for(xx = 0; xx < 450; xx++)
	    printf("verts[%d], %f %f %f\n", xx, verts[xx].v0[0],
		verts[xx].v0[1], verts[xx].v0[2]);
    }
#endif

    pfASDAttr(asd, PFASD_FACES, 0, numfaces, faces);
    pfASDNumBaseFaces(asd, numfaces0);
printf("total %d verts, %d faces\n", vcnt, numfaces);
#if NORMAL
    pfASDAttr(asd, PFASD_PER_VERTEX_ATTR, PFASD_NORMALS, numnormals, attrs);
    pfASDMorphAttrs(asd, PFASD_NORMALS);
#else
    {
	float *normal;
	normal = (float *)pfMalloc(sizeof(float)*6, pfGetSharedArena());
	normal[0] = normal[1] = 0;
	normal[2] = 1.0;
	normal[3] = normal[4] = normal[5] = 0.0;
	pfASDAttr(asd, PFASD_OVERALL_ATTR, PFASD_NORMALS, 1, normal);
    }
#endif

    pfASDConfig(asd);
    
    /*
     *  Make a default GeoState for the newly loaded ASD node.
     *  This just uses the default material; note that the
     *  pfMtlColorMode must be enabled to allow pfGeoSet or other
     *  RGB color commands to override this default color setting.
     */

    gstate = pfNewGState(pfGetSharedArena());
    gstates = pfMalloc(sizeof(pfGeoState*), pfGetSharedArena());
    gstates[0] = gstate;
    material = pfNewMtl(pfGetSharedArena());
    pfMtlColorMode(material, PFMTL_FRONT, PFMTL_CMODE_OFF);
    pfMtlColor(material, PFMTL_AMBIENT,  0.8f, 0.8f, 0.8f);
    pfMtlColor(material, PFMTL_DIFFUSE,  0.8f, 0.8f, 0.8f);
    pfMtlColor(material, PFMTL_SPECULAR, 0.f, 0.f, 0.f);

    pfGStateAttr(gstate, PFSTATE_FRONTMTL, material);
    pfGStateMode(gstate, PFSTATE_ENLIGHTING, PF_ON);
    pfASDGStates(asd, gstates, 1);
    pfRef(gstate);

    if(pfIsOfType(asd, pfGetASDClassType()))
        printf("in pfdBuildASD, this is an ASD node\n");

printf("the arena size is %d K bytes\n", pfGetSharedArenaSize()/(1<<10));
printf("return asd\n");
    return asd;
}

void
pfdStoreASD(pfASD *asd, FILE *f)
{
    int numlods, numverts, numattrs, numfaces, numfaces0;
    pfASDLODRange *lods;
    pfASDVert *verts;
    pfASDFace *faces;
    float *a, *attrs, *overall_attrs;
    int bind, n, overall_mode, attr_mode;
    int overall_stride, stride;
    int morphattr;

printf("inside pfdStoreASD\n");
    pfGetASDAttr(asd, PFASD_LODS, &bind, &numlods, (void **)&lods);
    fwrite(&numlods, sizeof(int), 1, f);
    fwrite(lods, sizeof(pfASDLODRange), numlods, f);
printf("numlods %d\n", numlods);
    pfGetASDAttr(asd, PFASD_COORDS, &bind, &numverts, (void **)&verts);
    fwrite(&numverts, sizeof(int), 1, f);
    fwrite(verts, sizeof(pfASDVert), numverts, f);
printf("numverts %d\n", numverts);

#if NORMAL
    overall_mode = 0;
    attr_mode = 0;
    overall_stride = stride = 0;
    numattrs = 0;
    pfGetASDAttr(asd, PFASD_OVERALL_ATTR, &bind, &n, (void **)&a);
    overall_attrs = a;
    overall_mode = bind;
    if(bind&PFASD_NORMALS)
    {
	overall_stride += 3;
    }
    if(bind&PFASD_COLORS) 
    {
	overall_stride += 4;
    }
    if(bind&PFASD_TCOORDS)
    {
	overall_stride += 2;
    }
    pfGetASDAttr(asd, PFASD_PER_VERTEX_ATTR, &bind, &n, (void **)&a);
    attrs = a;
    attr_mode = bind;
    numattrs = n;
    if(bind&PFASD_NORMALS)
    {
	stride += 3;
    }
    if(bind&PFASD_COLORS)
    {
        stride += 4;
    }
    if(bind&PFASD_TCOORDS)
    {
        stride += 2;
    }
    bind = PFASD_PER_VERTEX_ATTR;
    fwrite(&bind, sizeof(int), 1, f);
    fwrite(&attr_mode, sizeof(int), 1, f);
    fwrite(&numattrs, sizeof(int), 1, f);
    fwrite(attrs, sizeof(float), numattrs * 2 * stride, f);
printf("per_vertex numattrs %d, attrmode %d\n", numattrs, attr_mode);
    bind = PFASD_OVERALL_ATTR;
    fwrite(&bind, sizeof(int), 1, f);
    fwrite(&overall_mode, sizeof(int), 1, f);
    if(overall_mode == 0)
	n = 0;
    else
    	n = 1;
    fwrite(&n, sizeof(int), 1, f);
    fwrite(overall_attrs, sizeof(float), 1 * 2 * overall_stride, f);
printf("overall numattrs %d, attrmode %d\n", n, overall_mode);
    
#endif

    pfGetASDAttr(asd, PFASD_FACES, &bind, &numfaces, (void **)&faces);
    fwrite(&numfaces, sizeof(int), 1, f);
    numfaces0 = pfGetASDNumBaseFaces(asd);
    fwrite(&numfaces0, sizeof(int), 1, f);
    fwrite(faces, sizeof(pfASDFace), numfaces, f);
printf("numfaces %d, numfaces0 %d\n", numfaces, numfaces0);

    morphattr = pfGetASDMorphAttrs(asd);
    fwrite(&morphattr, sizeof(int), 1, f);
printf("morphattr %d\n", morphattr);
}



void
pfdBreakTiles(int numlods, pfASDLODRange *lods, int numverts, pfASDVert *verts, int numfaces, pfASDFace *faces, int numfaces0, char *prename, char *conf)
{
    int i, j, k;
    pfASDFace *face;
    pfVec3 min, max;
    pfVec3 *origin;
    int ***FACE, ***V;
    int **numv, **numf;
    int cnt, fcnt;
    int x, y;
    pfVec3 grid; /* length of the grid cell */
    int *facequeue, qstart, qend;
    FILE *confp;
    short **totaltiles;
    float **tilesize;
    int *maxfaces, *maxverts;
    int *vflag, *fflag;

    pfBox *facebounds;

    pfBox bbox;

    vflag = (int *)pfCalloc(numverts, sizeof(int), pfGetSharedArena());
    facequeue = (int *)pfMalloc(numfaces*sizeof(int), pfGetSharedArena());
    fflag = (int *)pfCalloc(numfaces, sizeof(int), pfGetSharedArena());
    qstart = qend = 0;

    confp = (FILE *)fopen(conf, "w");
    totaltiles = (short **)pfMalloc((numlods+1)*sizeof(short*), pfGetSharedArena());
    tilesize = (float **)pfMalloc((numlods+1)*sizeof(float *), pfGetSharedArena());
    maxfaces = (int *)pfMalloc((numlods+1)*sizeof(int), pfGetSharedArena());
    maxverts = (int *)pfMalloc((numlods+1)*sizeof(int), pfGetSharedArena());

    for(i = 0; i <= numlods; i++)
    {
        totaltiles[i] = (short *)pfMalloc(2*sizeof(short), pfGetSharedArena());
        tilesize[i] = (float *)pfMalloc(2*sizeof(float), pfGetSharedArena());
    }

    PFCOPY_VEC3(min, verts[0].v0);
    PFCOPY_VEC3(max, verts[0].v0);
    for(i = 0; i < numverts; i++)
    {
        verts[i].neighborid[0]=verts[i].neighborid[1]=PFASD_NIL_ID;
        verts[i].vertid = i;
        if(verts[i].v0[0] < min[0]) min[0] = verts[i].v0[0];
        if(verts[i].v0[1] < min[1]) min[1] = verts[i].v0[1];
        if(verts[i].v0[2] < min[2]) min[2] = verts[i].v0[2];
        if(verts[i].v0[0] > max[0]) max[0] = verts[i].v0[0];
        if(verts[i].v0[1] > max[1]) max[1] = verts[i].v0[1];
        if(verts[i].v0[2] > max[2]) max[2] = verts[i].v0[2];
    }

    fprintf(confp, "%s\n", prename);
    fprintf(confp, "%d\n", numfaces0);
    fprintf(confp, "%d\n", numlods);
    for(i = 0; i < numlods; i++)
        fprintf(confp, "%f %f\n", lods[i].switchin, lods[i].morph);

    fprintf(confp, "%f %f %f\n", min[0], min[1], min[2]);
    fprintf(confp, "%f %f %f\n", max[0], max[1], max[2]);
    PFCOPY_VEC3(bbox.min, min);
    PFCOPY_VEC3(bbox.max, max);
    origin = (pfVec3*)pfMalloc(sizeof(pfVec3)*numlods, pfGetSharedArena());
    for(i = 0; i< numlods; i++)
	PFCOPY_VEC3(origin[i], min);
/* all lods have the same min corner */
#if 0
    pfASDOrigin(asd, origin);
    pfASDBBox(asd, &bbox);
    pfASDMorphAttrs(asd, PFASD_NO_ATTR);
    pfASDNumBaseFaces(asd, numfaces0);
    pfASDPageFname(asd, prename);
#endif


/* find all the bboxes for all the faces. these boxes are stored in
   the files too */

    facebounds = pfdGetTerrainBounds(faces, verts, numfaces0, numfaces);

    FACE = (int ***)pfCalloc(1, sizeof(int**), pfGetSharedArena());
    FACE[0] = (int **)pfCalloc(1, sizeof(int*), pfGetSharedArena());
    FACE[0][0] = (int *)pfCalloc(numfaces0, sizeof(int), pfGetSharedArena());
    V = (int ***)pfCalloc(1, sizeof(int**), pfGetSharedArena());
    V[0] = (int **)pfCalloc(1, sizeof(int*), pfGetSharedArena());
    V[0][0] = (int *)pfCalloc(numfaces0*6, sizeof(int), pfGetSharedArena());
    numv = (int **)pfCalloc(1, sizeof(int*), pfGetSharedArena());
    numv[0] = (int *)pfCalloc(1, sizeof(int), pfGetSharedArena());
    numf = (int **)pfCalloc(1, sizeof(int*), pfGetSharedArena());
    numf[0] = (int *)pfCalloc(1, sizeof(int), pfGetSharedArena());

    numf[0][0] = 0;
    numv[0][0] = 0;
    for(i = 0; i < numfaces0; i++)
    {
        face = &faces[i];

        FACE[0][0][i] = i;
        fflag[i] = 1;
        for(j = 0; j < 3; j++)
            if(vflag[face->vert[j]] == 0)
            {
                V[0][0][numv[0][0]++] = face->vert[j];
                vflag[face->vert[j]] = 1;
            }

        for(j = 0; j < 3; j++)
        {
            int sid;

            sid = face->refvert[j];
            if(sid > PFASD_NIL_ID)
            {
                if(verts[sid].neighborid[0] == PFASD_NIL_ID)
                    verts[sid].neighborid[0] = i;
                else if(verts[sid].neighborid[1] == PFASD_NIL_ID)
                    verts[sid].neighborid[1] = i;
                else
                    pfNotify(PFNFY_DEBUG, PFNFY_PRINT, "pfdBreakTile:\n refvert %d has too many neighbors\n", sid);
                if(vflag[sid] == 0)
                {
                    V[0][0][numv[0][0]++] = sid;
                    vflag[sid] = 1;
                }
            }
        }

        facequeue[qend++] = i; /* put tris on the queue for next lod breakup */
    }

    numf[0][0] = numfaces0;
    pfdWriteFile(FACE, numf, V, numv, faces, verts, facebounds, 0, 1, 1, prename);
    totaltiles[0][0] = totaltiles[0][1] = 1;
    tilesize[0][0] = max[0]-min[0];
    tilesize[0][1] = max[1]-min[1];
    maxfaces[0] = numfaces0;
    maxverts[0] = numv[0][0];

    pfFree(FACE[0][0]);
    pfFree(FACE[0]);
    pfFree(FACE);
    pfFree(V[0][0]);
    pfFree(V[0]);
    pfFree(V);
    pfFree(numv[0]);
    pfFree(numv);
    pfFree(numf[0]);
    pfFree(numf);

    while(qstart != qend)
    {
    float maxedge;
    int level;

    cnt = qstart;
    fcnt = 0;
    maxedge = 0;
    face = &faces[facequeue[cnt]];
    level = face->level;
    printf("check out faces in level %d\n", level);
    while((cnt != qend)&&(face->level == level))
         /* this is the triangles of this level */
    {
        float e;
        int a, b;
        int ii;

        fcnt++;
        for(ii = 0; ii < 3; ii++)
        {
            a = face->vert[ii];
            b = face->vert[(ii+1)%3];
            e = PFDISTANCE_PT3(verts[a].v0, verts[b].v0);
            if(e > maxedge)
                maxedge = e;
        }
        face = &faces[facequeue[++cnt]];
/*
        printf("while check out faces in level %d\n", face->level);
*/
    }
       
    x = (max[0] - min[0])/(2*maxedge) + 1; /* estimated x num of cells */
    y = (max[1] - min[1])/(2*maxedge) + 1; /* estimated y num of cells */

printf("x %d, y %d, fcnt %d fcnt/(x*y) %d \n", x, y, fcnt, fcnt/(x*y));
    if(fcnt/(x*y) < MIN_TRIS) /* if the cell has too few triangles */
    {
        x = sqrt(fcnt/MIN_TRIS) + 1;
        y = sqrt(fcnt/MIN_TRIS) + 1;
    }

    while(fcnt/(x*y) > MAX_TRIS/2) /* if the cell contains too many triangles */
    {
	x = 2*x;
	y = 2*y;
    }

    grid[0] = (max[0] - min[0])/(float)x; /* enlarge the cell */
    grid[1] = (max[1] - min[1])/(float)y;

/* we will construct tiles for the next LOD based on this LOD */
    level += 1;
    totaltiles[level][0] = x;
    totaltiles[level][1] = y;
    tilesize[level][0] = grid[0];
    tilesize[level][1] = grid[1];
/* 8 is an experimental number */
  
    FACE = (int ***)pfCalloc(x, sizeof(int**), pfGetSharedArena());
    V = (int ***)pfCalloc(x, sizeof(int**), pfGetSharedArena());
    numf = (int **)pfCalloc(x, sizeof(int*), pfGetSharedArena());
    numv = (int **)pfCalloc(x, sizeof(int*), pfGetSharedArena());

    for(i = 0; i < x; i++)
    {
        FACE[i] = (int **)pfCalloc(y, sizeof(int*), pfGetSharedArena());
        V[i] = (int **)pfCalloc(y, sizeof(int*), pfGetSharedArena());
        numf[i] = (int *)pfCalloc(y, sizeof(int), pfGetSharedArena());
        numv[i] = (int *)pfCalloc(y, sizeof(int), pfGetSharedArena());
        for(j = 0; j < y; j++)
        {
            FACE[i][j] = (int *)pfCalloc(MAX_TRIS, sizeof(int), pfGetSharedArena());
            V[i][j] = (int *)pfCalloc(MAX_TRIS*6, sizeof(int), pfGetSharedArena());
        }
    }

    while(qstart != cnt) /* allocate children of tris in this LOD */
    {
        int ix, iy;
        float xx, yy;
        int faceid;

        face = &faces[facequeue[qstart]];
/*
        printf("process facequeue[%d]=%d\n", qstart, facequeue[qstart]);
        printf("face->level %d\n", face->level);
*/
        faceid = facequeue[qstart];
        xx = (verts[face->vert[0]].v0[0]-min[0])/grid[0];
        ix = (verts[face->vert[0]].v0[0]-min[0])/grid[0];
        if(((xx - ix) < SMALL_EPS)&&(xx > SMALL_EPS))
            ix = ix - 1;
        yy = (verts[face->vert[0]].v0[1]-min[1])/grid[1];
        iy = (verts[face->vert[0]].v0[1]-min[1])/grid[1];
        if(((yy - iy) < SMALL_EPS)&&(yy > SMALL_EPS))
            iy = iy - 1;
        for(i = 0; i < 4; i++)
        {
            if(face->child[i] > PFASD_NIL_ID) /* got a child, put it
                                                        in the cell */
            {
                int cid;
                pfASDFace *cface;

                cid = face->child[i];
                cface = &faces[cid];
/*
                printf("checking out child face %d\n", cid);
*/
                if(fflag[cid] == 0)
                {
/*
                printf("assign child face %d, level %d\n", cid, cface->level);
*/
                    FACE[ix][iy][numf[ix][iy]] = cid;
                    fflag[cid] = 1;
                    numf[ix][iy]++;
/* XXXX if there are too many triangles, enlarge the storage space. hopefully there won't be more than 2*MAX_TRIS triangles. otherwise, serious memory problems */
		    if(numf[ix][iy] >= MAX_TRIS)
			FACE[ix][iy] = pfRealloc(FACE[ix][iy], 2*MAX_TRIS*sizeof(int));
                }
                for(j = 0; j < 3; j++)
                {
                    int sid;
                    int sx, sy;

                    sid = cface->refvert[j];
                    if(sid > PFASD_NIL_ID)
                    {
                        if(vflag[sid]==0)
                        {
                        xx = (verts[sid].v0[0]+verts[sid].vd[0]-min[0])/grid[0];                        sx = (verts[sid].v0[0]+verts[sid].vd[0]-min[0])/grid[0];                        if(((xx-sx) < SMALL_EPS)&&(xx > SMALL_EPS))
                        sx -= 1;
                        yy = (verts[sid].v0[1]+verts[sid].vd[1]-min[1])/grid[1];                        sy = (verts[sid].v0[1]+verts[sid].vd[1]-min[1])/grid[1];                        if(((yy-sy) < SMALL_EPS)&&(yy > SMALL_EPS))
                        sy -= 1;
                        V[sx][sy][numv[sx][sy]] = sid;
                        numv[sx][sy]++;
			if(numv[sx][sy] >= MAX_TRIS*6)
			    V[sx][sy] = pfRealloc(V[sx][sy], 12*MAX_TRIS*sizeof(int));
                        vflag[sid] = 1;
                        }
                        if(verts[sid].neighborid[0] == PFASD_NIL_ID)
                            verts[sid].neighborid[0] = cid;
                        else if(verts[sid].neighborid[1] == PFASD_NIL_ID)
                            verts[sid].neighborid[1] = cid;

                    }
                }
                facequeue[qend++] = cid;
            }
        }
        qstart++;
    }

    maxfaces[level] = 0;
    maxverts[level] = 0;
    for(i = 0; i < x; i++)
        for(j = 0; j < y; j++)
        {
            if(numf[i][j] > maxfaces[level])
                maxfaces[level] = numf[i][j];
            if(numv[i][j] > maxverts[level])
                maxverts[level] = numv[i][j];
        }

    pfdWriteFile(FACE, numf, V, numv, faces, verts, facebounds, level, x, y, prename);

    for(i = 0; i < x; i++)
    {
        for(j = 0; j < y; j++)
        {
            pfFree(FACE[i][j]);
            pfFree(V[i][j]);
        }
        pfFree(FACE[i]);
        pfFree(V[i]);
        pfFree(numf[i]);
        pfFree(numv[i]);
    }

    pfFree(FACE);
    pfFree(V);
    pfFree(numv);
    pfFree(numf);
    }

    for(i = 0; i < numlods; i++)
        fprintf(confp, "%d %d\n", totaltiles[i][0], totaltiles[i][1]);

    for(i = 0; i < numlods; i++)
        fprintf(confp, "%f %f\n", tilesize[i][0], tilesize[i][1]);

    for(i = 0; i < numlods; i++)
    	fprintf(confp, "%d %d\n", maxfaces[i], maxverts[i]);

    pfFree(verts);
    pfFree(facebounds);
    pfFree(maxfaces);
    pfFree(maxverts);
    fclose(confp);

}


void
pfdPagingLookahead(char *confile, char *pfile, int *lookahead)
{
    FILE *confp;
    FILE *pfp;
    int page[2], numfaces0, numlods, x, y, i;
    char prename[140];
    pfASDLODRange *lods;
    float dummy, tilesize[2];

    if((confp = (FILE *)fopen(confile, "r")) == NULL)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT,
	    "can't open file %s\n", confile);
	return;
    }

    if((pfp = (FILE *)fopen(pfile, "w")) == NULL)
    {
        pfNotify(PFNFY_WARN, PFNFY_PRINT,
            "can't open file %s\n", pfile);
        return;
    }

    fscanf(confp, "%s", prename);
    fscanf(confp, "%d", &numfaces0);
    fscanf(confp, "%d", &numlods);

    lods = (pfASDLODRange *) pfMalloc(sizeof(pfASDLODRange) * numlods,
                           pfGetSharedArena());
    for(i = 0; i < numlods; i++)
        fscanf(confp, "%f %f", &(lods[i].switchin), &(lods[i].morph));
    fscanf(confp, "%f %f %f", &dummy, &dummy, &dummy);
    fscanf(confp, "%f %f %f", &dummy, &dummy, &dummy);
    for(i = 0; i< numlods; i++)
    {
        int x, y;
        fscanf(confp, "%d %d", &x, &y);
    }
    for(i = 0; i < numlods; i++)
    {
        fscanf(confp, "%f %f", &tilesize[0], &tilesize[1]);
	printf("%f %f\n", tilesize[0], tilesize[1]);
	page[0] = (int)(lods[i].switchin/tilesize[0]) + lookahead[0];
	page[1] = (int)(lods[i].switchin/tilesize[1]) + lookahead[1];
	printf("lookahead %d %d\n", lookahead[0], lookahead[1]);
	printf("page %f %d\n", lods[i].switchin/tilesize[0], page[0]);
	fprintf(pfp, "%d %d\n", page[0], page[1]);
    }

    fclose(confp);
    fclose(pfp);
}

pfASD *
pfdLoadConfig(char *fname, char *pagename)
{
    	FILE *confp, *pfp;
	int numlods;
	pfASDLODRange *lods;
        int *maxv, *maxf, mv, mf;
        int ci, cj;
        pfBox bbox;
        pfVec3 min;
 	pfVec3 *origin;
        short **totaltiles, **page;
        float **tilesize;
 	pfASD *asd;
 	int numfaces0;
	char *prename;

    	pfGeoState *gstate, **gstates;
    	pfMaterial *material;

	int i;
	
        if((confp = (FILE *)fopen(fname, "r"))==NULL)
	{
	    pfNotify(PFNFY_WARN, PFNFY_ASSERT,
		"can't open file %s\n", fname);
	    return NULL;
 	}

 	if((pfp = (FILE *)fopen(pagename, "r"))==NULL)
        {
            pfNotify(PFNFY_WARN, PFNFY_ASSERT,
                "can't open file %s\n", pagename);
            return NULL;
        }

	asd = pfNewASD();
	prename = pfMalloc(sizeof(char)*150, pfGetSharedArena());
	fscanf(confp, "%s", prename);
	pfASDPageFname(asd, prename);
        fscanf(confp, "%d", &numfaces0);
	pfASDNumBaseFaces(asd, numfaces0);
        fscanf(confp, "%d", &numlods);
        page = (short **)pfMalloc(numlods*sizeof(short*), pfGetSharedArena());
        totaltiles = (short **)pfMalloc(numlods*sizeof(short*), pfGetSharedArena());
        tilesize = (float **)pfMalloc(numlods*sizeof(float *), pfGetSharedArena());
  	maxf = (int *)pfMalloc(numlods*sizeof(int), pfGetSharedArena());
  	maxv = (int *)pfMalloc(numlods*sizeof(int), pfGetSharedArena());
        for(ci = 0; ci < numlods; ci++)
        {
            totaltiles[ci] = (short *)pfMalloc(2*sizeof(short), pfGetSharedArena());
            tilesize[ci] = (float *)pfMalloc(2*sizeof(float), pfGetSharedArena());
        }

        lods = (pfASDLODRange *) pfMalloc(sizeof(pfASDLODRange) * numlods,
                                    pfGetSharedArena());
        for(ci = 0; ci < numlods; ci++)
            fscanf(confp, "%f %f", &(lods[ci].switchin), &(lods[ci].morph));
        pfASDAttr(asd, PFASD_LODS, 0, numlods, lods);
        fscanf(confp, "%f %f %f", &min[0], &min[1], &min[2]);
        PFCOPY_VEC3(bbox.min, min);
        fscanf(confp, "%f %f %f", &bbox.max[0], &bbox.max[1], &bbox.max[2]);
	origin = (pfVec3*)pfMalloc(sizeof(pfVec3)*numlods, pfGetSharedArena());
    	for(i = 0; i< numlods; i++)
            PFCOPY_VEC3(origin[i], min);
        pfASDOrigin(asd, origin);
        pfASDBBox(asd, &bbox);
        for(ci = 0; ci < numlods; ci++)
	{
	    int x, y;
            fscanf(confp, "%d %d", &x, &y);
            totaltiles[ci][0] = (short)x;
	    totaltiles[ci][1] = (short)y;
	}
        pfASDTotalTiles(asd, totaltiles);

/*
        for(ci = 0; ci < numlods; ci++)
        {
	    int x, y;
            fscanf(confp, "%d %d", &x, &y);
	    page[ci][0] = (short)x;
	    page[ci][1] = (short)y;
	printf("page[%d] %d %d\n", ci, page[ci][0], page[ci][1]);
 	    if(page[ci][0] == 0) page[ci][0]+=1;
	    if(page[ci][1] == 0) page[ci][1]+=1;
        }
        pfASDPageSize(asd, page);
*/

        for(ci = 0; ci < numlods; ci++)
            fscanf(confp, "%f %f", &tilesize[ci][0], &tilesize[ci][1]);
        pfASDTileSize(asd, tilesize);
	mf = mv = -1;
     	for(ci = 0; ci < numlods; ci++)
	{
            fscanf(confp, "%d %d", &maxf[ci], &maxv[ci]);
	    if(maxf[ci] > mf) mf = maxf[ci];
	    if(maxv[ci] > mv) mv = maxv[ci];
	}
	printf("max faces %d, max verts %d\n", mf, mv);
        pfASDMaxTileMemSize(asd, mf, mv);

#if 0
 	for(ci = 0; ci < numlods; ci++)
	{
	    pfFree(totaltiles[ci]);
	    pfFree(tilesize[ci]);
	}
	pfFree(totaltiles);
	pfFree(tilesize);
	pfFree(maxf);
	pfFree(maxv);
	pfFree(origin);
#endif

	page = (short **)pfMalloc(numlods*sizeof(short*), pfGetSharedArena());
	for(ci = 0; ci < numlods; ci++)
        {
            int x, y;
            fscanf(pfp, "%d %d", &x, &y);
            page[ci] = (short *)pfMalloc(2*sizeof(short), pfGetSharedArena());
            page[ci][0] = (short)x;
            page[ci][1] = (short)y;
        printf("page[%d] %d %d\n", ci, page[ci][0], page[ci][1]);
            if(page[ci][0] == 0) page[ci][0]+=1;
            if(page[ci][1] == 0) page[ci][1]+=1;
        }
        pfASDPageSize(asd, page);
#if 0
	for(ci = 0; ci < numlods; ci++)
        {
            pfFree(page[ci]);
	}
	pfFree(page);
#endif

 	fclose(confp);
	fclose(pfp);

    /*
     *  Make a default GeoState for the newly loaded ASD node.
     *  This just uses the default material; note that the
     *  pfMtlColorMode must be enabled to allow pfGeoSet or other
     *  RGB color commands to override this default color setting.
     */

    gstate = pfNewGState(pfGetSharedArena());
    gstates = pfMalloc(sizeof(pfGeoState*), pfGetSharedArena());
    gstates[0] = gstate;
    material = pfNewMtl(pfGetSharedArena());
    pfMtlColorMode(material, PFMTL_FRONT, PFMTL_CMODE_OFF);
    pfGStateAttr(gstate, PFSTATE_FRONTMTL, material);
    pfGStateMode(gstate, PFSTATE_ENLIGHTING, PF_ON);
    pfASDGStates(asd, gstates, 1);
    pfRef(gstate);

    pfASDInitPaging(asd);
    pfASDConfig(asd);

    return asd;
}

void
pfdWriteFile(int ***FACE, int **numf, int ***V, int **numv, pfASDFace *faces, pfASDVert *verts, pfBox *facebounds, int lod, int x, int y, char *prename)
{
    char fname[300];
    char tname[300];
    FILE *fp, *tp;
    int i, j, m, n;

    for(i = 0; i < x; i++)
        for(j = 0; j < y; j++)
        {
	/* if there are no data in this tile, don't write it out */
 	    if((numf[i][j]==0)&&(numv[i][j]==0))
		continue;
            sprintf(fname, "%s%02d%03d%03d", prename, lod, i, j);
	    /*
            sprintf(tname, "a%s%02d%03d%03d", prename, lod, i, j);
	    */
            printf("output file %s\n", fname);
            if(!(fp = (FILE *)fopen(fname, "w")))
            {
                printf("can't open file %s\n", fname);
                return;
            }
	    /*
            if(!(tp = (FILE *)fopen(tname, "w")))
            {
                printf("can't open file %s\n", tname);
                return;
            }
	    */
            fwrite(&(numf[i][j]), sizeof(int), 1, fp);
            fwrite(&(numv[i][j]), sizeof(int), 1, fp);
	    /*
            fprintf(tp, "%d ", (numf[i][j]));
            fprintf(tp, "%d\n", (numv[i][j]));
	    */
            for(m = 0; m < numf[i][j]; m++)
            {
		    fwrite(&FACE[i][j][m], sizeof(int), 1, fp);
                    fwrite(&faces[FACE[i][j][m]], sizeof(pfASDFace), 1, fp);
            }
            for(m = 0; m < numf[i][j]; m++)
            {
                    fwrite(&facebounds[FACE[i][j][m]], sizeof(pfBox), 1, fp);
		    /*
                    fprintf(tp, "%f %f %f %f %f %f\n", facebounds[FACE[i][j][m]].min[0], facebounds[FACE[i][j][m]].min[1], facebounds[FACE[i][j][m]].min[2], facebounds[FACE[i][j][m]].max[0], facebounds[FACE[i][j][m]].max[1], facebounds[FACE[i][j][m]].max[2]);
		    */
            }
            for(n = 0; n < numv[i][j]; n++)
            {
		    fwrite(&(verts[V[i][j][n]].vertid), sizeof(int), 1, fp);
                    fwrite(&verts[V[i][j][n]], sizeof(pfASDVert), 1, fp);
		    /*
                    fprintf(tp, "%d\n", verts[V[i][j][n]].vertid);
		    */
            }
            fclose(fp);
	    /*
            fclose(tp);
	    */
        }
}

pfBox *
pfdGetTerrainBounds(pfASDFace *faces, pfASDVert *verts, int numfaces0, int numfaces){
    int faceid;
    pfBox *facebounds;

    facebounds = (pfBox *)pfMalloc(sizeof(pfBox)*numfaces, pfGetSharedArena());
    for (faceid=0; faceid<numfaces0; faceid++)
            computeBBox(facebounds, faces, verts, faceid);
    return facebounds;
}

void
computeBBox(pfBox *facebounds, pfASDFace *faces, pfASDVert *verts, int _faceid)
{
    int i;
    const pfASDFace *face;
    pfBox *bounds;

    if (_faceid == PFASD_NIL_ID) return;

    face = &faces[_faceid];
    bounds = (pfBox*) &facebounds[_faceid];

    pfMakeEmptyBox(bounds);
    for (i=0; i<3; i++)
    {
        pfVec3 v, vd;
        int vertid;
        int refvertid;
        vertid = face->vert[i];
        refvertid = face->refvert[i];

        PFCOPY_VEC3(v, verts[vertid].v0);
        pfBoxExtendByPt(bounds, v);

        PFCOPY_VEC3(vd, verts[vertid].vd);
        v[0] += vd[0];
        v[1] += vd[1];
        v[2] += vd[2];
        pfBoxExtendByPt(bounds, v);

/*
        if(refvertid > PFASD_NIL_ID) {
            v = VERT[refvertid].v0;
            bounds->extendBy(v);
        }
*/
    }

  /*
   * Calculate child bounding volumes, and union with parent
   */
    for (i=0; i<4; i++)
    {
        int childfaceid;

        childfaceid = face->child[i];
        if (childfaceid == PFASD_NIL_ID) continue;

        computeBBox(facebounds, faces, verts, childfaceid);
        pfBoxExtendByBox(bounds, &facebounds[childfaceid]);
    }

/*
printf("bounds %f %f %f, %f %f %f\n", bounds->min[0],
bounds->min[1], bounds->min[2], bounds->max[0], bounds->max[1], bounds->max[2]);*/

}