#pragma ident "@(#)cgpdecompress.c 1.8 97/05/09 SMI"

/*
 * Copyright (c) 1997 by Sun Microsystems, Inc.
 * All Rights Reserved
 */

/*
 ***********************************************************************
 *
 *  original file: hs_degcompress.c
 *
 *	@(#)hs_gdecompress.c 1.15 95/09/19 13:44:28
 *
 *  Copyright (c) 1993,1994,1995,1996 by Sun Microsystems, Inc.
 *
 *  Initial program creation 9/93  Michael F. Deering
 *
 *  HoloSketch Geometery Decompression related routines.
 *  First created Michael Deering, 1994.
 *
 *
 ***********************************************************************
 */
/*
#define JUST_PRINT_1
#define PRINT_ONLY
*/

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

#include "cgpi.h"
#include "cgpdecompress.h"
#include "cgpgcnorms.h"

static         void index_to_normal(int sex, int oct, int u, int v,
                            int index, float *n);
static int     process_decompression_opcode(int mbp);
static void    process_decompression();
static void    process_set_normal(int mbp);
static void    process_set_color(int mbp);
static int     process_vertex();
static void    process_mesh_b_r();
static void    process_set_state();
static void    process_set_table();
static void    process_v_no_op();
static void    gc_in(int bit_ct, unsigned int *bit_place);
static void    process_eos();

static void    cgpWriteUint(CGuint);
static void    cgpWriteFloat(CGfloat);
static void    cgpWriteUintAt(CGuint, CGuint);
static void    cgpWritePointType();
static CGuint  cgpGetBuffIdx();

static fArray		geom[3];
static fArray		norm[3];
static fArray		color[3];
static fArray           curGeom;
static fArray           curNorm;
static fArray           curColor;

static CGubyte          PrimClass;

static CGuint		curPt;		/* current point being processed */
static CGuint		midPt;		/* middle point of last 3 processed */
static CGuint		oldPt;		/* oldest point of last 3 processed */
static CGboolean	inBegin; 
static CGuint		replacement;	/* current vertex replacement policy */
static CGuint           rep;	        /* replacment value of current vtx */
static CGuint		numPts;		/* number of points */
static CGuint           meshState;


/* decompression buffer */
static CGuint *Buff;
static CGuint BuffSize;
static CGuint CurBuffPos;
static CGuint CntIdx;
static CGuint VtxCnt;
static CGviewport vwp;

/* The three Huffman tables; 00 is pos, 01 is norm, 10 is color */
static gc_table_e gctables[3][64];


/* The Mesh Buffer, and its update pointer state variable */
static gc_mesh_buffer_e mesh_buffer[16];
static int	mesh_buffer_p     = 15;

/* Geometery Compression state variables */
static int	bundling_norm;
static int	bundling_color;
static int	doing_alpha;
static short	cur_x, cur_y, cur_z;
static short	cur_r, cur_g, cur_b, cur_a;
static int	cur_sex, cur_oct, cur_u, cur_v;
static short	cur_nx, cur_ny, cur_nz;

/* Internal decompression holding variables */
static unsigned int	current_header    = 0;
static unsigned int	next_header       = 0;
static	unsigned int	bit_acc = 0;
static	int	bit_acc_valid = 32;

/* Bit-field masks: BMASK[i] = (1<<i)-1, but works for 32 as well */
static unsigned int BMASK[33] = {
    0x0,        0x1,        0x3,        0x7,
    0xF,        0x1F,       0x3F,       0x7F,
    0xFF,       0x1FF,      0x3FF,      0x7FF,
    0xFFF,      0x1FFF,     0x3FFF,     0x7FFF,
    0xFFFF,     0x1FFFF,    0x3FFFF,    0x7FFFF,
    0xFFFFF,    0x1FFFFF,   0x3FFFFF,   0x7FFFFF,
    0xFFFFFF,   0x1FFFFFF,  0x3FFFFFF,  0x7FFFFFF,
    0xFFFFFFF,  0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF,
    0xFFFFFFFF, 
};

static int gciobufp, gciobufe;
static char *gciobuf;

/*
#define NORM_FIX 0x80000000u
#define FNORM_FIX (float) NORM_FIX
*/
#define FNORM_FIX (float) 1.0

/****************************************************************************
 *  All input parameters are assumed to have been normalized
 *  to the full range,
 *  in particular, index has been normalized to 18 bits
 */

