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

File: [Development] / performer / src / lib / libpfdb / libpfflt11 / pfflt11_geom.c (download)

Revision 1.1, Tue Nov 21 21:39:33 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 1992, 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
 */

/*
 * pfflt_geom.c $Revision: 1.1 $ 
 * 
 * Version 11 .flt file converter.
*/
#include <stdio.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h> 
#include <varargs.h>
#include <math.h>
#include <Performer/pf.h>
#include <Performer/pfdu.h>
#include <Performer/pfdb/pfflt11.h>

#ifdef	_POSIX_SOURCE
extern int strncasecmp (const char *s1, const char *s2, size_t n);
#endif

#include <string.h>

#define VLISTSIZE	2048
#define MAXTERMS	16		

static char 		terms[MAXTERMS][80];  /* for comments */

static pfVec3	*LPointVertList;
static int	 LPVertSize, LPVertCount;

typedef struct _MtlLink
{
    struct _MtlLink	*next;
    pfMaterial		*pfmtl;

} MtlLink;

static LightPoint   *LightPointList;
static MtlLink      **SharedMtlList = NULL;
static int	    SharedMtlCount = 0;
static pfMaterial   *DefaultMtl = NULL;
static pfTexture    **TexList = NULL;
static pfTexEnv	    *ModTEnv = NULL;

/*
 * It is crucial to share textures between .flt files so these lists
 * store textures for sharing.
*/
static pfTexture    *SharedTexList[MG_MAX_TEXTURES];   /* XXX - should grow */
static pfTexEnv	    *SharedTevList[MG_MAX_TEXTURES];    /* XXX - should grow */
static int	    SharedTexCount = 0;

/* Database counters */
static int	    PolyCount[257], ConcaveCount;
static int	    OmniCount, DirCount;
static int	    GSetCount, GStateCount;
static int	    StripCount, StripTriCount, TriCount, QuadCount;
static int	    TexCount = 0;
static int	    TotalTris = 0;

static int	    GfxType;
static void	    *Arena;

static int	    getTexture(int);
static pfMaterial*  getMaterial(GeoState *gs);
static int	    addLPVert(pfVec3 v);
static char*	    addLPoint(mgPolygon *);
static pfNode*	    makeLPoint(void);
static pfNode*	    makeGeode(void);
static pfGeoState*  makeGState(GeoState *gstate);
static int	    parseComment(mgComment *);

#define	GFX_REALITY	0x1
#define	GFX_MULTISAMPLE	0x10

    /*------------------------------------------------------------*/


/*
 * Initialize lists and counters.
*/    
void
GeomInit11(void)
{
    int    	i;
    char	gv[32];

    /* Get Performer shared memory arena */
    Arena = pfGetSharedArena();

    /* Determine graphics configuration */
    GfxType = 0;
    {
	GfxType |= GFX_REALITY;
	GfxType |= GFX_MULTISAMPLE;
    }

    LPointVertList = (pfVec3 *) pfMalloc(sizeof(pfVec3) * VLISTSIZE, NULL);
    LPVertSize = VLISTSIZE;

    if(DefaultMtl == NULL)
    {
	DefaultMtl = pfNewMtl(Arena);
	pfMtlColorMode(DefaultMtl, PFMTL_FRONT, PFMTL_CMODE_AD);
	pfRef(DefaultMtl);
    }

    if(SharedMtlList == NULL)
	SharedMtlList = (MtlLink**)pfCalloc(64, sizeof(MtlLink*), NULL);

    /* 
     * Initialize counters 
    */

    StripCount = StripTriCount = TriCount = QuadCount = ConcaveCount = 0;

    GStateCount = GSetCount = 0;
    OmniCount = DirCount = 0;

    for (i = 0; i < 257; i++)
   	PolyCount[i] = 0;
    LightPointList = NULL;

    /* Allocate MODULATE texture environment which is most common. */
    if(ModTEnv == NULL)
    {
	ModTEnv = pfNewTEnv(Arena);
	pfRef(ModTEnv);
    }
}


