[BACK]Return to pfptu.c CVS log [TXT][DIR] Up to [Development] / performer / src / lib / libpfdb / libpfptu

File: [Development] / performer / src / lib / libpfdb / libpfptu / pfptu.c (download)

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

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

/*
 * Copyright 1994, 1995, 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
 */

/*
 * pfptu.c: $Revision: 1.1 $ $Date: 2000/11/21 21:39:34 $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <bstring.h>
#include <math.h>
#include <ctype.h>

#ifdef	_POSIX_SOURCE
extern char *strdup (const char *s1);
#endif

#include <Performer/pf.h>
#include <Performer/pfdu.h>
#include <Performer/pfutil.h>
#include <Performer/pfdb/pfptu.h>
#include <Performer/image.h>
#include "ilgettile.h"

static void MakeTri(pfdGeoBuilder *b, 
		    pfVec3 co1, pfVec3 co2, pfVec3 co3, 
		    float tex1s, float tex1t, 
		    float tex2s, float tex2t, 
		    float tex3s, float tex3t);
static void MakeQuad(pfdGeoBuilder *b, 
		     pfVec3 co1, pfVec3 co2, pfVec3 co3, pfVec3 co4, 
		     float tex1s, float tex1t, float tex2s, float tex2t, 
		     float tex3s, float tex3t, float tex4s, float tex4t);

static TNode	    *NewTerrainNode(char *fileName);
static void	    MakeGStateList(TNode *T);
static void	    AttachLists(TNode *T);
static void	    MakeTextureList(TNode *T);
static void	    MakeGeodeList(TNode *T);

#define	USE_TEXGEN

static pfdGeom *geometry = NULL;
static pfGeoState *state = NULL;

pfNode *
pfdLoadFile_ptu (char *fileName)
{
    TNode	*T;
    
    /* restore builder to initial state */
    pfdResetBldrGeometry();
    pfdResetBldrState();

    /* space for four vertices */
    geometry = pfdNewGeom(4);

    T = NewTerrainNode(fileName);
    if (T == NULL)
	return (pfNode *)T;
    
    ildeffile(T->imageFile, T->imageFile, T->imageSizeX, T->imageSizeY);
    ildeffile(T->postFile, T->postFile, T->postSizeX, T->postSizeY);

    MakeTextureList(T);
    
    MakeGeodeList(T);
   
    ilfreetiles();
    
    MakeGStateList(T);
    
    AttachLists(T);
    
    pfNodeName(T->group, T->name);
    
    pfUserData(T->group, (void *)T);
    pfNotify(PFNFY_INFO, PFNFY_PRINT, 
	    "<terrain node>\t%s\n\t\tpost <%d %d>: %s\n\t\ttexture <%d %d>: %s\n\t\tnlods: %d\
	    \n\t\ttiles: %d\t%d\n\t\tscale: %f\t%f\t%f\n<end %s>\n", 
	    T->name, 
	    T->postSizeX, T->postSizeY, T->postFile, 
	    T->imageSizeX, T->imageSizeY, T->imageFile, 
	    T->nLODs, T->tilesX, T->tilesY, 
	    T->scaleX, T->scaleY, T->scaleZ, 
	    T->name);

    /* release storage used by geometry */
    pfdDelGeom(geometry);

    return (pfNode *)T->group;
}


static int defaultDetProps[5] = { 16, 16, 4, 4, 0 };