static void
index_to_normal(int sex, int oct, int u, int v, int index, float *n)
{
    int  sns, www;
    float nx, ny, nz, t;

    if (sex > 5) { /* Special normals */
        sns = (index>>(18-7)) & 0x3;
        switch(sns) {
        case 0: /* six coordinate axies */
            www = (index>>(18-5)) & 0x7;
            switch(www>>1) {
                case 0: nx = FNORM_FIX; ny = nz = 0.0; break;
                case 1: ny = FNORM_FIX; nx = nz = 0.0; break;
                case 2: nz = FNORM_FIX; nx = ny = 0.0; break;
            }
            sex = 0; oct = www&1; oct = (oct<<2) | (oct<<1) | oct;
            break;
        case 2: /* eight mid */
            oct = (index>>(18-5)) & 0x7;
            sex = 0;
            nx = ny = nz = (1.0/sqrt(3.0) * FNORM_FIX);
            break;
        case 1: case 3: /* great circle */
            www = (index>>(18-6)) & 0xF;
            switch(www>>2) {
                case 0: nx = (((www&2)?-1.0:1.0)/sqrt(2) * FNORM_FIX);
                        ny = (((www&1)?-1.0:1.0)/sqrt(2) * FNORM_FIX);
                        nz = 0.0; break;
                case 1: ny = (((www&2)?-1.0:1.0)/sqrt(2) * FNORM_FIX);
                        nz = (((www&1)?-1.0:1.0)/sqrt(2) * FNORM_FIX);
                        nx = 0.0; break;
                case 2: nx = (((www&2)?-1.0:1.0)/sqrt(2) * FNORM_FIX);
                        nz = (((www&1)?-1.0:1.0)/sqrt(2) * FNORM_FIX);
                        nx = 0.0; break;
            }
            sex = 0; oct = 0;
            break;
        }
        if (oct & 1) nz = -nz;
        if (oct & 2) ny = -ny;
        if (oct & 4) nx = -nx;
    } else { /* Regular normals */
        nx = gc_normals[v][u][0];
        ny = gc_normals[v][u][1];
        nz = gc_normals[v][u][2];

        /*
         *  Reverse the swaping
         */
        if (sex & 4) { t = nx; nx = nz; nz = t; }
        if (sex & 2) { t = ny; ny = nz; nz = t; }
        if (sex & 1) { t = nx; nx = ny; ny = t; }

        /*
         *  Reverse the sign flipping
         */
        if (oct & 1) nz = -nz;
        if (oct & 2) ny = -ny;
        if (oct & 4) nx = -nx;
    }

#ifdef PRINT_ONLY
printf("N <%6.3f %6.3f %6.3f>\n", nx, ny, nz);
#endif

    /* Set normal */
    n[0] = nx;
    n[1] = ny;
    n[2] = nz;

}  /* end of index_to_normal */


/*******************
 * cgpWriteUint
 *
 * Writes an unsigned int to the decompression buffer.
 *
 *  Input:
 *    CGuint data
 *
 *  Output:
 *    None.
 *
 */

static
void cgpWriteUint(CGuint data)
{
    Buff[CurBuffPos] = data;
    CGP_INC_DECOMP_BUFF_POS;
}


/*******************
 * cgpWriteFloat
 *
 * Writes a float to the decompression buffer.
 *
 *  Input:
 *    CGfloat data
 *
 *  Output:
 *    None.
 *
 */

static
void cgpWriteFloat(CGfloat data)
{
    CGfloat *f = (CGfloat*) &Buff[CurBuffPos];

    *f = data;
    CGP_INC_DECOMP_BUFF_POS;
}


/*******************
 * cgpWriteUintAt
 *
 * Writes an unsigned int to the decompression buffer at the
 *  specified position.
 *
 *  Input:
 *    CGuint idx   - the index in the decompression buffer to write to
 *    CGuint data
 *
 *  Output:
 *    None.
 *
 */

static
void cgpWriteUintAt(CGuint idx, CGuint data)
{
    Buff[idx] = data;
}


/*******************
 * cgpWritePointType
 *
 * Writes the current point type to the decompression buffer.
 *
 *  Input:
 *    None.
 *
 *  Output:
 *    None.
 *
 */

static
void cgpWritePointType()
{
    CGuint ptType = 0;

    if (bundling_color)
    {
        ptType |= CG_COLOR;

        if (doing_alpha)
            ptType |= CG_ALPHA;
    }

    if (bundling_norm)
        ptType |= CG_NORMAL;

    cgpWriteUint(ptType);
}


/*******************
 * cgpGetBuffIdx
 *
 * Gets the index that the decompression buffer is currently set
 *  to write to.
 *
 *  Input:
 *    None.
 *
 *  Returns:
 *    CGuint - the index to the current decompression buffer entry
 *
 */

static
CGuint cgpGetBuffIdx()
{
    return CurBuffPos;
}


/****************************************************************************
 *
 * Issue the current point
 *
 ****************************************************************************/
static void
sendVertex(CGuint vtx)
{
    CGfloat val;

    /* copy color, if present */
    if (bundling_color)
    {
        cgpWriteFloat(color[vtx].val[0]);
        cgpWriteFloat(color[vtx].val[1]);
        cgpWriteFloat(color[vtx].val[2]);

        if (doing_alpha)
            cgpWriteFloat(color[vtx].val[3]);
    }

    /* copy normal, if present */
    if (bundling_norm)
    {
        cgpWriteFloat(norm[vtx].val[0]);
        cgpWriteFloat(norm[vtx].val[1]);
        cgpWriteFloat(norm[vtx].val[2]);
    }

    /* copy vertex to buffer - put back into original poisition, first */
    val = geom[vtx].val[0];
    val *= vwp.scale;
    val += vwp.xo;
    cgpWriteFloat(val);

    val = geom[vtx].val[1];
    val *= vwp.scale;
    val += vwp.yo;
    cgpWriteFloat(val);

    val = geom[vtx].val[2];
    val *= vwp.scale;
    val += vwp.zo;
    cgpWriteFloat(val);

    /* one more vertex in this primitive */
    VtxCnt++;
}


/****************************************************************************
 *
 * copyCurVertex
 *
 * vtx - position in circular queue to copy current data to
 *
 * Copies the current vertex (data in cur* fields) to the appropriate
 * place in the appropriate circular queue.  The curcular queue is needed
 * to restart whenever the general tri mesh switches from a strip to a star
 * (or star to strip).
 ***************************************************************************/