/*
 * Clean up and print out statistics
*/
void
GeomExit11(void)
{
    int    numTris, i;
    MgFile  *mgf;

    /* Create list of Performer textures used. */
    TexCount = 0;
    mgf = RootMgFile11;
    while(mgf)
    {
	TexCount += mgf->pfTexCount;	
	mgf = mgf->next;	
    }

    if(TexList)
	pfFree(TexList);
    TexList = (pfTexture**)
	pfMalloc((unsigned int)(sizeof(pfTexture*)*TexCount), NULL);
    
    mgf = RootMgFile11;
    TexCount = 0;
    while(mgf)
    {
	for(i=0; i<mgf->pfTexCount; i++)
	    TexList[TexCount++] = mgf->pfTexList[i];
	mgf = mgf->next;	
    }

    if(RootMgFile11->root)
    {
	/*
	 * Print out statistics.
	*/
	if(pfGetNotifyLevel() >= PFNFY_INFO)
	{
	    pfNotify(PFNFY_INFO, PFNFY_PRINT,
		"FLIGHT Geometry statistics : \n");

	    numTris = 0;
	    for (i = 0; i < 257; i++)
		if (PolyCount[i] != 0)
		{
		    pfNotify(PFNFY_INFO, PFNFY_MORE, "\t%ld-sided: %ld\n",
			i, PolyCount[i]);
		    if(i > 2)
			numTris += PolyCount[i] * (i - 2);
		}

	    pfNotify(PFNFY_INFO, PFNFY_MORE, "  Concave: %ld\n", ConcaveCount);
	    pfNotify(PFNFY_INFO, PFNFY_MORE, "  Triangles: %ld\n", numTris);

	    pfNotify(PFNFY_INFO, PFNFY_MORE, "  Performer Geometry:\n");
	    pfNotify(PFNFY_INFO, PFNFY_MORE, "    TexCount = %ld, "
		"SharedMtlCount = %ld\n", TexCount, SharedMtlCount);

	    numTris = TriCount + QuadCount*2 + StripTriCount;
	    TotalTris += numTris;

	    pfNotify(PFNFY_INFO, PFNFY_MORE, "    Light Points: Omni = %d, "
		"Dir = %d\n", OmniCount, DirCount);

	    pfNotify(PFNFY_INFO, PFNFY_MORE, "    GeoStates = %ld, "
		"GeoSets = %ld\n", GStateCount, GSetCount);

	    if(GSetCount > 0.)
		pfNotify(PFNFY_INFO, PFNFY_MORE, "    Triangles/GeoSet "
		    "= %.2f\n", (float)numTris / (float)GSetCount);

	    pfNotify(PFNFY_INFO, PFNFY_MORE, "    Tris = %ld, Quads = %ld,\n", 
		TriCount, QuadCount);

	    pfNotify(PFNFY_INFO, PFNFY_MORE, "    Strips = %ld, Stripped "
		"Tris = %ld\n", StripCount, StripTriCount);

	    if(StripCount > 0.)
		pfNotify(PFNFY_INFO, PFNFY_MORE, "    Tris/Strip = %.2f\n", 
		    (float)StripTriCount / (float)StripCount);

	    pfNotify(PFNFY_INFO, PFNFY_MORE, "    Total Triangles: %ld\n",
		TotalTris);

	}
    }

    /* Free vertex buffers. */
    pfFree(LPointVertList);
}


/*
 * Return Performer material corresponding to FLIGHT material and
 * create new pfMaterial if necessary. This routine uses lmcolor mode
 * PFMTL_CMODE_AD to reduce number of materials needed.
*/
static pfMaterial*
getMaterial(GeoState *gs)
{
    int		mid;

    if (CurMgFile11->mgMtlList == NULL || gs->material == -1)
    {
	gs->material = -1;	/* XXX - default to std material */
	return DefaultMtl;
    }

    mid = gs->material;

    if(CurMgFile11->pfMtlList[mid] == NULL)
    {
	MtlLink		*mlink;
	pfMaterial	*pfm;
	mgMaterial	*mgm = &CurMgFile11->mgMtlList[mid];
	int		emissive;

	emissive = (mgm->emission[0] + mgm->emission[1] + mgm->emission[2]) > 
		   (mgm->diffuse[0] + mgm->diffuse[1] + mgm->diffuse[2]);

	/* Search for material on global list */
	mlink = SharedMtlList[mid];
	while(mlink)
	{
	    pfVec3	clr;

	    pfm = mlink->pfmtl;
	
	    /* Compare specular, emissive but ignore diffuse and ambient */
	
	    if (emissive)
	    {
		pfGetMtlColor(pfm, PFMTL_EMISSION, clr, clr+1, clr+2);
		if(!PFALMOST_EQUAL_VEC3(clr, mgm->emission, .01f))
		{
		    mlink = mlink->next;
		    continue;
		}
	    }
	    pfGetMtlColor(pfm, PFMTL_SPECULAR,  clr, clr+1, clr+2);
	    if(!PFALMOST_EQUAL_VEC3(clr, mgm->specular, .01f))
	    {
		mlink = mlink->next;
		continue;
	    }
	    if(pfGetMtlAlpha(pfm) != mgm->alpha)
	    {
		mlink = mlink->next;
		continue;
	    }
	    if(pfGetMtlShininess(pfm) != mgm->shininess)
	    {
		mlink = mlink->next;
		continue;
	    }
	    break;
	}

	if(mlink == NULL)
	{
	    mlink = (MtlLink*) pfMalloc(sizeof(MtlLink), NULL);
    	    CurMgFile11->pfMtlList[mid] = pfm = mlink->pfmtl = pfNewMtl(Arena);
	    pfRef(pfm);
	    SharedMtlCount++;

	    mlink->next = SharedMtlList[mid];
	    SharedMtlList[mid] = mlink;


    	    pfMtlColor(pfm, PFMTL_AMBIENT,  mgm->ambient[0],
				       	mgm->ambient[1], 
					mgm->ambient[2]);

    	    pfMtlColor(pfm, PFMTL_SPECULAR, mgm->specular[0],
				        mgm->specular[1],
				        mgm->specular[2]);

    	    pfMtlColor(pfm, PFMTL_EMISSION, mgm->emission[0],
				        mgm->emission[1],
				        mgm->emission[2]);

    	    pfMtlColor(pfm, PFMTL_DIFFUSE, mgm->diffuse[0],
				        mgm->diffuse[1],
				        mgm->diffuse[2]);

    	    pfMtlShininess(pfm, mgm->shininess);
    	    pfMtlAlpha(pfm, mgm->alpha);

	    /* 
	     * If material is not emissive we use lmcolor() mode to 
	     * change diffuse and ambient colors. Note that this will
	     * make ambient == diffuse color so the material may
	     * not look the same as it would in MultiGen.
	    */
	    if (!emissive)
		pfMtlColorMode(pfm, PFMTL_FRONT, PFMTL_CMODE_AD);
	    else
	    	pfMtlColorMode(pfm, PFMTL_FRONT, PFMTL_CMODE_COLOR);

        }
	else 
	    CurMgFile11->pfMtlList[mid] = mlink->pfmtl;
    }

    return CurMgFile11->pfMtlList[mid];
}