static void
MakeTri(pfdGeoBuilder *b, 
	pfVec3 co1, pfVec3 co2, pfVec3 co3, 
	float tex1s, float tex1t, 
	float tex2s, float tex2t, 	
	float tex3s, float tex3t)
{
    pfdGeom p, *pb;
    pfVec3 tmp1, tmp2;
    int	   t; 
    
    geometry->cbind = PFGS_PER_PRIM;
    geometry->nbind = PFGS_PER_PRIM;
#ifdef USE_TEXGEN
    geometry->tbind[0] = PFGS_OFF;
#else
    geometry->tbind[0] = PFGS_PER_VERTEX;
#endif

    for (t = 1 ; t < PF_MAX_TEXTURES ; t ++)
	geometry->tbind[t] = PFGS_PER_VERTEX;

    geometry->flags = 0;
    geometry->numVerts = 3;
    
   /* specify vertices */
    pfCopyVec3( geometry->coords[0], co1);
    pfCopyVec3( geometry->coords[1], co2);
    pfCopyVec3( geometry->coords[2], co3);

#ifdef USE_TEXGEN
#else
    /* specify texture coordinates */
    pfSetVec2(geometry->texCoords[0],	tex1s, tex1t);
    pfSetVec2(geometry->texCoords[1],   tex2s, tex2t); 
    pfSetVec2(geometry->texCoords[2],   tex3s, tex3t);
#endif

    /* specify color */
    pfSetVec4(geometry->colors[0], 1.0, 1.0, 1.0, 1.0);
				
    /* compute face normal */
    pfSubVec3(tmp1, geometry->coords[1], geometry->coords[0]);
    pfSubVec3(tmp2, geometry->coords[2], geometry->coords[1]);
    pfCrossVec3(geometry->norms[0], tmp1, tmp2);
    pfNormalizeVec3(geometry->norms[0]);
			    
    /* add face to builder */
    pfdAddPoly(b, geometry);
}

static void 
MakeQuad(pfdGeoBuilder *b, 
	 pfVec3 co1, pfVec3 co2, pfVec3 co3, pfVec3 co4, 
	 float tex1s, float tex1t, float tex2s, float tex2t, 
	 float tex3s, float tex3t, float tex4s, float tex4t)
{
    MakeTri(b, co1, co2, co4, tex1s, tex1t, tex2s, tex2t, tex4s, tex4t);
    MakeTri(b, co4, co2, co3, tex4s, tex4t, tex2s, tex2t, tex3s, tex3t);
}


static TNode*
NewTerrainNode(char *fileName)
{
    TNode	*T;
    FILE	*fp;
    char	buf[200];
    int		i;
    char	filePath[PF_MAXSTRING];
    

    if (pfFindFile(fileName, filePath, R_OK))
	fp = fopen(filePath, "r");
    else
	fp = NULL;

    if (fp == NULL)
    {
	pfNotify(PFNFY_WARN, PFNFY_PRINT, 
		 "pfdLoadFile_ptu: can't open file %s", fileName);
	return NULL;
    }
    
    T = (TNode *)pfMalloc(sizeof(TNode), pfGetSharedArena());
    
    fscanf(fp, "%s", buf);
    T->name = strdup(buf);

    fscanf(fp, "%d", &T->nLODs);
    fscanf(fp, "%d %d", &T->tilesX, &T->tilesY);
    fscanf(fp, "%f %f %f", &T->scaleX, &T->scaleY, &T->scaleZ);

    fscanf(fp, "%s", buf);
    T->postFile = strdup(buf);
    fscanf(fp, "%d %d", &T->postSizeX, &T->postSizeY);   
    
    fscanf(fp, "%s", buf);
    T->imageFile = strdup(buf);
    fscanf(fp, "%d %d", &T->imageSizeX, &T->imageSizeY);   
        
    fscanf(fp, "%s", buf);
    T->detailTextureFile = strdup(buf);
    T->detailTexture = pfNewTex(pfGetSharedArena());
    pfLoadTexFile(T->detailTexture, T->detailTextureFile);
    
    if (fscanf(fp, "%d %d %d %d %d", 
		    &T->detProps[0], &T->detProps[1], &T->detProps[2], 
		    &T->detProps[3], &T->detProps[4]) != 5)
	for(i=0;i<5;i++)
	    T->detProps[i] = defaultDetProps[i];

    T->postTileSizeX = T->postSizeX/T->tilesX;
    T->postTileSizeY = T->postSizeY/T->tilesY;
    T->imageTileSizeX = T->imageSizeX/T->tilesX;
    T->imageTileSizeY = T->imageSizeY/T->tilesY;
    
    return T;
}