static void
copyCurVertex(CGint vtx)
{
    geom[vtx].val[0] = curGeom.val[0];
    geom[vtx].val[1] = curGeom.val[1];
    geom[vtx].val[2] = curGeom.val[2];
             
    if (bundling_norm) {
        norm[vtx].val[0] = curNorm.val[0];
        norm[vtx].val[1] = curNorm.val[1];
        norm[vtx].val[2] = curNorm.val[2];

    }

    if (bundling_color) {
        color[vtx].val[0] = curColor.val[0];
        color[vtx].val[1] = curColor.val[1];
        color[vtx].val[2] = curColor.val[2];
        color[vtx].val[3] = curColor.val[3];
    }
}


/****************************************************************************
 *
 * issueVertex
 *
 * Determines if a vertex can be issued or stored on the circular
 * queue.  Until the third point is decompressed the replacment type
 * (which determines if this is a strip or star) is not known.
 */
static void
issueVertex(void)
{
    CGint tmp, i;

    if (PrimClass == PRIMCLASS_POINT)
    {
        /* draw every point */
        if (!inBegin)
        {
            cgpWriteUint(CG_CMD_POINTS);

            /* add point type */
            cgpWritePointType();

            /* save location of count filed */
            CntIdx = cgpGetBuffIdx();
            VtxCnt = 0;

            /* skip over count field - will be updated later */
            cgpWriteUint(0);

            /* mark that a begin has happened so count field will be updated */
            inBegin = 1;
        }

        copyCurVertex(0);
        sendVertex(0);

        return;
    }

    if (PrimClass == PRIMCLASS_LINE)
    {
        if (numPts == 0)
        {
            cgpWriteUint(CG_CMD_LINE_STRIP);

            /* add point type */
            cgpWritePointType();

            /* save location of count field */
            CntIdx = cgpGetBuffIdx();
            VtxCnt = 0;

            /* skip over count field - will be updated later */
            cgpWriteUint(0);

            copyCurVertex(0);
            sendVertex(0);
            numPts = 1;

            /* mark that a begin has happened so count field will be updated */
            inBegin = 1;
        }
        else
        {
            if (rep == REPLACE_MIDDLE || rep == REPLACE_OLDEST)
            {
                copyCurVertex(0);
                sendVertex(0);
            }
            else
            {
                /* have a move, starting a new line strip */

                /* write out vertex count of previous strip */
                cgpWriteUintAt(CntIdx, VtxCnt);

                /* add point type */
                cgpWritePointType();

                cgpWriteUint(CG_CMD_LINE_STRIP);

                /* save location of count filed */
                CntIdx = cgpGetBuffIdx();
                VtxCnt = 0;

                /* skip over count field - will be updated later */
                cgpWriteUint(0);

                copyCurVertex(0);
                sendVertex(0);
            }
        }

        return;
    }

    switch(numPts) {
        case 0:
            /* first point uncompressed */
            copyCurVertex(0);

            numPts = 1;

            return;

        case 1:
             /* second point uncompressed */
             copyCurVertex(1);

             numPts = 2;

             return;

        case 2:
            /* got three points */
            copyCurVertex(2);

            numPts = 3;

            /* save replacement value to check for strip/star switch */
            replacement = rep;

            /* send down this triangle */
            if (rep == REPLACE_MIDDLE)
                cgpWriteUint(CG_CMD_TRIANGLE_FAN);
            else
                cgpWriteUint(CG_CMD_TRIANGLE_STRIP);

            /* add point type */
            cgpWritePointType();

            /* save location of count field */
            CntIdx = cgpGetBuffIdx();
            VtxCnt = 0;

            /* skip over count field - will be updated later */
            cgpWriteUint(0);

            /* mark that a begin has happened so count field will be updated */
            inBegin = 1;

            for (i = 0; i < 3; i++) { 
                sendVertex(i);
            }

            /* set up pointers to triangle vertices */
            curPt = 0;
            oldPt = 1;
            midPt = 2;

             return;

        default:
            /* 4 or more points */
            copyCurVertex(curPt);

            /* has replacement value changed? */
            if (rep == replacement) {
                sendVertex(curPt);

               /* cycle vertex pointers */
               tmp = oldPt;
               oldPt = midPt;
               midPt = curPt;
               curPt = tmp;

               return;
            }

            /* something switched - have a new primitive */

            /* write out vertex count of previous strip */
            cgpWriteUintAt(CntIdx, VtxCnt);

            if (rep == RESTART_CW || rep == RESTART_CCW) {

                /* restarting new triangle */
                copyCurVertex(0);

                numPts = 1;

                /* no longer in a begin/end pair */
                inBegin = 0;

                return;
            }

            /* must be doing a strip/star switch */
            if (rep == REPLACE_MIDDLE)
                cgpWriteUint(CG_CMD_TRIANGLE_FAN);
            else
                cgpWriteUint(CG_CMD_TRIANGLE_STRIP);

            /* save this value */
            replacement = rep;

            /* add point type */
            cgpWritePointType();

            /* save location of count field */
            CntIdx = cgpGetBuffIdx();
            VtxCnt = 0;

            /* skip over count field - will be updated later */
            cgpWriteUint(0);

            /* send down previous two vertices */
            sendVertex(oldPt);
            sendVertex(midPt);

            sendVertex(curPt);

            /* update vertex position pointers */
            tmp = oldPt;
            oldPt = midPt;
            midPt = curPt;
            curPt = tmp;
    }
}

/*****************************************************************************
 *
 * process_decompression
 *
 */