/* 
 * Return Performer texture corresponding to FLIGHT texture and 
 * create new pfTexture if necessary. Textures are shared between .flt
 * files by texture file name.
*/
static int
getTexture(int id)
{
    int             i;
    mgTextureRef   *tex;
    pfTexture      *pfTex;
    pfTexEnv       *pfTev;
    char	    attrFile[PF_MAXSTRING], path[PF_MAXSTRING];
    int             ifd;
    struct stat     stbuf;
    MgFile	    *cmf = CurMgFile11;

    if (id < 0)
	return id;

    if(cmf->pfTexIndex[id] >= 0)
	return cmf->pfTexIndex[id];
    else if(cmf->pfTexIndex[id] == -3)	/* -3 means texture not found */
	return -1;	  
    
    /* Find texture with index == id */
    for (i = 0; i < cmf->mgTexCount; i++)
    {
	if (id == cmf->mgTexList[i]->index)
	    break;
    }

    if (i == cmf->mgTexCount)
	return -1;

    tex = cmf->mgTexList[i];

    /* See if we can find texture file */
    if (!pfFindFile(tex->file, path, R_OK))
    {
	pfNotify(PFNFY_NOTICE, PFNFY_PRINT, 
			"Could not open texture file %s.", tex->file );
	cmf->pfTexIndex[id] = -3;  /* -3 means texture not found */
	return -1;
    }

    /* See if texture was already loaded by another file */
    for (i = 0; i < SharedTexCount; i++)
    {
	const char *str =  pfGetTexName(SharedTexList[i]);
	if (!str)
	    continue;

	if(!strcmp(path, str))
	{
	    cmf->pfTexList[cmf->pfTexCount] = SharedTexList[i];
	    cmf->pfTEnvList[cmf->pfTexCount] = SharedTevList[i];
	    cmf->pfTexIndex[id] = cmf->pfTexCount;
	    cmf->pfTexCount++;
	    return cmf->pfTexIndex[id];
	}	    	    	
    }
    
    /* Could not share texture */
    pfTex = cmf->pfTexList[cmf->pfTexCount] = pfNewTex(Arena);
    pfRef(pfTex);
    cmf->pfTexIndex[id] = cmf->pfTexCount;
    pfLoadTexFile(pfTex, path);
    if(pfGetNotifyLevel() >= PFNFY_INFO)
    {
	int	comp, x, y, z;
   	unsigned int	*image;

	pfGetTexImage(pfTex, &image, &comp, &x, &y, &z);

	pfNotify(PFNFY_DEBUG, PFNFY_PRINT, "Loaded texture %s: %d %d %d %d\n",
			tex->file, comp, x, y, z);
    }
    
    /* Read texture attribute file */
    sprintf(attrFile, "%s.attr", tex->file);
    if (pfFindFile(attrFile, path, R_OK) &&
       (ifd = open(path, O_RDONLY)) >= 0 &&
       fstat(ifd, &stbuf) == 0)
    {
	static long minf[] = { PFTEX_POINT, PFTEX_BILINEAR, PFTEX_MIPMAP, 
                               PFTEX_MIPMAP_POINT, PFTEX_MIPMAP_LINEAR, 
                               PFTEX_MIPMAP_BILINEAR, PFTEX_MIPMAP_TRILINEAR};
	static long magf[] = { PFTEX_POINT, PFTEX_BILINEAR }; 
	static long w[] =   {PFTEX_REPEAT, PFTEX_CLAMP, PFTEX_SELECT};
    	static long env[] = {PFTE_MODULATE, PFTE_BLEND, PFTE_DECAL};
	int comp;
	unsigned int *foo;

	mgTextureAttr	    texAttr;

	/* read the texture attribute file */
	read(ifd, (char*)&texAttr, sizeof(texAttr));
	close(ifd);

	pfTexFilter(pfTex, PFTEX_MAGFILTER, magf[texAttr.magfilter]);
	if(texAttr.wrap >= 0 && texAttr.wrap < 3)
	    pfTexRepeat(pfTex, PFTEX_WRAP, w[texAttr.wrap]);
	if(texAttr.wrapu >= 0 && texAttr.wrapu < 3)
	    pfTexRepeat(pfTex, PFTEX_WRAP_S, w[texAttr.wrapu]);
	if(texAttr.wrapv >= 0 && texAttr.wrapv < 3)
	    pfTexRepeat(pfTex, PFTEX_WRAP_T, w[texAttr.wrapv]);

	if(env[texAttr.envtype] == PFTE_MODULATE || texAttr.envtype < 0 ||
	   texAttr.envtype > 2)
	    pfTev = ModTEnv;
	else
	{
	    pfTev = pfNewTEnv(Arena);
    	    pfRef(pfTev);
	    pfTEnvMode(pfTev, env[texAttr.envtype]);
	}

	/* XXX texture environment color not supported */ 
    }    
    else
    {
    	pfNotify(PFNFY_WARN,PFNFY_RESOURCE, "Could not load "
	    "texure attribute file \"%s\"", attrFile);
	pfTev = ModTEnv;
    }

    cmf->pfTEnvList[cmf->pfTexCount] = pfTev;
    SharedTexList[SharedTexCount] = pfTex;
    SharedTevList[SharedTexCount] = pfTev;
    SharedTexCount++;

    cmf->pfTexCount++;
    return cmf->pfTexIndex[id];
}

