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

File: [Development] / performer / src / lib / libpfdu / pfdGSet.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 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
 */

/*
 * pfdGSet.c
 *
 * $Revision: 1.1 $
 * $Date: 2000/11/21 21:39:35 $
 *
 * Construct implicit surfaces like spheres, cubes, cones, cylinders
 *
 */

#include <stdio.h>
#include <malloc.h>
#ifndef __linux__
#include <bstring.h>
#else
#include <string.h>
#endif
#include <Performer/pfdu.h>

#define PFD_TEXTURED		0x100

static pfVec3	cubeCoords[6][4] = {
    {{-1.0f, -1.0f,  1.0f}, { 1.0f, -1.0f,  1.0f}, 	/* +Z */
    {  1.0f,  1.0f,  1.0f}, {-1.0f,  1.0f,  1.0f}}, 	/* +Z */
    {{-1.0f, -1.0f, -1.0f}, {-1.0f,  1.0f, -1.0f}, 	/* -Z */
    {  1.0f,  1.0f, -1.0f}, { 1.0f, -1.0f, -1.0f}}, 	/* -Z */
    {{ 1.0f, -1.0f,  1.0f}, { 1.0f, -1.0f, -1.0f}, 	/* +X */
    {  1.0f,  1.0f, -1.0f}, { 1.0f,  1.0f,  1.0f}}, 	/* +X */
    {{-1.0f, -1.0f,  1.0f}, {-1.0f,  1.0f,  1.0f}, 	/* -X */
    { -1.0f,  1.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}}, 	/* -X */
    {{-1.0f,  1.0f,  1.0f}, { 1.0f,  1.0f,  1.0f}, 	/* +Y */
    {  1.0f,  1.0f, -1.0f}, {-1.0f,  1.0f, -1.0f}}, 	/* +Y */
    {{-1.0f, -1.0f,  1.0f}, {-1.0f, -1.0f, -1.0f}, 	/* -Y */
    {  1.0f, -1.0f, -1.0f}, { 1.0f, -1.0f,  1.0f}}}; 	/* -Y */

static pfVec3	cubeNorms[6] = {
    { 0.0f,  0.0f,  1.0f}, 	/* +Z */
    { 0.0f,  0.0f, -1.0f}, 	/* -Z */
    { 1.0f,  0.0f,  0.0f}, 	/* +X */
    {-1.0f,  0.0f,  0.0f}, 	/* -X */
    { 0.0f,  1.0f,  0.0f}, 	/* +Y */
    { 0.0f, -1.0f,  0.0f}}; 	/* -Y */


pfGeoSet *pfdNewCube(void *arena); 
pfGeoSet *pfdNewSphere(int ntris, void *arena); 
pfGeoSet *pfdNewCylinder(int ntris, void *arena); 
pfGeoSet *pfdNewPipe(float botRad, float topRad, int ntris, void *arena); 
pfGeoSet *pfdNewCone(int ntris, void *arena);
pfGeoSet *pfdNewCircle(int ntris, void *arena);
pfGeoSet *pfdNewRing(int ntris, void *arena);
pfGeoSet *pfdNewPyramid(void *arena);
pfGeoSet *pfdNewArrow(int ntris, void *arena);
pfGeoSet *pfdNewDoubleArrow(int ntris, void *arena);

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

static void
xformCoords(pfVec3 *coords, pfMatrix mat, int n)
{
    int	i;

    for (i=0; i<n; i++)
	pfXformPt3(coords[i], coords[i], mat);
}

static void
xformNorms(pfVec3 *norms, pfMatrix mat, int n)
{
    pfMatrix	invTranspose;
    int		i;

    pfInvertFullMat(invTranspose, mat);
    pfTransposeMat(invTranspose, invTranspose); 
    
    for (i=0; i<n; i++)
    {
	pfXformVec3(norms[i], norms[i], invTranspose);
	pfNormalizeVec3(norms[i]);
    }
}