static void
process_decompression()
{
    int mbp;

    current_header = next_header;

    if ((current_header & 0xC0) == GC_VERTEX) {

	if (!bundling_norm && !bundling_color) {
	    gc_in(8, &next_header);  /* get next opcode */
	    mbp = process_decompression_opcode(0);  /* pos */
	} else if (bundling_norm && !bundling_color) {
	    gc_in(6, &next_header);		/* get norm header */
	    mbp = process_decompression_opcode(0); /* pos */
	    current_header = next_header | GC_SET_NORM;
	    gc_in(8, &next_header);		/* get next opcode */
	    process_decompression_opcode(mbp); /* norm */
	} else if (!bundling_norm && bundling_color) {
	    gc_in(6, &next_header);		/* get color header */
	    mbp = process_decompression_opcode(0); /* pos */
	    current_header = next_header | GC_SET_COLOR;
	    gc_in(8, &next_header);		/* get next opcode */
	    process_decompression_opcode(mbp); /* color */
	} else {
	    gc_in(6, &next_header);		/* get norm header */
	    mbp = process_decompression_opcode(0); /* pos */
	    current_header = next_header | GC_SET_NORM;
	    gc_in(6, &next_header);		/* get color header */
	    process_decompression_opcode(mbp);	/* norm */
	    current_header = next_header | GC_SET_COLOR;
	    gc_in(8, &next_header);		/* get next opcode */
	    process_decompression_opcode(mbp); /* color */
	}

        issueVertex();

        /* to keep track of what data a mesh buffer ref should use */
        /* these bits get turned off in the set_color/normal routines */
        meshState |= USE_MESH_NORMAL;
        meshState |= USE_MESH_COLOR;
    } else {
	gc_in(8, &next_header);  /* get next opcode */
	process_decompression_opcode(0);  /* command */
    }

}  /* end of process_decompression */



/*****************************************************************************
 *  Decode the opcode in current_header, and dispatch the
 *  approprate processing routine.
 *  Also, return mbp if processing a vertex.
 */

static int
process_decompression_opcode(int mbp)
{

    if ((current_header & 0xC0) == GC_SET_NORM)
	process_set_normal(mbp);
    else if ((current_header & 0xC0) == GC_SET_COLOR)
	process_set_color(mbp);
    else if ((current_header & 0xC0) == GC_VERTEX)
	return process_vertex();
    else if ((current_header & 0xE0) == GC_MESH_B_R) {
	process_mesh_b_r();
        issueVertex();

        /* to keep track of what data a mesh buffer ref should use */
        /* these bits get turned off in the set_color/normal routines */
        meshState |= USE_MESH_NORMAL;
        meshState |= USE_MESH_COLOR;
    }
    else if ((current_header & 0xF8) == GC_SET_STATE)
	process_set_state();
    else if ((current_header & 0xF8) == GC_SET_TABLE)
	process_set_table();
    else if ((current_header & 0xFF) == GC_EOS)
	process_eos();
    else if ((current_header & 0xFF) == GC_V_NO_OP)
	process_v_no_op();
    
    return 0;

}  /* end of process_decompression_opcode */



/*****************************************************************************
 *  Process a set current normal command.
 */

static void
process_set_normal(int mbp)
{
    gc_table_e *gct;
    unsigned int   ii;
    int		index, du, dv, n, total_length;
    short	nx, ny, nz;

    /* if next command is a mesh buffer reference, use this normal */
    meshState &= ~USE_MESH_NORMAL;

    gct = &gctables[2][current_header & 0x3F];
    index = current_header & BMASK[6-gct->tag_length];

    if (gct->abs_rel) {
	/* read in the rest of the command */
        /* absolute always have sext/oct */
        total_length = n = gct->data_length * 2 + 6;
	gc_in((n - (6 - gct->tag_length)), &ii);
	index = (index << (n - (6 - gct->tag_length))) | ii;
        index &= BMASK[n];

	/* If absolute, just set current normal index to this value */
	cur_sex =  index >> (n - 3);
	cur_oct = (index >> (n - 6)) & 0x7;
	/* The number of u v bits within the index */
	n = gct->data_length;
	cur_u = (index >> n) & BMASK[n];
	cur_v = index & BMASK[n];

        /* shift up to proper position */
        cur_u <<= gct->right_shift;
        cur_v <<= gct->right_shift;

#ifdef PRINT_ONLY
	if (cur_sex < 6)
	    printf("N %d %d <%d %d>\n", cur_sex, cur_oct, cur_u, cur_v);
	else
	    printf("SN %d\n", index>>(11-(18-gct->data_length)));
#endif
    } else {
        /* read in the rest of the command */
	total_length = n = (gct->data_length - gct->right_shift) * 2;
        gc_in((n - (6 - gct->tag_length)), &ii);
	
	/* If relative, convert index to du,dv, update current normal index */

	/* Extract du and dv */
        index = (index << (n - (6 - gct->tag_length))) | ii;
	n = gct->data_length - gct->right_shift;
	du = (index >> n) & BMASK[n];
	dv = ii & BMASK[n];

#ifdef PRINT_ONLY
	printf("DN %d %d  (%d)\n", index>>n, index & BMASK[n], n);
#endif

	/* Sign extend du dv */
	du = (du<<(32-n));
	dv = (dv<<(32-n));
	du = du>>(32-n);
	dv = dv>>(32-n);

	du = du << gct->right_shift;
	dv = dv << gct->right_shift;

	/* Update cur_u, cur_v */
	cur_u += du; cur_v += dv;

        if (cur_u < 0)
        {
            switch (cur_sex)
            {
                case 0:
                    cur_sex = 4;
                    break;
                case 1:
                    cur_sex = 5;
                    break;
                case 2:
                    cur_sex = 3;
                    break;
                case 3:
                    cur_sex = 2;
                    break;
                case 4:
                    cur_sex = 0;
                    break;
                case 5:
                    cur_sex = 1;
                    break;

            }
            cur_u = -cur_u;
        }
        else if (cur_v < 0) {
            switch (cur_sex)
            {
                case 1:
                case 5:
                    cur_oct ^= 0x4;
                    break;

                case 0:
                case 4:
                    cur_oct ^= 0x2;
                    break;

                case 2:
                case 3:
                    cur_oct ^= 0x1;
                    break;
            }
            cur_v = -cur_v;
        }
        else if (cur_u + cur_v > 64)
        {
            switch (cur_sex)
            {
                case 0:
                    cur_sex = 2;
                    break;

                case 1:
                    cur_sex = 3;
                    break;

                case 2:
                    cur_sex = 0;
                    break;

                case 3:
                    cur_sex = 1;
                    break;

                case 4:
                    cur_sex = 5;
                    break;

                case 5:
                    cur_sex = 4;
                    break;
            }
            cur_u = 64 - cur_u;
            cur_v = 64 - cur_v;
        }

#ifdef PRINT_ONLY
	printf("DN %d %d (%d)\n", du, dv, total_length);
	printf("DN %d %d <%d %d>\n", cur_sex, cur_oct, cur_u, cur_v);
#endif
    }


    /* Do optional mesh buffer push */
    if (mbp) {
	mesh_buffer[mesh_buffer_p].sextant = cur_sex;
	mesh_buffer[mesh_buffer_p].octant = cur_oct;
	mesh_buffer[mesh_buffer_p].u = cur_u;
	mesh_buffer[mesh_buffer_p].v = cur_v;
    }

    /* Convert current normal index to nx ny nz */
    /* Convert normal back to -1.0 - 1.0 floating point from index */
    index_to_normal(cur_sex, cur_oct, cur_u, cur_v,
		    index<<(18 - total_length), (float *)&curNorm.val);

#ifdef JUST_PRINT_1
printf("set_normal %d (%d %d)\n", index, gct->tag_length, gct->data_length);
#endif

    /* a set normal when bundling is not happening is a global normal change */ 
    if (!bundling_norm)
    {
        cgpWriteUint(CG_CMD_SET_NORMAL);
        cgpWriteFloat(curNorm.val[0]);
        cgpWriteFloat(curNorm.val[1]);
        cgpWriteFloat(curNorm.val[2]);
    }

}  /* end of process_set_normal */