static void
MakeGStateList(TNode *T)
{
    int i;
    pfMaterial *material = NULL;
    pfTexture *texture = NULL;
    pfTexGen *texgen = NULL;

    if (state == NULL)
    {
	state = pfNewGState(pfGetSharedArena());

	material = pfNewMtl(pfGetSharedArena());
        pfMtlColor(material, PFMTL_AMBIENT,  0.1, 0.1, 0.1);
        pfMtlColor(material, PFMTL_SPECULAR, 0.8, 0.8, 0.8);
        pfMtlShininess(material, 10.0);
        if (pfdGetMesherMode(PFDMESH_SHOW_TSTRIPS))
            pfMtlColorMode(material, PFMTL_FRONT, PFMTL_CMODE_AD);
        else
            pfMtlColorMode(material, PFMTL_FRONT, PFMTL_CMODE_COLOR);
	pfGStateAttr(state, PFSTATE_FRONTMTL, material);
	pfGStateMode(state, PFSTATE_ENLIGHTING, PF_ON);

	texture = pfNewTex(pfGetSharedArena());  
        pfLoadTexFile(texture, T->imageFile);
	pfTexName(texture, T->imageFile); 
#ifdef USE_TEXGEN
#else
	pfTexRepeat(texture, PFTEX_WRAP_T, PFTEX_CLAMP);
	pfTexRepeat(texture, PFTEX_WRAP_S, PFTEX_CLAMP);
#endif
	pfGStateAttr(state, PFSTATE_TEXTURE, texture);
	pfGStateMode(state, PFSTATE_ENTEXTURE, PF_ON);

#ifdef USE_TEXGEN
        {
	float xScale = (1.0f-(T->tilesX-1)/(float)T->postSizeX)
				     / (T->scaleX*T->postSizeX);
	float yScale = (1.0f-(T->tilesY-1)/(float)T->postSizeY)
				     / (T->scaleY*T->postSizeY);
	texgen = pfNewTGen(pfGetSharedArena());
	pfTGenMode(texgen, PF_S, PFTG_OBJECT_LINEAR);
	pfTGenPlane(texgen, PF_S, xScale, 0.0, 0.0, 0.0);
	pfTGenMode(texgen, PF_T, PFTG_OBJECT_LINEAR);
	pfTGenPlane(texgen, PF_T, 0.0, yScale, 0.0, 0.0);
	pfGStateAttr(state, PFSTATE_TEXGEN, texgen);
	pfGStateMode(state, PFSTATE_ENTEXGEN, PF_ON);
        }
#endif
    }
}

static void
AttachLists(TNode *T)
{
    int		i, j;
    
    T->group = pfNewGroup();
   
    for(j=0;j<T->tilesX*T->tilesY;j++)
    {
	pfLOD *tileLOD = pfNewLOD();
	
	pfAddChild(T->group, tileLOD);
	for(i=0;i<T->nLODs;i++)
	{
	    int k;
	    pfGeode *geode = (pfGeode *)pfGet(T->geodeList, i*T->tilesX*T->tilesY + j);
	    
	    pfAddChild(tileLOD, geode);
	    for(k=0;k<pfGetNumGSets(geode);k++)
	    {
		pfGeoSet *gset = pfGetGSet(geode, k);
		pfGSetGState(gset, state);
	    }
	}
	{
 	    pfBox box;
	    pfVec3 center;
	    float size;
	    int k;
	    
	    pfLODRange(tileLOD, 0, 0);
	    pfuTravCalcBBox(pfGetChild(tileLOD, 1), &box);
	    size = 2*pfDistancePt3(box.min, box.max);
	    pfCombineVec3(center, 0.5f, box.min, 0.5f, box.max);
	    pfLODCenter(tileLOD, center);

	    for(k=1;k<T->nLODs+2;k++)
	    {
		pfLODRange(tileLOD, k, size*k);
	    }
	}
    }    
}