/* 
 * Transform gset's coordinates and normals by 'mat'
 * 
 * Only implemented for non-indexed, normal-strided geosets.
*/
void
pfdXformGSet(pfGeoSet *gset, pfMatrix mat)
{
    pfVec3	*coords, *norms;
    ushort	*vindex, *nindex;
    int		vstride, nstride;

    pfGetGSetAttrLists(gset, PFGS_COORD3, (void**)&coords, &vindex);
    pfGetGSetAttrLists(gset, PFGS_NORMAL3, (void**)&norms, &nindex);

    if (vindex || nindex)
    {
	pfNotify(PFNFY_WARN, PFNFY_RESOURCE, "pfdXformGSet() "
		 "Flatten not yet implemented for indexed pfGeoSets");
	return;
    }

    if (coords)
	xformCoords(coords, mat, pfGetSize(coords) / sizeof(pfVec3));

    if (norms)
	xformNorms(norms, mat, pfGetSize(norms) / sizeof(pfVec3));
}

static void
getBoxSize(int *nstrips, int *nc, int *nn, int nbind)
{
    *nstrips = 6;
    *nc = 24;
    if (nbind == PFGS_PER_VERTEX)
	*nn = 24;
    else if (nbind == PFGS_PER_PRIM)
	*nn = 6;
    else
	*nn = 0;
}

static void
makeBox(pfVec3 **coords, pfVec3 **norms, int **lens, 
	float xmin, float ymin, float zmin, 
	float xmax, float ymax, float zmax, int nbind) 
{
    int		i, j, k;
    pfMatrix	mat;
    static int  stripOrder[] = {0, 1, 3, 2};

    for (k=0, i=0; i<6; i++)
    {
	if (nbind == PFGS_PER_PRIM)
	{
	    pfCopyVec3((*norms)[0], cubeNorms[i]);
	    *norms += 1;
	}
	for (j=0; j<4; j++, k++)
	{
	    pfCopyVec3((*coords)[k], cubeCoords[i][stripOrder[j]]);
	    if (nbind == PFGS_PER_VERTEX)
	    {
		pfCopyVec3((*norms)[0], cubeNorms[i]);
		*norms += 1;
	    }
	}
	if (lens)
	{
	    (*lens)[0] = 4;
	    *lens += 1;
	}
    }
    pfMakeTransMat(mat, 1.0f, 1.0f, 1.0f);
    pfPostScaleMat(mat, mat, 
		   (xmax - xmin)/2, (ymax - ymin)/2, (zmax - zmin)/2);
    pfPostTransMat(mat, mat, xmin, ymin, zmin);

    for (i=0; i<24; i++)
	pfXformPt3((*coords)[i], (*coords)[i], mat);

    *coords += 24;
}

/* 
 * Make cube centered at the origin with sides of length 1
 */
pfGeoSet*
pfdNewCube(void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		*lens;
    int 	ns, nc, nn;

    g = pfNewGSet(arena);

    getBoxSize(&ns, &nc, &nn, PFGS_PER_PRIM);

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * nc, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * nn, arena);
    lens = (int*) pfMalloc(sizeof(int) * ns, arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_PRIM, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);

    pfGSetNumPrims(g, ns);
    pfGSetPrimLengths(g, lens);

    makeBox(&coords, &norms, &lens, -.5f, -.5f, -.5f, .5f, .5f, .5f,
	    PFGS_PER_PRIM);

    return g;
}

/* 
 * Start with a cube. Chop faces into uniform 2D grid then bloat grid points
 * to surface of unit sphere. A nonuniform grid would generate a more 
 * uniform sphere. 
 */