/****************************************************************************
 *  Process a set current color command.
 */

static void
process_set_color(int mbp)
{
    gc_table_e *gct;
    unsigned int ii;
    int r, g, b, a, index;
    short dr, dg, db, da;
    float fR, fG, fB, fA;
    int data_len;

    /* if next command is a mesh buffer reference, use this color */
    meshState &= ~USE_MESH_COLOR;

    gct = &gctables[1][current_header & 0x3F];

    /* true length of data */
    data_len = gct->data_length - gct->right_shift;

    r = current_header & BMASK[6 - gct->tag_length];

    if (gct->tag_length + data_len == 6) goto get_gba;
    if (gct->tag_length + data_len <  6) {
        r = r >> (6 - gct->tag_length - data_len);

        g = current_header & BMASK[6-gct->tag_length-data_len];
        if (gct->tag_length + 2*data_len == 6) goto get_ba;
        if (gct->tag_length + 2*data_len <  6) {
            g = g >> (6 - gct->tag_length - 2*data_len);

            b = current_header & BMASK[6-gct->tag_length-2*data_len];
            if (gct->tag_length + 3*data_len == 6) goto get_a;
            if (gct->tag_length + 3*data_len <  6) {
                b = b >> (6 - gct->tag_length - 3*data_len);

                if (doing_alpha) {
                    a = current_header &
                       BMASK[6 - gct->tag_length - 4*data_len];
                    if (gct->tag_length + 4 * data_len == 6) goto done;
                    if (gct->tag_length + 4 * data_len < 6) {
                        a = a >> (6 - gct->tag_length - 3*data_len);
                    }
                    else {
                        gc_in(data_len - (6-gct->tag_length - 3*data_len), &ii);
                        a = (a << (data_len -
                            (6-gct->tag_length - 3 * data_len))) | ii;
                        goto done;
                    }
                } 
            } else {
                gc_in(data_len - (6 - gct->tag_length - 2*data_len), &ii);
                b = (b << (data_len -
                        (6 - gct->tag_length - 2*data_len))) | ii;
                goto get_a;
            }
        } else {
            gc_in(data_len - (6 - gct->tag_length - data_len), &ii);
            g = (g << (data_len -
                   (6 - gct->tag_length - data_len))) | ii;
            goto get_ba;
        }
    } else {
        gc_in(data_len - (6 - gct->tag_length), &ii);
        r = (r << (data_len - (6 - gct->tag_length))) | ii;
      get_gba:
        gc_in(data_len, (unsigned int *) &g);
      get_ba:
        gc_in(data_len, (unsigned int *) &b);

      get_a:
        if (doing_alpha)
            gc_in(data_len, (unsigned int *) &a);
    }

done:

#ifdef PRINT_ONLY
    printf("color %d %d %d %d (%d %d)\n", r, g, b, a,
	gct->tag_length, gct->data_length);
#endif

    /* Sign extend delta x y z components */
    r <<= (32 - data_len);  r >>= (32 - data_len);
    g <<= (32 - data_len);  g >>= (32 - data_len);
    b <<= (32 - data_len);  b >>= (32 - data_len);
    a <<= (32 - data_len);  a >>= (32 - data_len);

    /* Normalize values */
    dr = r << gct->right_shift;
    dg = g << gct->right_shift;
    db = b << gct->right_shift;
    da = a << gct->right_shift;

    /* Update current position, first adding deltas, if in relative mode */
    if (gct->abs_rel) {
        cur_r = dr; cur_g = dg; cur_b = db;
        if (doing_alpha)
            cur_a = da;
    } else {
        cur_r += dr; cur_g += dg; cur_b += db;
        if (doing_alpha)
            cur_a += da;
    }

#ifdef JUST_PRINT_1
    printf("Color %d %d %d %d\n", cur_r, cur_g, cur_b, cur_a);
#endif
    /* Do optional mesh buffer push */
    if (mbp) {
	mesh_buffer[mesh_buffer_p].r = r;
	mesh_buffer[mesh_buffer_p].g = g;
	mesh_buffer[mesh_buffer_p].b = b;

        if (doing_alpha)
	    mesh_buffer[mesh_buffer_p].a = a;
        else
	    mesh_buffer[mesh_buffer_p].a = cur_a;
    }

    /* Convert point back to -1.0 - 1.0 floating point */
    fR = cur_r; fR /= 32768.0;
    fG = cur_g; fG /= 32768.0;
    fB = cur_b; fB /= 32768.0;
    fA = cur_a; fA /= 32768.0;
#ifdef JUST_PRINT_1
printf("C(%f %f %f %f)\n", fR, fG, fB, fA);
#endif

    curColor.val[0] = fR;
    curColor.val[1] = fG;
    curColor.val[2] = fB;
    curColor.val[3] = fA;

    /* a set color when bundling is not happening is a global color change */ 
    if (!bundling_color)
        if (doing_alpha)
        {
            cgpWriteUint(CG_CMD_SET_COLOR4);
            cgpWriteFloat(curColor.val[0]);
            cgpWriteFloat(curColor.val[1]);
            cgpWriteFloat(curColor.val[2]);
            cgpWriteFloat(curColor.val[3]);
        }
        else
        {
            cgpWriteUint(CG_CMD_SET_COLOR3);
            cgpWriteFloat(curColor.val[0]);
            cgpWriteFloat(curColor.val[1]);
            cgpWriteFloat(curColor.val[2]);
        }

}  /* end of process_set_color */