/*
 * If a polygon is flagged as a light string, its vertices are light 
 * points so add them to a special list.
*/
static int
addLPVert(pfVec3 v)
{
    if (LPVertCount >= LPVertSize)
    {
	LPVertSize <<= 1;
	LPointVertList = (pfVec3 *) pfRealloc(LPointVertList,
	    (unsigned int)(LPVertSize * sizeof(pfVec3)));
    }
    LPVertCount++;
    PFCOPY_VEC3(LPointVertList[LPVertCount-1], v);
    return(LPVertCount - 1);
}


/*
 * The FLIGHT polygon is flagged as a light string so add it to a 
 * special list.
*/
static char*
addLPoint(mgPolygon * mgPoly)
{
    int           coms;
    int           numPoints; 
    LightPoint     *newLPoint, *lp;
    mgVertAbs      *aVert;
    register char  *ptr;
    int	   depth=0, done=0;
    pfVec3	   vert, vert2;
    mgComment	   *comment = NULL;
    mgMatrix	   *matrix = NULL;
    mgReplicate	   *replicate = NULL;
    mgVect	   *vector = NULL;
    mgBead	   *bead;

    /*
     * count points 
     */
    numPoints = 0;
    ptr = (char*)mgPoly + mgPoly->length;
    while(!done)
    {
	bead = (mgBead*)ptr;
	switch (bead->opcode)
	{
	case MG_VERT_ABS:
	case MG_VERT_SHAD:
	case MG_VERT_NORM:
	    if(matrix && replicate)
		numPoints += replicate->count + 1;
	    else
		numPoints++;
	    break;
	case MG_PUSH:
	    depth++;
	    break;
	case MG_POP:
	    depth--;
	    if(depth <= 0)
		done = 1;
	    break;
	case MG_VECTOR:
	    vector = (mgVect*)bead;
	    break;
	case MG_MATRIX:
	    matrix = (mgMatrix*)bead;
	    break;
	case MG_REPLICATE:
	    replicate = (mgReplicate*)bead;
	    break;
	case MG_COMMENT:
	    comment = (mgComment*)bead;
	    break;
	default:
	    if (bead->opcode < MG_NUM_OPCODES)
		pfNotify(PFNFY_WARN,PFNFY_PRINT, 
		    "LoadFlt() Unsupported bead %ld \"%s\" found in polygon.",
		    bead->opcode,
		    OpcodeNames11[bead->opcode]);
	    else
		pfNotify(PFNFY_WARN,PFNFY_PRINT, 
		    "LoadFlt() Unsupported bead %ld found in polygon.",
		    bead->opcode);
	    break;
	}
        ptr += bead->length;
    }

    /*
     * create the the lightpoint structure 
     */
    newLPoint = (LightPoint *) pfMalloc(sizeof(LightPoint), NULL);
    newLPoint->indexList = (int *) pfMalloc((unsigned int)(sizeof(int) * numPoints), NULL);
    newLPoint->numPoints = numPoints;
    newLPoint->next = NULL;
    newLPoint->set = -1;	   /* for sorting in makeLPoint() */
    newLPoint->name[0] = 0;
    numPoints = 0;

    /* Convert color */
    MG2PF_COLOR3(newLPoint->color, mgPoly->color1);

    newLPoint->color[3] = 1.0f;	   /* Set alpha */

    /*
     * Count points 
    */
    ptr = (char*)mgPoly + mgPoly->length;
    done = 0;
    while(!done)
    {
	bead = (mgBead*)ptr;
	switch (bead->opcode)
	{
	case MG_VERT_ABS:
	case MG_VERT_SHAD:
	case MG_VERT_NORM:
	    aVert = (mgVertAbs *) bead;
	    vert[0] = aVert->v[0];
	    vert[1] = aVert->v[1];
	    vert[2] = aVert->v[2];
	    if(matrix && replicate)
	    {
		int i;
 		for(i=0; i<replicate->count+1; i++)
		{
		    MG2PF_COORD3(vert2, vert);
		    newLPoint->indexList[numPoints] = addLPVert(vert2);
		    pfXformPt3(vert, vert, matrix->matrix);
		    /* matrix translate values are also scaled. #?$-@! */
		    numPoints++;
		}
	    }
	    else
	    {
	        if(matrix)
		    pfXformPt3(vert, vert, matrix->matrix);
		MG2PF_COORD3(vert2, vert);
		newLPoint->indexList[numPoints] = addLPVert(vert2);
		numPoints++;
	    }
	    break;
	case MG_PUSH:
	    depth++;
	    break;
	case MG_POP:
	    depth--;
	    if(depth <= 0)
		done = 1;
	    break;
	case MG_VECTOR:
	case MG_MATRIX:
	case MG_REPLICATE:
	case MG_COMMENT:
	    coms = parseComment(comment);
	    break;
	}
        ptr += bead->length;
    }

    /* get the name if any */
    if(coms)
	strcpy(newLPoint->name, terms[0]);

    /* 
     * set the direction vector and light type. some files 
     * have a bug here, so check vector for each type.
     */
    switch(mgPoly->drawType)
    {
	case 8:		/* omni-direction light */
	    newLPoint->lightType = PFLP_OMNIDIRECTIONAL;
	    break;
	case 9:		/* uni-directional light */
	    if(vector == NULL)
		newLPoint->lightType = PFLP_OMNIDIRECTIONAL;
	    else
	    {
		newLPoint->lightType = PFLP_UNIDIRECTIONAL;
		MG2PF_NORM3(newLPoint->vector, vector->vec);
	    }
	    break;
	case 10:	/* bi-directional light */
	    if(vector == NULL)
		newLPoint->lightType = PFLP_OMNIDIRECTIONAL;
	    else
	    {
		newLPoint->lightType = PFLP_BIDIRECTIONAL;
		MG2PF_NORM3(newLPoint->vector, vector->vec);
	    }
	    break;
    }
    if(newLPoint->lightType == PFLP_OMNIDIRECTIONAL)
  	OmniCount += numPoints;
    else
  	DirCount += numPoints;

    /* Prepend new LightPoint */
    lp = LightPointList;
    LightPointList = newLPoint;
    LightPointList->next = lp;

    return ptr;
}