pfGeoSet*
pfdNewSphere(int ntris, void *arena)
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		*lens, nrows, i, j, k, l, ncoords;
    float	x, y, z, d;

    nrows = pfSqrt(ntris / 6);
    nrows = PF_MAX2(nrows, 1);

    ncoords = 2*(3 * nrows + 1) * nrows * 2;

    g = pfNewGSet(arena);

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * ncoords, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * ncoords, arena);
    lens = (int*) pfMalloc(sizeof(int) * 2 * nrows, arena);

    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_VERTEX, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);
    pfGSetNumPrims(g, 2 * nrows);
    pfGSetPrimLengths(g, lens);

    k = l = 0;
    d = 2.0f / (float)nrows;

    x = -1.0f;
    for (i=0; i<nrows; i++)
    {
	y = -1.0f;
	z = -1.0f;
	lens[l++] = 2*(3 * nrows + 1);
	for (j=0; j<nrows; j++)
	{
	    pfSetVec3(coords[k], x, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    pfSetVec3(coords[k], x+d, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    z += d;
	}
	for (j=0; j<nrows; j++)
	{
	    pfSetVec3(coords[k], x, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    pfSetVec3(coords[k], x+d, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    y += d;
	}
	for (j=0; j<=nrows; j++)
	{
	    pfSetVec3(coords[k], x, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    pfSetVec3(coords[k], x+d, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    z -= d;
	}
	x += d;
    }
    y = -1.0f;
    for (i=0; i<nrows; i++)
    {
	x = -1.0f;
	z =  1.0f;
	lens[l++] = 2*(3 * nrows + 1);
	for (j=0; j<nrows; j++)
	{
	    pfSetVec3(coords[k], x, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    pfSetVec3(coords[k], x, y+d, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    z -= d;
	}
	for (j=0; j<nrows; j++)
	{
	    pfSetVec3(coords[k], x, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    pfSetVec3(coords[k], x, y+d, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    x += d;
	}
	for (j=0; j<=nrows; j++)
	{
	    pfSetVec3(coords[k], x, y, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    pfSetVec3(coords[k], x, y+d, z);
	    pfNormalizeVec3(coords[k]);
	    pfCopyVec3(norms[k], coords[k]);
	    k++;
	    z += d;
	}
	y += d;
    }
    return g;
}


#define DOWN	-PF_Z
#define UP	PF_Z

static void
getCircleSize(int nsides, int *nstrips, int *nc, int *nn)
{
    *nstrips = 1;
    *nc = nsides;

    /* !!!!!!!!!! */
    if (nn)
	*nn = nsides;
}


static int
makeCircle(pfVec3 **coords, pfVec3 **norms, int **lens, 
	   int nsides, float radius, int axis, float dist)
{
    int		i, j, k, nv, odd;
    float	theta, dt, x, y, z;

    if (norms)
    {
	for (i=0; i<nsides; i++)
	    pfSetVec3((*norms)[i], 0.0f, 0.0f, 
		      (axis > 0) ? 1.0f : -1.0f);

	*norms += nsides;
    }

    dt = 360.0f / nsides;
    k = 0;

    if (axis > 0)
	dt = -dt;

    j = nsides - 1;
    i = 0;
    pfSinCos(dt * i, &x, &y);
    pfSetVec3((*coords)[k], x * radius, y * radius, dist);
    k++;
    i++;
    pfSinCos(dt * i, &x, &y);
    pfSetVec3((*coords)[k], x * radius, y * radius, dist);
    k++;
    i++;
    pfSinCos(dt * j, &x, &y);
    pfSetVec3((*coords)[k], x * radius, y * radius, dist);
    k++;
    j--;

    odd = 0;
    while (i <= j)
    {
	float	x, y;

	if (odd)
	{
	    pfSinCos(dt * j, &x, &y);
	    pfSetVec3((*coords)[k], x * radius, y * radius, dist);
	    k++;
	    j--;
	}
	else
	{
	    pfSinCos(dt * i, &x, &y);
	    pfSetVec3((*coords)[k], x * radius, y * radius, dist);
	    k++;
	    i++;
	}
	odd = !odd;
    }
    *coords += k;

    if (lens)
    {
	(*lens)[0] = nsides;
	*lens += 1;
    }

    return k;
}


static void
getRingSize(int nsides, int *nstrips, int *nc)
{
    *nstrips = 1;
    *nc = nsides;
}


static int
makeRing(pfVec3 **coords, int **lens, 
	 int nsides, float radius, int axis, float dist)
{
    int		i, j, k, nv, odd;
    float	theta, dt, x, y, z;

    dt = 360.0f / (nsides-1);
    k = 0;

    for (i=0; i<nsides; i++)
    {
	pfSinCos(dt*i, &x, &y);
	pfSetVec3((*coords)[i], x * radius, y * radius, dist);
    }
    *coords += nsides;
    if (lens)
    {
	(*lens)[0] = nsides;
	*lens++;
    }

    return k;
}


/*
 * Make wireframe circle of radius 1 in the Z=0 plane
 */
pfGeoSet*
pfdNewRing(int nlines, void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords;
    pfVec2	*texCoords;
    int		*lens;

    g = pfNewGSet(arena);

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * nlines, arena);
    lens = (int*) pfMalloc(sizeof(int), arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetPrimType(g, PFGS_LINESTRIPS);
    pfGSetNumPrims(g, 1);
    pfGSetPrimLengths(g, lens);

    makeRing(&coords, &lens, nlines, 1.0f, PF_Z, 0.0f);

    return g;
}

static void
getPipeSize(int nsides, int *nstrips, int *nc, int *nn)
{
    *nstrips = 1;
    *nc = 2 * (nsides + 1);
    *nn = 2 * (nsides + 1);
}

/*
 * Make cylinder without end caps. zmin must be < zmax
*/
static int
makePipe(pfVec3 **coords, pfVec3 **norms, int **lens, 
	 int nsides, float bottomRadius, float topRadius, 
	 float zmin, float zmax)
{
    int		i, k;
    float	theta, dt, x, y, z, slope, znorm;

    slope = (bottomRadius - topRadius) / (zmax - zmin);

    if (slope != 0.0f)
	znorm = 1.0f / slope;
    else
	znorm = 0.0f;

    dt = 360.0f / nsides;
    theta = 0.0f;
    k = 0;

    /* Make the pipe */
    for (i=0; i<=nsides; i++)
    {
	pfSinCos(theta, &x, &y);

	pfSetVec3((*coords)[k], x * bottomRadius, y * bottomRadius, zmin);
	pfSetVec3((*norms)[k], x, y, znorm);
	if (znorm)
	    pfNormalizeVec3((*norms)[k]);
	k++;
	pfSetVec3((*coords)[k], x * topRadius, y * topRadius, zmax);
	pfCopyVec3((*norms)[k], (*norms)[k-1]); 
	k++;

	theta += dt;
    }
    *coords += k;
    *norms += k;

    if (lens)
    {
	(*lens)[0] = k;
	*lens += 1;
    }

    return k;
}

/*
 * Make cylinder of radius 1 and length 2 centered along the Z axis
 */
pfGeoSet*
pfdNewCylinder(int ntris, void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		*lens, nsides, i, j, k, s, odd, nv;
    float	theta, dt, x, y, z;
    int		tns, tnc, tnn, ns, nc, nn;

    nsides = ntris / 4;
    nsides = PF_MAX2(nsides, 3);

    g = pfNewGSet(arena);

    getPipeSize(nsides, &tns, &tnc, &tnn);
    getCircleSize(nsides, &ns, &nc, &nn);

    tns += 2 * ns;
    tnc += 2 * nc;
    tnn += 2 * nn;

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnc, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnn, arena);
    lens = (int*) pfMalloc(sizeof(int) * tns, arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_VERTEX, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);
    pfGSetNumPrims(g, tns);
    pfGSetPrimLengths(g, lens);

    makePipe(&coords, &norms, &lens, nsides, 1.0f, 1.0f, -1.0f, 1.0f);
    makeCircle(&coords, &norms, &lens, nsides, 1.0f, DOWN, -1.0f);
    makeCircle(&coords, &norms, &lens, nsides, 1.0f, UP, 1.0f);

    return g;
}

pfGeoSet*
pfdNewPipe(float botRad, float topRad, int ntris, void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		*lens, nsides, i, j, k, s, odd, nv;
    float	theta, dt, x, y, z;
    int		tns, tnc, tnn, ns, nc, nn;

    nsides = ntris / 2;
    nsides = PF_MAX2(nsides, 3);

    g = pfNewGSet(arena);

    getPipeSize(nsides, &tns, &tnc, &tnn);

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnc, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnn, arena);
    lens = (int*) pfMalloc(sizeof(int) * tns, arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_VERTEX, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);
    pfGSetNumPrims(g, tns);
    pfGSetPrimLengths(g, lens);

    makePipe(&coords, &norms, &lens, nsides, botRad, topRad, -1.0f, 1.0f);

    return g;
}

void
getConeTopSize(int nsides, int *nstrips, int *nc, int *nn)
{
    getPipeSize(nsides, nstrips, nc, nn);
}

static int
makeConeTop(pfVec3 **coords, pfVec3 **norms, int **lens, 
	    int nsides, float radius, float height, float base)
{

    if (height < 0.0f)
	makePipe(coords, norms, lens, nsides, 0.0f, radius, base+height, base);
    else
	makePipe(coords, norms, lens, nsides, radius, 0.0f, base, base+height);

}

void
getConeSize(int nsides, int *nstrips, int *nc, int *nn)
{
    int	ns, nnc, nnn;

    getConeTopSize(nsides, nstrips, nc, nn);
    getCircleSize(nsides, &ns, &nnc, &nnn);

    *nstrips += ns;
    *nc += nnc;
    *nn += nnn;
}

static void
makeCone(pfVec3 **coords, pfVec3 **norms, int **lens, 
	 int nsides, float radius, float height, float base)
{
    makeConeTop(coords, norms, lens, nsides, radius, height, base);
    if (height < 0.0f)
	makeCircle(coords, norms, lens, nsides, radius, UP, base);
    else
	makeCircle(coords, norms, lens, nsides, radius, DOWN, base);
}

/*
 * Make cone of radius 1 and height 1 centered along the Z axis with
 * base at Z = 0.
 */
pfGeoSet*
pfdNewCone(int ntris, void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		nsides, ncoords, i, j, k, *lens, odd;
    float	dt, theta, x, y;
    int		tns, tnc, tnn, ns, nc, nn;

    nsides = ntris / 2;
    ncoords = nsides * 4;

    getConeTopSize(nsides, &tns, &tnc, &tnn);
    getCircleSize(nsides, &ns, &nc, &nn);
    tns += ns;
    tnc += nc;
    tnn += nn;

    g = pfNewGSet(arena);

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnc, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnn, arena);
    lens = (int*) pfMalloc(sizeof(int) * tns, arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_VERTEX, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);
    pfGSetNumPrims(g, tns);
    pfGSetPrimLengths(g, lens);

    makeConeTop(&coords, &norms, &lens, nsides, 1.0f, 1.0f, 0.0f);
    makeCircle(&coords, &norms, &lens, nsides, 1.0f, DOWN, 0.0f);

    return g;
}


/*
 * Make circle of radius 1 facing +Z
 */
pfGeoSet*
pfdNewCircle(int ntris, void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		*lens, i, j, k, odd, nsides = ntris+2;
    float	dt, x, y;

    g = pfNewGSet(arena);

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * nsides, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3), arena);
    lens = (int*) pfMalloc(sizeof(int), arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_OVERALL, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);
    pfGSetNumPrims(g, 1);
    pfGSetPrimLengths(g, lens);

    pfSetVec3(norms[0], 0.0f, 0.0f, 1.0f);

    makeCircle(&coords, NULL, &lens, nsides, 1.0f, UP, 0.0f);

    return g;
}

static void
getPyramidSize(int *nstrips, int *nc, int *nn, int nbind)
{
    *nstrips = 5;
    *nc = 16;

    if (nbind == PFGS_PER_VERTEX)
	*nn = *nc;
    else if (nbind == PFGS_PER_PRIM)
	*nn = 5;
    else
	*nn = 0;
}

static void
makePyramid(pfVec3 **coords, pfVec3 **norms, int **lens, 
	    float width, float height, float base, int nbind)
{
    int		i, j, k;
    float	w = width / 2.0f, out, up;
    pfVec3	norm;

    static pfVec3	pyrCoords[] = {
    {-1.0f, -1.0f, 0.0f}, 
    {-1.0f,  1.0f, 0.0f}, 
    { 1.0f,  1.0f, 0.0f}, 
    { 1.0f, -1.0f, 0.0f}, 
    { 0.0f,  0.0f, 1.0f}};	/* top */

    static pfVec3	pyrNorms[] = {
    { 0.0f,  0.0f,-1.0f},	/* base */
    { 0.0f, -1.0f, 1.0f}, 	/* sides */
    { 1.0f,  0.0f, 1.0f}, 
    { 0.0f,  1.0f, 1.0f}, 
    {-1.0f,  0.0f, 1.0f}};

    static int		pyrIndices[] = {
	0, 1, 3, 2, -1,	/* base */
	0, 3, 4, -1, 	/* sides */
	3, 2, 4, -1,
	2, 1, 4, -1,
	1, 0, 4, -1};

    pfSetVec3(norm, PF_ABS(height), 0.0f, w);
    pfNormalizeVec3(norm);
    out = norm[0];
    up = norm[2];

    for (k=0, j=0, i=0; i<5; i++, j++)
    {
	int		n;

	n = 0;
	while (pyrIndices[j] >= 0)
	{
	    pfCopyVec3((*coords)[k], pyrCoords[pyrIndices[j]]);
	    (*coords)[k][2] = (*coords)[k][2] * height + base;
	    (*coords)[k][0] *= w;
	    (*coords)[k][1] *= w;

	    if (nbind == PFGS_PER_VERTEX)
	    {
		pfCopyVec3((*norms)[0], pyrNorms[i]);
		(*norms)[0][0] *= out;
		(*norms)[0][1] *= out;
		if (height < 0.0f)
		    (*norms)[0][2] *= -up;
		else
		    (*norms)[0][2] *= up;
		pfNormalizeVec3((*norms)[0]);
		*norms += 1;
	    }
	    j++;
	    k++;
	    n++;

	}
	if (height < 0.0f)
	{
	    pfVec3	tmp;

	    pfCopyVec3(tmp, (*coords)[k-n+1]);
	    pfCopyVec3((*coords)[k-n+1], (*coords)[k-n+2]);
	    pfCopyVec3((*coords)[k-n+2], tmp);
	}
	if (nbind == PFGS_PER_PRIM)
	{
	    pfCopyVec3((*norms)[0], pyrNorms[i]);
	    (*norms)[0][0] *= out;
	    (*norms)[0][1] *= out;
	    if (height < 0.0f)
		(*norms)[0][2] *= -up;
	    else
		(*norms)[0][2] *= up;
	    pfNormalizeVec3((*norms)[0]);
	    *norms += 1;
	}
	if (lens)
	{
	    (*lens)[0] = n;
	    *lens += 1;
	}
    }
    *coords += k;

}

#define INV_SQRT2	.7071067811865475244

/*
 * Make pyramid with unit square base at Z=0 and height 1.
 */
pfGeoSet*
pfdNewPyramid(void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		*lens, nstrips, nc, nn;

    g = pfNewGSet(arena);

    getPyramidSize(&nstrips, &nc, &nn, PFGS_PER_PRIM);

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * nc, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * nn, arena);
    lens = (int*) pfMalloc(sizeof(int) * nstrips, arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_PRIM, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);
    pfGSetNumPrims(g, nstrips);
    pfGSetPrimLengths(g, lens);

    makePyramid(&coords, &norms, &lens, 1.0f, 1.0f, 0.0f, PFGS_PER_PRIM);

    return g;
}

#define ARROW_LEN	.25f
#define ARROW_WIDTH	.125f 
#define SHAFT_WIDTH	.0625f


/*
 * Make arrow with base at Z=0, total height = 1, shaft width of 
 * SHAFT_WIDTH and arrowhead length ARROW_LEN and width ARROW_WIDTH
 */
pfGeoSet*
pfdNewArrow(int ntris, void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		*lens, nsides;
    int		tns, tnc, tnn, ns, nc, nn;

    /* side ratios base:pipe:cone = 1 : 1 : 2 (cone is twice as fine) */
    /* tri ratios  base:pipe:cone = 1 : 2 : 2 */
    nsides = ntris / 7;	/* nsides is sides in pipe/base, cone is 2x */
    
    nsides = PF_MAX2(nsides, 4);

    g = pfNewGSet(arena);

    /* '4' is coarsest arrow which has pyramid for end and cube for shaft */
    if (nsides == 4 && ntris < 28)
    {
	getBoxSize(&tns, &tnc, &tnn, PFGS_PER_PRIM);
	getPyramidSize(&ns, &nc, &nn, PFGS_PER_PRIM);
	tns += ns;
	tnc += nc;
	tnn += nn;

	coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnc, arena);
	norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnn, arena);
	lens = (int*) pfMalloc(sizeof(int) * tns, arena);
	
	pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
	pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_PRIM, norms, NULL);
	pfGSetPrimType(g, PFGS_TRISTRIPS);
	pfGSetNumPrims(g, tns);
	pfGSetPrimLengths(g, lens);

	makeBox(&coords, &norms, &lens, 
		-SHAFT_WIDTH*.5f, -SHAFT_WIDTH*.5f, 0.0f, SHAFT_WIDTH*.5f, SHAFT_WIDTH*.5f, 
		1.0f - ARROW_LEN, PFGS_PER_PRIM);
	makePyramid(&coords, &norms, &lens, ARROW_WIDTH, ARROW_LEN, 1.0f-ARROW_LEN,
		    PFGS_PER_PRIM);

	return g;
    }

    getPipeSize(nsides, &tns, &tnc, &tnn);
    getCircleSize(nsides, &ns, &nc, &nn);
    tns += ns;
    tnc += nc;
    tnn += nc;	/* Need PER_VERTEX here */
    getConeSize(2*nsides, &ns, &nc, &nn);
    tns += ns;
    tnc += nc;
    tnn += nn;

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnc, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnn, arena);
    lens = (int*) pfMalloc(sizeof(int) * tns, arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_VERTEX, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);
    pfGSetNumPrims(g, tns);
    pfGSetPrimLengths(g, lens);

    makePipe(&coords, &norms, &lens, nsides, SHAFT_WIDTH*.5f, SHAFT_WIDTH*.5f, 
	     0.0f, 1.0f-ARROW_LEN);
    makeCircle(&coords, &norms, &lens, nsides, SHAFT_WIDTH*.5f, DOWN, 0.0f);
    makeCone(&coords, &norms, &lens, 2*nsides, ARROW_WIDTH, 
	     ARROW_LEN, 1.0f-ARROW_LEN);

    return g;
}