/****************************************************************************
 *  Process a vertex command.
 *  Any bundled normal and/or color will be processed by separate routines.
 *  Return the mesh buffer push indicator.
 */

static int
process_vertex()
{
    gc_table_e *gct;
    unsigned int    ii;
    int    mbp;
    int    x, y, z;
    int    sign_bit;
    short  dx, dy, dz;
    float  fX, fY, fZ; 
    int data_len;

    /* if next command is a mesh buffer reference use colors/normals from */
    /* mesh buffer */
    meshState = 0;

    /* Get a pointer the approprate tag table entry */
    gct = &gctables[0][current_header & 0x3F];

    /* get true length of data */
    data_len = gct->data_length - gct->right_shift;

    /* Read in the replace code and mesh buffer push bits, if not in header */
    if (6 - (3 * data_len) - gct->tag_length > 0)
    {
        int numBits = 6 - (3 * data_len) - gct->tag_length;
        int jj;

        jj = current_header & BMASK[numBits];
        gc_in(3 - numBits, &ii);
        ii |= (jj <<  (3 - numBits));
    }
    else
        gc_in(3, &ii);

    rep = ii >> 1;
    mbp = ii & 0x1;

    x = current_header & BMASK[6-gct->tag_length];

    if (gct->tag_length + data_len == 6) goto get_yz;
    if (gct->tag_length + data_len <  6) {
	x = x >> (6 - gct->tag_length - data_len);

	y = current_header & BMASK[6-gct->tag_length-data_len];
	if (gct->tag_length + 2*data_len == 6) goto get_z;
	if (gct->tag_length + 2*data_len <  6) {
	    y = y >> (6 - gct->tag_length - 2*data_len);

	    z = current_header & BMASK[6-gct->tag_length-2*data_len];
	    if (gct->tag_length + 3*data_len == 6) goto done;
	    if (gct->tag_length + 3*data_len <  6) {
		z = z >> (6 - gct->tag_length - 3*data_len);
	    } else {
		gc_in(data_len -
		      (6 - gct->tag_length - 2*data_len),
		      &ii);
		z = (z << (data_len - (6 - gct->tag_length - 2*data_len))) | ii;
		    goto done;
	    }
	} else {
	    gc_in(data_len - (6 - gct->tag_length - data_len),
		  &ii);
	    y = (y << (data_len - (6 - gct->tag_length - data_len))) | ii;
	    goto get_z;
	}
    } else {
	gc_in(data_len - (6 - gct->tag_length), &ii);
	x = (x << (data_len - (6 - gct->tag_length))) | ii;
      get_yz:
	gc_in(data_len, (unsigned int *) &y);
      get_z:
	gc_in(data_len, (unsigned int *) &z);
    }

  done:

#ifdef PRINT_ONLY
    printf("vertex %d %d %d (%d %d)\n", x, y, z, gct->tag_length, gct->data_length);
#endif

    /* Sign extend delta x y z components */
    x = x << (32 - data_len); x = x >> (32 - data_len);
    y = y << (32 - data_len); y = y >> (32 - data_len);
    z = z << (32 - data_len); z = z >> (32 - data_len);

    /* Normalize values */
    dx = x << gct->right_shift;
    dy = y << gct->right_shift;
    dz = z << gct->right_shift;

    /* Update current position, first adding deltas, if in relative mode */
    if (gct->abs_rel) {
	cur_x = dx; cur_y = dy; cur_z = dz;
    } else {
	cur_x += dx; cur_y += dy; cur_z += dz;
    }

#ifdef JUST_PRINT_1
    printf("Vertex %d %d %d\n", cur_x, cur_y, cur_z);
#endif

    /* Do optional mesh buffer push */
    if (mbp) {
        /* increment to next position (mesh_buffer_p is initialized to 15) */
	mesh_buffer_p = (mesh_buffer_p + 1) & 0xF;
	mesh_buffer[mesh_buffer_p].x = cur_x;
	mesh_buffer[mesh_buffer_p].y = cur_y;
	mesh_buffer[mesh_buffer_p].z = cur_z;

      
	  /*mesh_buffer_p = (mesh_buffer_p + 1) & 0xF; */
    }

    /* Convert point back to -1.0 - 1.0 floating point */
    fX = cur_x; fX /= 32768.0;
    fY = cur_y; fY /= 32768.0;
    fZ = cur_z; fZ /= 32768.0;

#ifdef JUST_PRINT_1
printf("V(%f %f %f) {%d}\n", fX, fY, fZ, rep);
#endif

    curGeom.val[0] = fX;
    curGeom.val[1] = fY;
    curGeom.val[2] = fZ;

    return mbp;

}  /* end of process_vertex */