/*
 * Add FLIGHT polygon to pfdGeoBuilder. Polygons are sorted by 
 * texture/material/backface into GeoStates. pfGeoSets are generated by 
 * the pfdGeoBuilder utility.
*/
char*
AddPoly11(mgPolygon * mgPoly)
{
    GeoState        *gstate;
    int            vertType;
    int             i, j;
    register char   *ptr;
    int	    depth=0, done=0, mixed;
    mgComment	    *comment = NULL;
    mgMatrix	    *matrix = NULL;
    mgReplicate	    *replicate = NULL;
    mgBead	    *bead;
    pfdGeom	    polygon, *pb;
    int		    t;

    pb = pfdNewGeom(4096);
    polygon = *pb;
    /* if it's a light point, create points */
    if(mgPoly->drawType >= 8 && mgPoly->drawType <= 10)
	return(addLPoint(mgPoly));

    polygon.numVerts = 0;
    polygon.flags = 0;
    vertType = 0;
    mixed = 0;

    /* 
     * Check for mixed vertex types. If a polygon has mixed vertex types
     * we default all vertices to MG_VERT_ABS.
    */
    ptr = (char*)mgPoly + mgPoly->length;
    while(!done)
    {
	/* Determine if vertex is textured by length of record */
	bead = (mgBead*)ptr;
	switch (bead->opcode)
	{
	case MG_VERT_ABS:

	    MG2PF_COORD3(polygon.coords[polygon.numVerts], ((mgVertAbs*)bead)->v);
	    if (bead->length > 16)
	    {
		bead->opcode |= MG_VERT_TEX;
		pfCopyVec2(polygon.texCoords[0][polygon.numVerts], ((mgVertAbs*)bead)->t);
	    }
	    polygon.numVerts++;

	    if(vertType == 0)
	    	vertType = bead->opcode;
	    else if(vertType != bead->opcode)
		mixed = 1;

	    break;
	case MG_VERT_SHAD:

	    MG2PF_COORD3(polygon.coords[polygon.numVerts], ((mgVertShad*)bead)->v);
	    MG2PF_COLOR3(polygon.colors[polygon.numVerts], ((mgVertShad*)bead)->color);
	    if (bead->length > 20)
	    {
		bead->opcode |= MG_VERT_TEX;
		pfCopyVec2(polygon.texCoords[0][polygon.numVerts], ((mgVertShad*)bead)->t);
	    }
	    polygon.numVerts++;

	    if(vertType == 0)
	    	vertType = bead->opcode;
	    else if(vertType != bead->opcode)
		mixed = 1;

	    break;
	case MG_VERT_NORM:

	    MG2PF_COORD3(polygon.coords[polygon.numVerts], ((mgVertNorm*)bead)->v);
	    MG2PF_NORM3(polygon.norms[polygon.numVerts], ((mgVertNorm*)bead)->n);
	    if (bead->length > 32)
	    {
		bead->opcode |= MG_VERT_TEX;
		pfCopyVec2(polygon.texCoords[0][polygon.numVerts], ((mgVertNorm*)bead)->t);
	    }
	    polygon.numVerts++;

	    if(vertType == 0)
	    	vertType = bead->opcode;
	    else if(vertType != bead->opcode)
		mixed = 1;

	    break;
	case MG_PUSH:
	    depth++;
	    break;
	case MG_POP:
	    depth--;
	    if(depth <= 0)
		done = 1;
	    break;
	case MG_MATRIX:
	    matrix = (mgMatrix*)bead;
	    break;
	case MG_REPLICATE:
	    replicate = (mgReplicate*)bead;
	    break;
	case MG_COMMENT:
	    comment = (mgComment*)bead;
	    break;
	case MG_POLYGON:
	    pfNotify(PFNFY_WARN,PFNFY_PRINT, 
		    "LoadFlt() Unsupported bead %ld \"%s\" found in polygon.",
		    bead->opcode,
		    OpcodeNames11[bead->opcode]);
	    return ptr;
	default:
	    if (bead->opcode < MG_NUM_OPCODES)
		pfNotify(PFNFY_WARN,PFNFY_PRINT, 
		    "LoadFlt() Unsupported bead %ld \"%s\" found in polygon.",
		    bead->opcode,
		    OpcodeNames11[bead->opcode]);
	    else
		pfNotify(PFNFY_WARN,PFNFY_PRINT, 
		    "LoadFlt() Unsupported bead %ld found in polygon.",
		    bead->opcode);
	    break;
	}

        ptr += bead->length;
    }

    if (polygon.numVerts < 3)	/* Discard bad polygon */
	return ptr;

    /* Set mixed flag which will cause polygon to be flat-shaded */
    if (FlatFlag11 && (vertType & ~MG_VERT_TEX) == MG_VERT_NORM)
	mixed = 1;

    /* 
     * Default to ABS vert if vertices are mixed type. These polys will be
     * flat-shaded. 
    */
    if (mixed)
    {
	if (vertType & MG_VERT_TEX)
	    vertType = MG_VERT_ABS | MG_VERT_TEX;
	else
	    vertType = MG_VERT_ABS;
    }

    switch (vertType & ~MG_VERT_TEX) {
    case MG_VERT_ABS:
	polygon.nbind = PFGS_OFF;
	polygon.cbind = PFGS_PER_PRIM;
	MG2PF_COLOR3(polygon.colors[0], mgPoly->color1);
	break;
    case MG_VERT_SHAD:
	polygon.nbind = PFGS_OFF;
	polygon.cbind = PFGS_PER_VERTEX;
	break;
    case MG_VERT_NORM:
	polygon.nbind = PFGS_PER_VERTEX;
	polygon.cbind = PFGS_PER_PRIM;
	MG2PF_COLOR3(polygon.colors[0], mgPoly->color1);
	break;
    }
    if (vertType & MG_VERT_TEX)
    {
	polygon.tbind[0] = PFGS_PER_VERTEX;
	for (t = 1 ; t < PF_MAX_TEXTURES ; t ++)
	    polygon.tbind[t] = PFGS_OFF;
	mgPoly->texture = (short)getTexture(mgPoly->texture);
    }
    else
    {
	for (t = 0 ; t < PF_MAX_TEXTURES ; t ++)
	    polygon.tbind[t] = PFGS_OFF;
	mgPoly->texture = -1;
    }

    /* FLIGHT requires multiplying polygon color by material diffuse */
    if (CurMgFile11->mgMtlList != NULL && mgPoly->material >= 0)
    {
	mgMaterial	*mgm = &CurMgFile11->mgMtlList[mgPoly->material];

    	polygon.colors[0][0] *= mgm->diffuse[0];
    	polygon.colors[0][1] *= mgm->diffuse[1];
    	polygon.colors[0][2] *= mgm->diffuse[2];
	polygon.colors[0][3] = mgm->alpha;
    }

    /* Find the geostate list to add the polygon to */
    for (gstate = CurMgFile11->gsList; gstate != NULL; gstate = gstate->next)
    {
	if (mgPoly->material == gstate->material && 
	    mgPoly->texture == gstate->texture &&
	    (mgPoly->drawType == 0) == gstate->backFace)
	{
	    break;
	}
    }

    /* Prepend new GeoState */
    if (gstate == NULL)
    {
	gstate = CurMgFile11->gsList;
	CurMgFile11->gsList = (GeoState *) pfMalloc(sizeof(GeoState), NULL);
	CurMgFile11->gsList->next = gstate;
	CurMgFile11->gsList->pfgs = NULL;
	gstate = CurMgFile11->gsList;
	gstate->material = mgPoly->material;
	gstate->texture = mgPoly->texture;
	gstate->backFace = (mgPoly->drawType == 0);
	gstate->bbuilder = NULL;
	gstate->builder = pfdNewGeoBldr();
	pfdGeoBldrMode(gstate->builder, PFDBLDR_AUTO_NORMALS, ComputeNormals11);
    }

    /* Add mgPoly to billboard builder if it is a billboard */
    if (mgPoly->textureTemplate == 2) 
    {
	if (gstate->bbuilder == NULL)
	{
	    gstate->bbuilder = pfdNewGeoBldr();
	    pfdGeoBldrMode(gstate->bbuilder, PFDBLDR_AUTO_NORMALS, ComputeNormals11);
	}
	
	pfdAddPoly(gstate->bbuilder, &polygon);
    }
    else
	pfdAddPoly(gstate->builder, &polygon);

    if (polygon.flags & PFD_CONCAVE)
	ConcaveCount++;
    PolyCount[polygon.numVerts]++;

    /*
     * Create replicated polygons 
    */
    if (replicate)
    {
	for (i = 0; i < replicate->count; i++)
	{
	    for (j = 0; j < polygon.numVerts; j++)
		pfXformPt3(polygon.coords[j], polygon.coords[j], matrix->matrix);

	    if (mgPoly->textureTemplate == 2) 
		pfdAddPoly(gstate->bbuilder, &polygon);
	    else
		pfdAddPoly(gstate->builder, &polygon);
	}
	PolyCount[polygon.numVerts] += replicate->count;
    }

    return ptr;
}