/*
 * Make double-headed arrow ranging from Z=-1 to Z=1 
 */
pfGeoSet*
pfdNewDoubleArrow(int ntris, void *arena) 
{
    pfGeoSet  	*g;
    pfVec3	*coords, *norms;
    pfVec2	*texCoords;
    int		*lens, nsides;
    int		tns, tnc, tnn, ns, nc, nn;

    /* side ratios cone:pipe:cone = 2 : 1 : 2 (cone is twice as fine) */
    /* tri ratios  cone:pipe:cone = 2 : 2 : 2 */
    nsides = ntris / 10; /* nsides is sides in pipe, cone is 2x */
    
    nsides = PF_MAX2(nsides, 4);

    g = pfNewGSet(arena);

    /* '4' is coarsest arrow which has pyramid for end and cube for shaft */
    if (nsides == 4 && ntris < 40)
    {
	getBoxSize(&tns, &tnc, &tnn, PFGS_PER_PRIM);
	getPyramidSize(&ns, &nc, &nn, PFGS_PER_PRIM);
	tns += 2*ns;
	tnc += 2*nc;
	tnn += 2*nn;

	coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnc, arena);
	norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnn, arena);
	lens = (int*) pfMalloc(sizeof(int) * tns, arena);
	
	pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
	pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_PRIM, norms, NULL);
	pfGSetPrimType(g, PFGS_TRISTRIPS);
	pfGSetNumPrims(g, tns);
	pfGSetPrimLengths(g, lens);

	makeBox(&coords, &norms, &lens, 
		-SHAFT_WIDTH*.5f, -SHAFT_WIDTH*.5f, -1.0f+ARROW_LEN, 
		SHAFT_WIDTH*.5f, SHAFT_WIDTH*.5f, 1.0f-ARROW_LEN, PFGS_PER_PRIM);

	makePyramid(&coords, &norms, &lens, ARROW_WIDTH, 
		    ARROW_LEN, 1.0f-ARROW_LEN, PFGS_PER_PRIM);
	makePyramid(&coords, &norms, &lens, ARROW_WIDTH, 
		    -ARROW_LEN, -(1.0f-ARROW_LEN), PFGS_PER_PRIM);
	return g;
    }

    getPipeSize(nsides, &tns, &tnc, &tnn);
    getConeSize(2*nsides, &ns, &nc, &nn);
    tns += 2*ns;
    tnc += 2*nc;
    tnn += 2*nn;

    coords = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnc, arena);
    norms = (pfVec3*) pfMalloc(sizeof(pfVec3) * tnn, arena);
    lens = (int*) pfMalloc(sizeof(int) * tns, arena);
    
    pfGSetAttr(g, PFGS_COORD3, PFGS_PER_VERTEX, coords, NULL);
    pfGSetAttr(g, PFGS_NORMAL3, PFGS_PER_VERTEX, norms, NULL);
    pfGSetPrimType(g, PFGS_TRISTRIPS);
    pfGSetNumPrims(g, tns);
    pfGSetPrimLengths(g, lens);

    makePipe(&coords, &norms, &lens, nsides, SHAFT_WIDTH*.5f, SHAFT_WIDTH*.5f, 
	     -1.0f+ARROW_LEN, 1.0f-ARROW_LEN);
    makeCone(&coords, &norms, &lens, 2*nsides, ARROW_WIDTH, 
	     ARROW_LEN, 1.0f-ARROW_LEN);
    makeCone(&coords, &norms, &lens, 2*nsides, ARROW_WIDTH, 
	     -ARROW_LEN, -(1.0f-ARROW_LEN));

    return g;
}