static void
MakeTextureList(TNode *T)
{
    int	thisLOD, i, j;
    
    T->texList = pfNewList(sizeof(pfTexture *), 
			T->nLODs*T->tilesX*T->tilesY, 
			pfGetSharedArena());
        
    for(thisLOD = 0; thisLOD < T->nLODs; thisLOD++)
    {
	int	    realTileSizeX = (int)(T->imageTileSizeX/pow(2, thisLOD));
	int	    realTileSizeY = (int)(T->imageTileSizeY/pow(2, thisLOD));
	
	if (realTileSizeX == 0) realTileSizeX = 1;
	if (realTileSizeY == 0) realTileSizeY = 1;

	for(j=0; j < T->tilesY; j++)
	{
	    for(i=0; i < T->tilesX; i++)
	    {
		pfTexture	*tex;
	        unsigned long	*img;
	        unsigned int	*newimg;
		char		*cimg, *newcimg;
		int		k;
		
		img = (unsigned long *)ilgettile(T->imageFile, 
				    (float)(i*(T->imageTileSizeX-1)), 
				    (float)(j*(T->imageTileSizeY-1)), 
				    T->imageTileSizeX, T->imageTileSizeY, 
				    realTileSizeX, realTileSizeY);
				    
		pfNotify(PFNFY_DEBUG, PFNFY_PRINT, 
		    "ITILE(%d,%d): <%.3f %.3f> to <%.3f %.3f> at <%d %d>", 
			thisLOD, 
			i+j*T->tilesX,
			(float)(i*(T->imageTileSizeX-1)),
			(float)(j*(T->imageTileSizeY-1)),
			(float)(i*(T->imageTileSizeX-1)) + (T->imageTileSizeX-1), 
			(float)(j*(T->imageTileSizeY-1)) + (T->imageTileSizeX-1), 
			realTileSizeX, realTileSizeY);
		
		newimg = (unsigned int *)pfMalloc(realTileSizeX*realTileSizeY*sizeof(unsigned int), 
				  pfGetSharedArena());
		newcimg = (char *)newimg;
		cimg = (char *)img;
		for(k=0;k<realTileSizeX*realTileSizeY;k++)
		{
		    newcimg[k*3+0] = cimg[k*4+1];
		    newcimg[k*3+1] = cimg[k*4+2];
		    newcimg[k*3+2] = cimg[k*4+3];
		}
		if (pfGetNotifyLevel() == 5)
		{
		    int k, l;
		    
		    for(l=0;l<realTileSizeY;l++)
		    {
			fprintf(stderr, "ROW %3d: ", l);
			for(k=0;k<realTileSizeX;k++)
			    fprintf(stderr, "%3d ", 
				cimg[l*realTileSizeX*4 + k*4+0] +
				cimg[l*realTileSizeX*4 + k*4+1] +
				cimg[l*realTileSizeX*4 + k*4+2]);
			fprintf(stderr, "\n");
		    }
		}
		
 		tex = pfNewTex(pfGetSharedArena());  
		pfTexName(tex, T->imageFile); 
		pfTexImage(tex, newimg, 3, realTileSizeX, realTileSizeY, 1);
		pfTexRepeat(tex, PFTEX_WRAP_T, PFTEX_CLAMP);
		pfTexRepeat(tex, PFTEX_WRAP_S, PFTEX_CLAMP);
		pfAdd(T->texList, tex);
	    }
	 }
    }
}