/*
 * This converts a FLIGHT object into a Performer sub-tree. The object
 * usually results in a single pfGeode but may generate pfLightPoints and/or
 * pfBillboards.
*/
pfNode*
MakeGeometry11(void)
{
    pfNode	*lp, *geode;

    lp = makeLPoint();
    geode = makeGeode();

    if(lp != NULL && geode != NULL)
    {
    	pfGroup	*group;

     	group = pfNewGroup();
	pfAddChild(group, lp);
	pfAddChild(group, geode);
    	return (pfNode *)group;
    }
    else if(lp != NULL)
	return lp;
    else if(geode != NULL)	
	return geode;
    else
	return NULL;
}


/*
 * Generate pfLightPoint nodes from LightPointList
*/
static pfNode * 
makeLPoint(void)
{
    pfLightPoint *lp;
    pfGroup  	*lpHead;
    LightPoint 	*lp2, *lp3;
    int 	i,k,j,q;
    int	dir, colorDiff;
    pfVec3	lc;
    pfVec3	vector;

    /* linked list of light points */
    if(LightPointList == NULL)
	return(NULL);

    lpHead = pfNewGroup();

    /* sort all light points by directionality */
    k = 0;
    while(1)
    {
	i = 0;
	lp2 = LightPointList;

        /* 
	 * Find first ungrouped light point in LightPointList.
	 * lp2->set = the set of points to which a point belongs, -1
	 * indicates light point is as yet ungrouped.
	*/
    	while(lp2 != NULL && lp2->set >= 0)
	    lp2 = lp2->next;

        if(lp2 == NULL)		/* All done */
	    break;

	dir = lp2->lightType;
	PFCOPY_VEC3(lc, lp2->color);
	PFCOPY_VEC3(vector, lp2->vector);
	
	colorDiff = 0;		/* Check if colors of lightpoints are same */
	while(lp2 != NULL)
	{
	    if(lp2->lightType == dir)
	    {
		if(!colorDiff && !PFALMOST_EQUAL_VEC3(lc, lp2->color, .01f))
		    colorDiff = 1;
		i += lp2->numPoints;
		lp2->set = k;
	    }
	    lp2 = lp2->next;
	}

	lp = pfNewLPoint(i);
	pfLPointSize(lp, 2.0);

	/* Convert direction to azim, elev, roll */
	if(dir != PFLP_OMNIDIRECTIONAL)
	{
	    float a, e;

	    if(PF_ABS(vector[PF_Y]) > PF_ABS(vector[PF_Z]))
		a = -PF_RAD2DEG(atan2f(vector[PF_X], vector[PF_Y]));
	    else
		a = -PF_RAD2DEG(atan2f(vector[PF_X], vector[PF_Z]));

	    if(PF_ABS(vector[PF_Y]) > PF_ABS(vector[PF_X]))
		e = PF_RAD2DEG(atan2f(vector[PF_Z], vector[PF_Y]));
	    else
		e = PF_RAD2DEG(atan2f(vector[PF_Z], vector[PF_X]));

	    pfLPointRot(lp, a, e, 0.);
	}

	/* Choose default of 90 x 90 degrees horizontal/vertical envelope. */
	pfLPointShape(lp, dir, 90.0, 90.0, 4.0);

        q = 0;
	lp2 = LightPointList;

	/* Set overall color if possible */
	if(!colorDiff)
	    pfLPointColor(lp, PFLP_OVERALL, lp2->color);

	while(lp2 != NULL)
	{
	    if(lp2->set == k)
	    {
		if(lp2->name[0] != 0)
		    pfNodeName(lp, lp2->name);

		if(colorDiff)
		{
		    for(j=0; j<lp2->numPoints; j++)
		    {
		    	pfLPointPos(lp, q+j, LPointVertList[lp2->indexList[j]]);
		    	pfLPointColor(lp, q+j, lp2->color);
		    }
		    q += j;
		}
		else
		{
		    for(j=0; j<lp2->numPoints; j++)
		    	pfLPointPos(lp, q+j, LPointVertList[lp2->indexList[j]]);
		    q += j;
		}
	    }
	    lp2 = lp2->next;
	}
	k++;
	pfAddChild(lpHead, lp);
    }
	

    /* free up the light list */
    lp2 = LightPointList;
    while(lp2 != NULL)
    {
	lp3 = lp2;
	lp2 = lp2->next;
	pfFree((void *)lp3->indexList);
	pfFree((void *)lp3);
    }
    LightPointList = NULL;
    LPVertCount = 0;
    return((pfNode*)lpHead);
}