/****************************************************************************
 *  Process a mesh buffer reference command.
 */

static void
process_mesh_b_r()
{
    int  index;
    gc_mesh_buffer_e *entry;
    int normal;
    unsigned int ii;

    gc_in(1, &ii);

    index = (current_header >> 1) & 0xF;
    rep = ((current_header & 0x1) << 1) | ii;

    /* adjust index to proper place in fifo */
    index = ((mesh_buffer_p + 1) - index) & 0xf;

    /* Point to mesh buffer element */
    entry = &mesh_buffer[index];

    cur_x = entry->x;
    cur_y = entry->y;
    cur_z = entry->z;
    /* Convert point back to -1.0 - 1.0 floating point */
    curGeom.val[0] = cur_x; curGeom.val[0] /= 32768.0;
    curGeom.val[1] = cur_y; curGeom.val[1] /= 32768.0;
    curGeom.val[2] = cur_z; curGeom.val[2] /= 32768.0;

#ifdef JUST_PRINT_1
    printf("V(%f %f %f) {%d}\n", curGeom.val[0], curGeom.val[1],
            curGeom.val[2], rep);
#endif
    /* get mesh buffer normal if previous command was not a setnormal */
    if (bundling_norm && meshState & USE_MESH_NORMAL) {
	cur_sex = entry->sextant;
	cur_oct = entry->octant;
	cur_u = entry->u;
	cur_v = entry->v;
    /* Convert current normal index to nx ny nz */
    /* Convert normal back to -1.0 - 1.0 floating point from index */
	normal = (cur_sex<<15) | (cur_oct<<12) | (cur_u << 6) | cur_v;
	index_to_normal(cur_sex, cur_oct, cur_u, cur_v,
			normal, curNorm.val);
    }
    if (bundling_color && meshState & USE_MESH_COLOR) {
	cur_r = entry->r;
	cur_g = entry->g;
	cur_b = entry->b;

        /* Convert point back to -1.0 - 1.0 floating point */
        curColor.val[0] = cur_r; curColor.val[0] /= 32768.0;
        curColor.val[1] = cur_g; curColor.val[1] /= 32768.0;
        curColor.val[2] = cur_b; curColor.val[2] /= 32768.0;

        if (doing_alpha)
        {
            cur_a = entry->a;
            curColor.val[3] = cur_a; curColor.val[3] /= 32768.0;
        }
    }

    /* reset meshState */
    meshState = 0;

#ifdef JUST_PRINT_1
    printf("mesh_b_r %2d %d\n", index, rep);
#endif

}  /* end of process_mesh_b_r */



/****************************************************************************
 *  Process a set state command.
 */

static void
process_set_state()
{
    unsigned  int  ii;

    gc_in(3, &ii);

    bundling_norm    = current_header & 0x1;
    bundling_color   = (ii >> 2) & 0x1;
    doing_alpha       = (ii>> 1) & 0x1;

#ifdef JUST_PRINT_1
    printf("set_state %d %d %d\n", bundling_norm, bundling_color, doing_alpha);
#endif

}  /* end of process_set_state */


/*****************************************************************************
 *  Process a set decompression table command.
 *
 *  Extract the parameters of the table set command,
 *  and set the approprate table entries.
 */