/*
 * Convenience routine for setting global gset color
 * 
 * Only works if color binding is PFGS_OVERALL
 */
void
pfdGSetColor(pfGeoSet *gset, float r, float g, float b, float a)
{
    ushort	*iclrs;
    pfVec3	*clrs;

    if (!gset)
	return;

    if (pfGetGSetAttrBind(gset, PFGS_COLOR4) != PFGS_OVERALL)
	return;

    pfGetGSetAttrLists(gset, PFGS_COLOR4, (void**)&clrs, &iclrs);

    if (!clrs)
	return;

    if (iclrs)
	pfSetVec4(clrs[iclrs[0]], r, g, b, a);
    else
	pfSetVec4(clrs[0], r, g, b, a);
}


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

#ifdef STRIP_BOX
static void
getBoxSize(int *nstrips, int *nc, int *nn)
{
    *nstrips = 2;
    *nc = 16;
    *nn = 12;
}

static void
makeBox(pfVec3 **coords, pfVec3 **norms, int **lens, 
	float xmin, float ymin, float zmin, 
	float xmax, float ymax, float zmax, int nbind) 
{
    pfSetVec3((*coords)[0], xmin, ymin, zmin);
    pfSetVec3((*coords)[1], xmax, ymin, zmin);
    pfSetVec3( (*norms)[0],  0.0f, -1.0f,  0.0f);
    pfSetVec3((*coords)[2], xmin, ymin, zmax);
    pfSetVec3( (*norms)[1],  0.0f, -1.0f,  0.0f);
    pfSetVec3((*coords)[3], xmax, ymin, zmax);
    pfSetVec3( (*norms)[2],  0.0f,  0.0f,  1.0f);
    pfSetVec3((*coords)[4], xmin, ymax, zmax);
    pfSetVec3( (*norms)[3],  0.0f,  0.0f,  1.0f);
    pfSetVec3((*coords)[5], xmax, ymax, zmax);
    pfSetVec3( (*norms)[4],  0.0f,  1.0f,  0.0f);
    pfSetVec3((*coords)[6], xmin, ymax, zmin);
    pfSetVec3( (*norms)[5],  0.0f,  1.0f,  0.0f);
    pfSetVec3((*coords)[7], xmax, ymax, zmin);

    pfSetVec3((*coords)[8],  xmin, ymin, zmax);
    pfSetVec3((*coords)[9],  xmin, ymax, zmax);
    pfSetVec3( (*norms)[6],  -1.0f,  0.0f,  0.0f);
    pfSetVec3((*coords)[10], xmin, ymin, zmin);
    pfSetVec3( (*norms)[7],  -1.0f,  0.0f,  0.0f);
    pfSetVec3((*coords)[11], xmin, ymax, zmin);
    pfSetVec3( (*norms)[8],   0.0f,  0.0f, -1.0f);
    pfSetVec3((*coords)[12], xmax, ymin, zmin);
    pfSetVec3( (*norms)[9],   0.0f,  0.0f, -1.0f);
    pfSetVec3((*coords)[13], xmax, ymax, zmin);
    pfSetVec3( (*norms)[10],  1.0f,  0.0f,  0.0f);
    pfSetVec3((*coords)[14], xmax, ymin, zmax);
    pfSetVec3( (*norms)[11],  1.0f,  0.0f,  0.0f);
    pfSetVec3((*coords)[15], xmax, ymax, zmax);

    (*lens)[0] = 8;
    (*lens)[1] = 8;
    (*lens) += 2;

    *coords += 16;
    *norms += 12;
}
#endif