/*
 * Convert FLIGHT polygons into pfGeoSets/pfGeoStates/pfGeodes
*/
static pfNode *
makeGeode(void)
{
    GeoState       *gstate;
    pfGeoState	   *pfGState;
    pfGeode	   *geode = NULL;
    pfBillboard	   *bboard = NULL;
    int	    i;
    const pfList    *gsets;

    if(CurMgFile11->gsList == NULL)
	return NULL;

    /* For all GeoStates */
    for (gstate = CurMgFile11->gsList; gstate != NULL; gstate = gstate->next)
    {
	pfGState = gstate->pfgs;
	if(pfGState == NULL)
	    gstate->pfgs = pfGState = makeGState(gstate);

	/* Add pfGeoSets to pfGeode */
	if (pfdGetNumTris(gstate->builder) > 0)
	{
	    gsets = pfdBuildGSets(gstate->builder);
	    
	    if (geode == NULL)
		geode = pfNewGeode();

	    for (i=0; i<pfGetNum(gsets); i++)
	    {
		pfGSetGState((pfGeoSet*)pfGet(gsets, i), pfGState);
		pfAddGSet(geode, (pfGeoSet*)pfGet(gsets, i));
	    }
	}
	/* Add pfGeoSets to pfBillboard */
	if (gstate->bbuilder && pfdGetNumTris(gstate->bbuilder) > 0)
	{
	    gsets = pfdBuildGSets(gstate->bbuilder);
	    
	    if (bboard == NULL)
		bboard = pfNewBboard();

	    for (i=0; i<pfGetNum(gsets); i++)
	    {
		pfGSetGState((pfGeoSet*)pfGet(gsets, i), pfGState);
		pfAddGSet(bboard, (pfGeoSet*)pfGet(gsets, i));
	    }
	}
    }	/* End while gstate */

    if(bboard != NULL && geode != NULL)
    {
	pfGroup		*grp;

	grp = pfNewGroup();
    	pfAddChild(grp, geode);
    	pfAddChild(grp, bboard);

	return (pfNode*)grp;
    }
    else if(geode != NULL)
	return ((pfNode*)geode);
    else if(bboard != NULL)
	return ((pfNode*)bboard);
    else
	return NULL;
}