static void
MakeGeodeList(TNode *T)
{
    int	realTileSizeX,	realTileSizeY;
    int	thisLOD, i, j, offset;
    
    T->geodeList = pfNewList(sizeof(long), 
			    T->nLODs*T->tilesX*T->tilesY, 
			    pfGetSharedArena());
    
    for(thisLOD = 0; thisLOD < T->nLODs; thisLOD++)
    {
   	pfVec3		*myData, *vertical, *horizontal;
	int		rImageTileSizeX, rImageTileSizeY;
	
	rImageTileSizeX = (int)(T->imageTileSizeX/pow(2, thisLOD));
	rImageTileSizeY = (int)(T->imageTileSizeY/pow(2, thisLOD));
	if (rImageTileSizeX < 1) rImageTileSizeX = 1;
	if (rImageTileSizeY < 1) rImageTileSizeY = 1;
	
	realTileSizeX = (int)(T->postTileSizeX/pow(2, thisLOD))-1;
	realTileSizeY = (int)(T->postTileSizeY/pow(2, thisLOD))-1;
	if (realTileSizeX < 1) realTileSizeX = 3;
	if (realTileSizeY < 1) realTileSizeY = 3;
	
	if (thisLOD == 0)
	{
	    horizontal = (pfVec3 *)pfMalloc(T->tilesX * (T->tilesY+1) * realTileSizeX * sizeof(pfVec3), pfGetSharedArena());
	    vertical = (pfVec3 *)pfMalloc((T->tilesX+1) * T->tilesY * realTileSizeY * sizeof(pfVec3), pfGetSharedArena());
	}
	
	myData = (pfVec3 *) pfMalloc(realTileSizeX*realTileSizeY*sizeof(pfVec3), pfGetSharedArena());	
	
	offset = (thisLOD != 0);

	for(j=0; j < T->tilesY; j++)
	{
	    for(i=0; i < T->tilesX; i++)
	    {
		pfdGeoBuilder	*b;
	        unsigned short		*img;
		int		k, m, n;

		float noffX = .5f/(float)rImageTileSizeX;
		float noffY = .5f/(float)rImageTileSizeY;
		float offX = (1.0f-2*noffX)/(float)(realTileSizeX-1);
		float offY = (1.0f-2*noffY)/(float)(realTileSizeY-1);		
		float hroffX = (1.0f - 2*noffX)/(float)(T->postTileSizeX-1);
		float hroffY = (1.0f - 2*noffY)/(float)(T->postTileSizeY-1);
		
		b = pfdNewGeoBldr();
		pfNotify(PFNFY_DEBUG, PFNFY_PRINT, 
			"PTILE(%d,%d): <%.3f %.3f> to <%.3f %.3f> at <%d %d>", 
			thisLOD, i+j*T->tilesX,
			(float)i*(float)(T->postTileSizeX-1)+.5f,
			(float)j*(float)(T->postTileSizeY-1)+.5f,
			(float)i*(float)(T->postTileSizeX-1)+.5f+T->postTileSizeX-1, 
			(float)j*(float)(T->postTileSizeY-1)+.5f+T->postTileSizeY-1, 
			realTileSizeX, realTileSizeY);
			
		img = (unsigned short *)ilgettile(T->postFile, 
				    (float)i*(float)(T->postTileSizeX-2)+.5f, 
				    (float)j*(float)(T->postTileSizeY-2)+.5f, 
				    T->postTileSizeX-1, T->postTileSizeY-1, 
				    realTileSizeX, realTileSizeY);

		for(k = 0; k < realTileSizeY; k++)
		    for(m = 0; m < realTileSizeX; m++)
			pfSetVec3(myData[k*realTileSizeX + m], 
			    (float)m * T->scaleX * (T->imageTileSizeX-1)/(realTileSizeY-1) +
			    (float)i * T->scaleX * (T->imageTileSizeX - 1), 
			    (float)k * T->scaleY * (T->imageTileSizeY-1)/(realTileSizeY-1) +
			    (float)j * T->scaleY * (T->imageTileSizeY - 1),  
			    (float)img[(k*realTileSizeX + m)] * T->scaleZ);

		if (pfGetNotifyLevel() == 5)
		{
		    fprintf(stderr, "\n");
		    for(k = 0; k < realTileSizeY; k++)
		    {
			for(m = 0; m < realTileSizeX; m++)
			    fprintf(stderr, "%3d ", 
				    img[(k*realTileSizeX + m)]);
			fprintf(stderr, "\n");
		    }
		    fprintf(stderr, "\n");
		}
		
		if (thisLOD == 0)
		{
 		    for(k = 0; k < realTileSizeX; k++)
			pfCopyVec3( horizontal[(T->tilesX*j+i) * realTileSizeX + k], 
				    myData[k]);
		    for(k = 0; k < realTileSizeY; k++)
			pfCopyVec3( vertical[((T->tilesX+1)*j+i) * realTileSizeY + k], 
				    myData[k*realTileSizeX]);
		    if (j == (T->tilesY-1))
			for(k=0; k < realTileSizeX; k++)
			    pfCopyVec3( horizontal[(T->tilesX*(j+1)+i) * realTileSizeX + k], 
					myData[realTileSizeX*(realTileSizeY-1) + k]);
		    if (i == (T->tilesX-1))
			for(k=0; k < realTileSizeY; k++)
			    pfCopyVec3( vertical[((T->tilesX+1)*j+i+1) * realTileSizeY + k], 
					myData[realTileSizeX * (k+1) - 1]);
					
		}
		else
		{
			int cur = 1;
			for(k = 1; k < realTileSizeY-1; k++)
			{   
			    while( (((float *)vertical[((T->tilesX+1)*j+i)*(T->postTileSizeY-1)+cur])[1] <= 
				    ((float *)myData[k * realTileSizeX + 1])[1])
				    && (cur < (T->postTileSizeY-1)))
			    {
				MakeTri(b, 
					myData[k * realTileSizeX + 1], 
					vertical[((T->tilesX+1)*j+i)*(T->postTileSizeY-1)+cur], 
					vertical[((T->tilesX+1)*j+i)*(T->postTileSizeY-1)+cur-1], 
					offX + noffX, offY*k + noffY, 
					noffX, hroffY*cur + noffY, 
					noffX, hroffY*(cur-1) + noffY);					
				cur++;
			    }
			    if ((k+1)<realTileSizeY-1)
			    {
				MakeTri(b, 
					myData[k * realTileSizeX + 1], 
					myData[(k+1) * realTileSizeX + 1], 
					vertical[((T->tilesX+1)*j+i)*(T->postTileSizeY-1)+(cur-1)], 
					offX + noffX, offY*k + noffY, 
					offX + noffX, offY*(k+1) + noffY, 
					noffX, hroffY*(cur-1) + noffY);
			    }
			    else
			    {
				while(cur < (T->postTileSizeY-1))
				{
				    MakeTri(b, 
					myData[k * realTileSizeX + 1], 
					vertical[((T->tilesX+1)*j+i)*(T->postTileSizeY-1)+cur], 
					vertical[((T->tilesX+1)*j+i)*(T->postTileSizeY-1)+cur-1], 
					offX + noffX, offY*k + noffY, 
					noffX, hroffY*cur + noffY, 
					noffX, hroffY*(cur-1) + noffY);
				    cur++;  
				}
			    }
			}
			cur = 1;
			for(k = 1; k < realTileSizeX-1; k++)
			{   
			    while( (	((float *)horizontal[(T->tilesX*j+i)*(T->postTileSizeX-1)+cur])[0] <= 
					((float *)myData[k + realTileSizeX])[0])
					&& (cur < (T->postTileSizeX-1)))
			    {
				MakeTri(b, 
					horizontal[(T->tilesX*j+i)*(T->postTileSizeX-1)+cur-1], 
					horizontal[(T->tilesX*j+i)*(T->postTileSizeX-1)+cur], 
					myData[k + realTileSizeX], 
					(cur-1)*hroffX + noffX, noffY, 
					cur*hroffX + noffX, noffY, 
					k*offX + noffX, noffY + offY); 
				cur++;
			    }
			    if ((k+1)<realTileSizeX-1)
			    {
				MakeTri(b, 
					horizontal[(T->tilesX*j+i)*(T->postTileSizeX-1)+(cur-1)], 
					myData[(k + realTileSizeX+1)], 
					myData[k + realTileSizeX], 
					(cur-1)*hroffX + noffX, noffY, 
					(k+1)*offX + noffX, noffY + offY, 
					k*offX + noffX, noffY + offY);
			    }
			    else
			    {
				while(cur < (T->postTileSizeX-1))
				{
				    MakeTri(b, 
					horizontal[(T->tilesX*j+i)*(T->postTileSizeX-1)+cur-1], 
					horizontal[(T->tilesX*j+i)*(T->postTileSizeX-1)+cur], 
					myData[k + realTileSizeX], 
					(cur-1)*hroffX + noffX, noffY, 
					cur*hroffX + noffX, noffY, 
					k*offX + noffX, noffY + offY); 
				    cur++;  
				}
			    }
			}
			cur = 1;
			for(k = 1; k < realTileSizeX-1; k++)
			{   
			    int n = k + realTileSizeX*(realTileSizeY - 2);
			    while( (	((float *)horizontal[(T->tilesX*(j+1)+i)*(T->postTileSizeX-1)+cur])[0] <= 
					((float *)myData[n])[0]) 
					&& (cur < (T->postTileSizeX-1)))
			    {
				MakeTri(b, 
					myData[n], 
					horizontal[(T->tilesX*(j+1)+i)*(T->postTileSizeX-1)+cur], 
					horizontal[(T->tilesX*(j+1)+i)*(T->postTileSizeX-1)+cur-1], 
					k*offX + noffX, 1.0 - (noffY + offY), 
					cur*hroffX + noffX, 1.0 - noffY, 
					(cur-1)*hroffX + noffX, 1.0 - noffY);
				cur++;
			    }
			    if ((k+1)<realTileSizeX-1)
			    {
				MakeTri(b, 
					myData[n], 
					myData[n+1], 
					horizontal[(T->tilesX*(j+1)+i)*(T->postTileSizeX-1)+(cur-1)], 
					k*offX + noffX, 1.0 - (noffY + offY), 
					(k+1)*offX + noffX, 1.0 - (noffY + offY), 
					(cur-1)*hroffX + noffX, 1.0 - noffY); 
			    }
			    else
			    {
				while(cur < (T->postTileSizeX-1))
				{
				    MakeTri(b, 
					myData[n], 
					horizontal[(T->tilesX*(j+1)+i)*(T->postTileSizeX-1)+cur], 
					horizontal[(T->tilesX*(j+1)+i)*(T->postTileSizeX-1)+cur-1], 
					k*offX + noffX, 1.0 - (noffY + offY), 
					cur*hroffX + noffX, 1.0 - noffY, 
					(cur-1)*hroffX + noffX, 1.0 - noffY); 
				    cur++;  
				}
			    }
			}
			cur = 1;
			for(k = 1; k < realTileSizeY-1; k++)
			{   
			    int n = (k+1) * realTileSizeX - 2;
			    while( (	((float *)vertical[((T->tilesX+1)*j+i+1)*(T->postTileSizeY-1)+cur])[1] <= 
					((float *)myData[k * realTileSizeX + 1])[1])
					&& (cur < (T->postTileSizeY-1)))
			    {
				MakeTri(b, 
					vertical[((T->tilesX+1)*j+i+1)*(T->postTileSizeY-1)+cur-1], 
					vertical[((T->tilesX+1)*j+i+1)*(T->postTileSizeY-1)+cur], 
					myData[n], 
					1.0 - noffX, hroffY*(cur-1) + noffY, 
					1.0 - noffX, hroffY*cur + noffY, 
					1.0 - (offX + noffX), offY*k + noffY);
				cur++;
			    }
			    if ((k+1)<realTileSizeY-1)
			    {
				MakeTri(b, 
					vertical[((T->tilesX+1)*j+i+1)*(T->postTileSizeY-1)+(cur-1)], 
					myData[n+realTileSizeX], 
					myData[n], 
					1.0 - noffX, hroffY*(cur-1) + noffY, 
					1.0 - (offX + noffX), offY*(k+1) + noffY, 
					1.0 - (offX + noffX), offY*k + noffY);
			    }
			    else
			    {
				while(cur < (T->postTileSizeY-1))
				{
				    MakeTri(b, 
					vertical[((T->tilesX+1)*j+i+1)*(T->postTileSizeY-1)+cur-1], 
					vertical[((T->tilesX+1)*j+i+1)*(T->postTileSizeY-1)+cur], 
					myData[n], 
					1.0 - noffX, hroffY*(cur-1) + noffY, 
					1.0 - noffX, hroffY*cur + noffY, 
					1.0 - (offX + noffX), offY*k + noffY); 
				    cur++;  
				}
			    }
			}

		}
		
		for(k = offset; k < realTileSizeY - offset - 1; k++)
		{
		    for(m = offset; m < realTileSizeX - offset - 1; m++)
		    {
			MakeQuad(   b, 
				    myData[(k * realTileSizeX + m)], 
				    myData[(k * realTileSizeX + m + 1)], 
				    myData[((k + 1) * realTileSizeX + m + 1)], 
				    myData[((k + 1) * realTileSizeX + m)], 
				    offX*(m+0) + noffX, offY*(k+0) + noffY, 
				    offX*(m+1) + noffX,	offY*(k+0) + noffY, 
				    offX*(m+1) + noffX, offY*(k+1) + noffY, 
				    offX*(m+0) + noffX, offY*(k+1) + noffY);
		    }
		}
		{
		    int ii;
		    
		    pfGeode *newGeode = pfNewGeode();
		    const pfList *tmpList = pfdBuildGSets(b);

		    n = pfGetNum(tmpList);
		    for(ii=0;ii<n;ii++)
		    {
			pfAddGSet(newGeode, (pfGeoSet *)pfGet(tmpList, ii));
		    }
		    pfAdd(T->geodeList, newGeode);
		    pfdDelGeoBldr(b);
		}
	    }
	}
	pfFree(myData);
	if (thisLOD == (T->nLODs - 1))
	{
	    pfFree(vertical);
	    pfFree(horizontal);
	}
    }
}