static void
process_set_table()
{
    gc_table_e *gct;
    int i;
    unsigned  int  ii;
    int adr;
    int tag_length;
    int data_length;
    int right_shift;
    int abs_rel;

    /* Point to approprate 64 entry table (pos, norm, or color */
    gct = &gctables[(current_header & 0x6)>>1][0];

    /* Get the remaining bits of the set table command */
    /* djp: changed to reflect spec */
    gc_in(15, &ii);

    /* Extract the individual fields from the two bit strings */
    /* djp: changed to reflect spec */
    adr = ((current_header & 0x1)<<6) | ((ii >> 9) & 0x3F);

    /* djp: changed to reflect spec */
    data_length = (ii >> 5) & 0x0F;
    if (data_length == 0)
        data_length = 16;

    right_shift = ii & 0x0F;
    abs_rel = (ii >> 4) & 0x1;

    /*
     * Decode the tag length from the address field by finding the
     * first set 1 from the left in the bitfield.
     */
    for (tag_length = 6; tag_length > 0; tag_length--) {
	if ((adr >> tag_length)) break;
    }

    /* Shift the address bits up into place, and And off the leading 1 */
    adr = (adr << (6 - tag_length)) & 0x3F;

#ifdef JUST_PRINT_1
    printf("set_table %d %d %d %d %d %d\n",
	(current_header & 0x6)>>1, adr, tag_length, data_length, right_shift, abs_rel);
#endif

    /* Fill in the table fields with the specified values */
    for (i = 0; i < (1 << (6 - tag_length)); i++) {
	gct[adr+i].tag_length = tag_length;
	gct[adr+i].data_length = data_length;
	gct[adr+i].right_shift = right_shift;
	gct[adr+i].abs_rel = abs_rel;
    }

}  /* end of process_set_table */



/*****************************************************************************
 *  Process a end-of-stream command.
 *  Terminates processing, for now.
 */

static void
process_eos()
{

#ifdef JUST_PRINT_1
    printf("end-of-stream\n");
#endif

}  /* end of process_eos */



/*****************************************************************************
 *  Process a variable length no-op command.
 */

static void
process_v_no_op()
{
    unsigned int ii, ct;

    gc_in(5, &ct);
    gc_in(ct, &ii);

#ifdef JUST_PRINT_1
    printf("v_no_op %d\n", ct);
#endif

}  /* end of process_v_no_op */



/****************************************************************************/
static void
gc_in(int bit_ct, unsigned int *bit_place)
{
    unsigned int *ii;

    if (bit_acc_valid == 0) {
	ii = (unsigned int *) &gciobuf[gciobufp];
	bit_acc = *ii;
        gciobufp += 4;
	bit_acc_valid = 32;
    }

    if (bit_acc_valid >= bit_ct) {
	*bit_place = (bit_acc >> (32 - bit_ct)) & BMASK[bit_ct];
	bit_acc = bit_acc << bit_ct;
	bit_acc_valid -= bit_ct;
    } else {
	*bit_place = (bit_acc >> (32 - bit_ct)) & BMASK[bit_ct];
	*bit_place = *bit_place >> (bit_ct - bit_acc_valid);
	*bit_place = *bit_place << (bit_ct - bit_acc_valid);
	ii = (unsigned int *) &gciobuf[gciobufp];
	bit_acc = *ii;
        gciobufp += 4;
	*bit_place = *bit_place |
		( (bit_acc >> (32 - (bit_ct - bit_acc_valid))) &
		  BMASK[bit_ct - bit_acc_valid]);
	bit_acc = bit_acc << (bit_ct - bit_acc_valid);
	bit_acc_valid = 32 - (bit_ct - bit_acc_valid);
    }
/*printf("bi #%d, 0x%x\n", bit_ct, *bit_place);*/
/*printf("bit_acc 0x%8x  bit_acc_valid %2d  bit_place 0x%8x\n",
	bit_acc, bit_acc_valid, *bit_place);*/

}  /* end of gc_in */


/****************************************************************************/
static void decompressData(char *begin, char *end)
{
    gciobufp = 0;
    gciobufe = end - begin;
    gciobuf = begin;

    bit_acc_valid = 0;
    next_header = GC_V_NO_OP;
    meshState = 0;
    while (gciobufe >= gciobufp)
	process_decompression();

}  /* end of decompressData */


/*****************************************************************************
 *
 * cgDecompressGeometryStream
 *
 * 
 *
 */
void
cgDecompressGeometryStream(CGubyte* cgBuff, CGuint **uData, CGuint *size)
{
    char*    end;
    CGubyte* bytePtr;
    CGfloat* fVal;
    CGint *  lenPtr;
    CGint    len;

    if (cgBuff == NULL)
        return;

    /* get size of buffer from the header */
    bytePtr = cgBuff;
    bytePtr += 44;
    lenPtr = (CGint*) bytePtr;
    len = *lenPtr;

    end = (char *) cgBuff + len * sizeof (CGubyte);

    /* allocate initial decompression buffer */
    Buff = (CGuint*) malloc(sizeof(CGuint) * CGP_DECOMP_BUFF_SIZE);
    BuffSize = CGP_DECOMP_BUFF_SIZE;
    VtxCnt = 0;

    /* reset flags */
    inBegin = 0;
    replacement = REPLACE_OLDEST;
    numPts = 0;

    bytePtr = cgBuff + 3;
    PrimClass = *bytePtr;

    /* get viewport structure */
    bytePtr = cgBuff;
    bytePtr += 28;
    fVal = (CGfloat *) bytePtr;
    vwp.scale = *fVal++;
    vwp.xo = *fVal++;
    vwp.yo = *fVal++;
    vwp.zo = *fVal;

    /* skip over header info */
    cgBuff += 48;

    /* reset some globals */
    bundling_norm    = 0;
    bundling_color   = 0;
    doing_alpha       = 0;

    decompressData((char *) cgBuff, end);

    /* restore original winding */

    if (inBegin)
        /* write out vertex count */
        cgpWriteUintAt(CntIdx, VtxCnt);

    /* resize data and send back to user */
    Buff = (CGuint*) realloc(Buff, sizeof(CGuint) * CurBuffPos);
    *uData = Buff;
    *size = CurBuffPos;
}