/*
 * Create and initialize a new pfGeoState
*/
static pfGeoState*
makeGState(GeoState *gstate)
{
    pfMaterial	   *mtl;
    pfGeoState	   *pfGState;
    float	   alpha;

    pfGState = pfNewGState(Arena);    
    GStateCount++;

    if (gstate->texture >= 0)
    {
        unsigned int          *image;
        int             comp, sx, sy, sz;
    
        /* Assume alpha texture requires afunction */
        pfGetTexImage(CurMgFile11->pfTexList[gstate->texture], 
              &image, &comp, &sx, &sy, &sz);

        if (comp == 2 || comp == 4)
        {
	    /* Use multisample transparency if possible */
            if (GfxType & GFX_MULTISAMPLE)
	    {
            	pfGStateMode(pfGState, PFSTATE_ALPHAFUNC, PFAF_GEQUAL);
            	pfGStateVal(pfGState, PFSTATE_ALPHAREF, (3.0f/255.0f));    
                pfGStateMode(pfGState, PFSTATE_TRANSPARENCY, PFTR_ON);
	    }
	    else
	    {
            	pfGStateMode(pfGState, PFSTATE_ALPHAFUNC, PFAF_GREATER);
            	pfGStateVal(pfGState, PFSTATE_ALPHAREF, (70.0f/255.0f));    
	    }
        }

        pfGStateAttr(pfGState, PFSTATE_TEXTURE,
                CurMgFile11->pfTexList[gstate->texture]);

	/* 
	 * XXX - Inherit TV_MODULATE texture environment for better
	 * performance.
	*/
	if(CurMgFile11->pfTEnvList[gstate->texture] != ModTEnv)
            pfGStateAttr(pfGState, PFSTATE_TEXENV, 
			CurMgFile11->pfTEnvList[gstate->texture]);
                

        pfGStateMode(pfGState, PFSTATE_ENTEXTURE, 1);
    }
    else 
        pfGStateMode(pfGState, PFSTATE_ENTEXTURE, 0);

    mtl = getMaterial(gstate);
	
    if(mtl != NULL)
    {
        pfGStateAttr(pfGState, PFSTATE_FRONTMTL, mtl);

        alpha = pfGetMtlAlpha(mtl);
        if (alpha < 1. - 1./256.)
            pfGStateMode(pfGState, PFSTATE_TRANSPARENCY, PFTR_ON);

        pfGStateMode(pfGState, PFSTATE_ENLIGHTING, PF_ON);
    }
    else
        pfGStateMode(pfGState, PFSTATE_ENLIGHTING, PF_OFF);

    /* Backface culling should be on by default */
    if(!gstate->backFace)
        pfGStateMode(pfGState, PFSTATE_CULLFACE, PFCF_OFF);

    return pfGState;
}

	/*-----------------------------------------------------*/
/*
 * Return list of textures used by previously loaded .flt file
*/
int
pfdGetTexList_flt11(pfTexture ***t)
{
    *t = TexList;
    if(TexList == NULL)
	return 0;
    else
	return TexCount;
}

int
pfdGetNumSharedTex_flt11(void)
{
    return SharedTexCount;
}

/*
 * Return list of unique textures shared by all .flt files
*/
int
pfdGetSharedTexList_flt11(pfTexture *tlist[])
{
    int	i;

    for(i=0; i<SharedTexCount; i++)
	tlist[i] = SharedTexList[i];

    return SharedTexCount;
}


/* the comment ptr in mg_node must point to the comment record */
static int
parseComment(mgComment *n)
{
    int i;
    char *c,*c2;

    if(n == NULL)
	return(0);

    c = n->text;

    if(c == NULL)
	return(0);


    while(*c != 0)
    {
	if(*c++ == '@')
	{
	    if(strncasecmp(c,"sgi",3) == 0)
	    {
		c += 3;
		i = 0;
		/* create a list of terms */
		while(*c != '\n' && *c != '\r' && *c != 0)
		{
		    c2 = terms[i];
		    /* get past leading white space */
		    while(*c == ' ' || *c == '\t' || *c == ',')
			c++;		
		    /* copy the string up to the next delimiter */
		    while(*c != ' ' && 
			    *c != '\t' &&
			    *c != ',' && 
			    *c != '\n' &&
			    *c != '\r' &&
			    *c != 0)
			*c2++ = *c++;

		    *c2 = 0;		/* end the string */
		    if(++i >= MAXTERMS)
			return(i);
		}
	    }
	}	
    }
    return(i);
}

/*
 * Count number of independent triangles, quads, and tristrips in geoset
*/
void
CountGSet11(pfGeoSet *gset)
{
    int	i, l, n;
 
    GSetCount++;

    n = pfGetGSetNumPrims(gset);
    switch(pfGetGSetPrimType(gset))
    {
	case PFGS_TRIS:
	    TriCount += n;
	    break;
	case PFGS_QUADS:
	    QuadCount += n;
	    break;
	case PFGS_TRISTRIPS:
	case PFGS_FLAT_TRISTRIPS:

	    for(i=0; i<n; i++)
 	    {
		l = pfGetGSetPrimLength(gset, i);
	        if(l == 3) 
		    TriCount++;
		else if(l == 4)
		    QuadCount++;
		else
		{
		    StripCount++;
		    StripTriCount += l - 2;
		}
	    }
	    break;
	default:
	    break;
    }		
}