#ifdef USE_DETAIL
	if (i < 3*T->tilesX*T->tilesY)
	{
	    static float	detClamp = 0.75f;
	    static pfVec2	detSpline[4];
	    pfSetVec2(detSpline[0], 0.0f, .5*0.0f*T->scaleY);
	    pfSetVec2(detSpline[1], 2.0f, .5*0.3f*T->scaleY);
	    pfSetVec2(detSpline[2], 4.0f, .5*0.6f*T->scaleY);
	    pfSetVec2(detSpline[3], 6.0f, .5*2.0f*T->scaleY);
/*	    pfTexDetail(pfGet(T->texList, i), PFTEX_DEFAULT, T->detailTexture);*/
	    pfTexDetail(pfGet(T->texList,i), 6-(log((float)T->detProps[3])/M_LN2), T->detailTexture);
	    pfTexSpline(pfGet(T->texList, i), PFTEX_DETAIL_SPLINE, detSpline, detClamp);
    	    pfTexFilter(pfGet(T->texList,i), PFTEX_MAGFILTER_COLOR, PFTEX_ADD_DETAIL);
       /* 
     * XXX !!! need to use 
     *	level = 6 - (log((float) T->detProps[3]) / M_LN2);
     *	pfDetailTex(baseTex, level, T->detailTexture) 
     */
/*    	    pfDetailTexTile(	T->detailTexture, T->detProps[0], T->detProps[1], 
				T->detProps[2], T->detProps[3], T->detProps[4]);*/
	    

	}
#endif