#pragma ident "@(#)cgpbuildbuffer.c	1.25 98/08/25 SMI"

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


/******************************************************************************
 * cgpbuildbuffer.c
 *
 * This file contains all the routines to convert a command stream of data
 * to the compressed geometry buffer format.
 *
 *  List of functions in this file:
 *	cgpBuildCompressedBuffer
 *      cgpIssueMBR
 *	cgpIssueSetColor
 *	cgpIssueSetColor4
 *	cgpIssueSetNormal
 *	cgpIssueSetState
 *	cgpIssueSetTable
 *	cgpIssueVertex
 *	cgpIssueVnop
 *	cgpWriteBitField
 *	cgpWriteData
 *	cgpWriteHeader
 *	cgpWriteStack
 *
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cgpi.h"
#include "cgpstream.h"
#include "cgpbuildbuffer.h"
#include "zdebug.h"

/* routines used only in this module */
static CGvoid cgpWriteStack();
static CGvoid cgpWriteHeader(CGubyte, CGubyte);
static CGvoid cgpWriteBitField(CGint, CGulongint);
static CGvoid cgpWriteData(CGint, CGuint);
static CGvoid cgpIssueMBR(CGPmbr *);
static CGvoid cgpIssueSetColor(CGPsetcolor *, CGboolean);
static CGvoid cgpIssueSetColor4(CGPsetcolor *, CGboolean);
static CGvoid cgpIssueSetNormal(CGPsetnormal *, CGboolean);
static CGvoid cgpIssueSetState(CGPsetstate *);
static CGvoid cgpIssueSetTable(CGPsettable *);
static CGvoid cgpIssueVertex(CGPvertex *);
static CGvoid cgpIssueVnop(CGPvnop *, CGboolean);

/* 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 CGint       bitLen[16];
static CGuint      bitStack[16];
static CGint       numStackEntries;
static CGulongint* buffHdr;
static CGulongint* curBuffEntry;
static CGuint      cmpBuffSize;
static CGint       buffBitsFree;
static CGint       buffEntriesUsed;
static CGuint      curState;

extern int CurrBuffRefs[10000];
static int CurrBuffIndex = 0;

/* gmesh cg buffer usage statitics */
int TotalBuffSize = 0;
int BuffTableSize;
int BuffNormSize;
int BuffVertSize;
int BuffMBRSize;

/*******************
 * cgpBuildCompressedBuffer
 *
 * Walks through a command stream and builds a compressed geometry
 * buffer.
 *
 *  Input:
 *    CGPcommandstream* cmdBuff 	- commands to process
 *    CGenum            primClass	- class of primitives in buffer:
 *            					CGP_CLASS_POINT
 *						CGP_CLASS_LINE
 *						CGP_CLASS_FILL
 *    CGboolean         normalPresent   - TRUE if a normal is present
 *                                        for the first buffer
 *    CGboolean         colorPresent   - TRUE if a color is present
 *                                        for the first buffer
 *    CGboolean         alphaPresent    - TRUE if color alpha is present
 *                                        for the first buffer
 *    CGviewport*     	viewport	- scale and offset values to restore
 *					  data to its original location  
 *
 *  Output:
 *    CGubyte** compressedBuff	- will point to the compressed geometry buffer
 *    CGuint*   buffSize        - size of the buffer (including header)
 *
 */

CGvoid
cgpBuildCompressedBuffer(CGPcommandstream *cmdBuff, CGenum primClass,
                         CGboolean normalPresent, CGboolean colorPresent,
                         CGboolean alphaPresent, CGviewport *viewport,
                         CGubyte **compressedBuff, CGint *buffSize)
{
    CGint i, noopBits;
    CGPcommandstream *curCmd;
    CGPvnop vnop, vnop1, vnop2;
    CGubyte *bytePtr;    /* to set fields in buffer header */
    CGfloat *fVal;
    CGint   *iVal;

    curCmd = cmdBuff;
    CurrBuffIndex = 0;

    cmpBuffSize = CGP_COMPRESS_GEOM_BUFFER_SIZE;
    curBuffEntry = buffHdr = (CGulongint *)
        malloc(sizeof(CGulongint) * cmpBuffSize);

    /* zero out 48 byte header */
    memset((char*)buffHdr, 0, 48);

    buffBitsFree = CGP_COMPRESS_BUFFER_BIT_WIDTH;

    /* add buffer type */
    bytePtr = (CGubyte *)buffHdr;

    /* set version number */
    *bytePtr++ = CG_MAJOR_VN;
    *bytePtr++ = CG_MINOR_VN;
    *bytePtr++ = CG_MINOR_MINOR_VN;

    /* add buffer type - 4th byte of header */
    *bytePtr++ = (CGubyte) primClass;

    /* add the data present bits - 5th byte */
    if (normalPresent)
        *bytePtr |= 0x1;

    if (colorPresent)
        *bytePtr |= 0x2;

    if (alphaPresent)
        *bytePtr |= 0x4;

    /* add viewport info */
    bytePtr = (CGubyte *) buffHdr;
    bytePtr += 36;
    fVal = (CGfloat *) bytePtr;
    *fVal++ = viewport->scale;
    *fVal++ = viewport->xo;

    /* skip over header */
    buffEntriesUsed = 6;
    curBuffEntry += 6;
    *curBuffEntry = 0;

    curState = 0;

    /* first command is an implicit VNOP header - add VNOP data to stack */
    bitStack[0] = 0;
    bitLen[0] = 5;
    numStackEntries = 1;

    while (curCmd->dataType != CGP_STREAM_CMD_NULL)
    {
        switch (curCmd->dataType)
        {
            case CGP_STREAM_CMD_VTX:
                cgpIssueVertex((CGPvertex *)curCmd->data);

                if (curState & CGP_STATE_BNV)
                {
                    /* next command is a set normal */
                    curCmd++;
                    cgpIssueSetNormal((CGPsetnormal *)curCmd->data, CGP_TRUE);
                }
                if (curState & CGP_STATE_BCV)
                {
                    /* next command must be a set color */
                    curCmd++;
                    cgpIssueSetColor((CGPsetcolor *)curCmd->data, CGP_TRUE);
                }
                break;

            case CGP_STREAM_CMD_SET_TABLE:
                cgpIssueSetTable((CGPsettable *)curCmd->data);
                break;

            case CGP_STREAM_CMD_SET_STATE:
                cgpIssueSetState((CGPsetstate *)curCmd->data);
                break;

            case CGP_STREAM_CMD_SET_COLOR:
                cgpIssueSetColor((CGPsetcolor *)curCmd->data, CGP_FALSE);
                break;

            case CGP_STREAM_CMD_VNOP:
                cgpIssueVnop((CGPvnop *)curCmd->data, CGP_TRUE);
                break;

            case CGP_STREAM_CMD_MBR:
                cgpIssueMBR((CGPmbr *) curCmd->data);
                break;

            case CGP_STREAM_CMD_SET_QUANT_GEOM:
            case CGP_STREAM_CMD_SET_QUANT_COLOR:
            case CGP_STREAM_CMD_SET_QUANT_NORMAL:

                /* nothing to issue here */
                free(curCmd->data);
                break;

            case CGP_STREAM_CMD_SET_NORMAL:
                cgpIssueSetNormal((CGPsetnormal *)curCmd->data, CGP_FALSE);
                break;

            default:
                fprintf(stderr, "Ack, don't know this command\n");
        }

        /* advance to next command */
        curCmd++;
    }

    /* now add VNOP - padding for bit alignment */

    /* find out how many bits to be written are on the stack */
    for (noopBits = i = 0; i < numStackEntries; i++)
        noopBits += bitLen[i];

    /*
     * The maximum VNOP length is 31 - we want everything to
     * align to 64 bit boundaries.  Worst case we will have to
     * write 63 0's to align the data - this will take 3
     * VNOP instructions.
     */

    /*
     * add the bits for 4 VNOP headers (3 for worst case alignment
     * and 1 to seed decopression for the next buffer) and 3 VNOP
     * data fields
     */
    noopBits += 47;

    noopBits -= buffBitsFree;

    /* remove all whole words left to write */
    noopBits %= CGP_COMPRESS_BUFFER_BIT_WIDTH;

    /* to be < 0 all data will fit in current word */
    if (noopBits < 0)
        vnop.numZeros = -noopBits;
    else
        vnop.numZeros = CGP_COMPRESS_BUFFER_BIT_WIDTH - noopBits;

    /* data will be aligned with no zeros anyway */
    if (vnop.numZeros == CGP_COMPRESS_BUFFER_BIT_WIDTH)
        vnop.numZeros = 0;

    /*
     * find out how many VNOP data fields are needed to produce
     * the needed padding 
     */
    if (vnop.numZeros > 62)
    {
        /* need to use 3 VNOP's to align data */
        vnop.numZeros = 31;
        vnop1.numZeros = 31;
        vnop2.numZeros = 1;
     }
     else if (vnop.numZeros > 31)
     {
          /* need to divide up the VNOP among two VNOP instructions */
          vnop1.numZeros = vnop.numZeros - 31;
          vnop.numZeros = 31;
          vnop2.numZeros = 0;
      }
      else
      {
          vnop1.numZeros = 0;
          vnop2.numZeros = 0;
      }

    /* send down VNOP instructions */
    cgpIssueVnop(&vnop, CGP_FALSE);
    cgpIssueVnop(&vnop1, CGP_FALSE);
    cgpIssueVnop(&vnop2, CGP_FALSE);

    /* must send VNOP header to seed for next buffer */
    vnop.numZeros = 0;
    cgpIssueVnop(&vnop, CGP_FALSE);
 
    /* size is in bytes +1 because buffEntriesUsed incremented when */
    /*  next entry is needed                                        */
    *buffSize = (buffEntriesUsed +1) * (CGP_COMPRESS_BUFFER_BIT_WIDTH / 8);

    /* realloc buffer to be proper size */
    buffHdr = (CGulongint*) realloc(buffHdr, *buffSize);

    *compressedBuff = (CGubyte *) buffHdr;

    /* add compressed geometry size (in bytes) to header */
    /*  the header is included in the size           */
    bytePtr = (CGubyte *) buffHdr;
    bytePtr += 44;
    iVal = (CGint*) bytePtr;
    *iVal = *buffSize;

    /* no longer need command stream */
    free(cmdBuff);
}


/*******************
 * cgpWriteBitField
 *
 * Writes a series of bits to the current position in the compressed
 * geometry buffer.
 *
 *  Input:
 *    CGint  numBits	- number of bits to be written
 *    CGuint data	- data to be written (right justified - 0's to the left)
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpWriteBitField(CGint numBits, CGulongint data)
{
  TotalBuffSize += numBits;
    if (numBits == 0)
        return;

    if (buffBitsFree >= numBits)
    {
        /* current data will fit in current buffer entry */
        buffBitsFree -= numBits;
        *curBuffEntry |= (data << buffBitsFree);
    }
    else
    {
        /* have to chop this up among 2 words */

        /* fill in open bits in this word */
        numBits -= buffBitsFree;
        *curBuffEntry |= (data >> numBits);

        /* get next word */
        if (++buffEntriesUsed == cmpBuffSize)
        {
            /* ran out of room - get more space */
            cmpBuffSize += CGP_COMPRESS_GEOM_BUFFER_SIZE;
            buffHdr = (CGulongint *)
	      realloc(buffHdr, sizeof(CGulongint) * cmpBuffSize);
            curBuffEntry = &buffHdr[buffEntriesUsed];
        }
        else
        {
            /* advance to next buffer entry */
            curBuffEntry++;
        }

        /* clear current buffer entry */
        *curBuffEntry = 0;


        /* put in remiaing bits */
        buffBitsFree = CGP_COMPRESS_BUFFER_BIT_WIDTH - numBits;
        *curBuffEntry = (data << buffBitsFree);
    }
}


/*******************
 * cgpWriteHeader
 *
 * Writes the header word (the first 6 or 8 bits of a command) and then
 * writes out any data put on the stack by the precious instruction.
 *
 *  Input:
 *    CGubyte	numBits	- number of bits in the header (either 6 or 8)
 *    CGubyte	hdr	- the header to write to the compressed buffer
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpWriteHeader(CGubyte numBits, CGubyte hdr)
{
    /* a header must either be 6 or 8 bits in length */
    cgpWriteBitField(numBits, (CGuint) hdr);

    cgpWriteStack();
}


/*******************
 * cgpWriteHeader
 *
 * Writes out all entries on the stack to the compressed geometry
 * buffer.
 *
 *  Input:
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpWriteStack(CGvoid)
{
    CGint i;

    for (i = 0; i < numStackEntries; i++)
        cgpWriteBitField(bitLen[i], bitStack[i]);

    numStackEntries = 0;
}


/*******************
 * cgpWriteData
 *
 * Writes an entry to the stack.  This data will be written to the compressed
 * geometry buffer at a later time (after the next instruction's header is
 * written) 
 *
 *  Input:
 *    CGint  numBits	- number of bits in this entry
 *    CGuint data	- data to be written (right justified - 0's to the left)
 *
 *  Output:
 *    None.
 *
 */


CGvoid
cgpWriteData(CGint numBits, CGuint data)
{
    bitLen[numStackEntries] = numBits;
    bitStack[numStackEntries++] = data;
}


/*******************
 * cgpIssueSetState
 *
 * Issues a set state command.  A SetState command controls if color/normal
 * information is being sent per vertex and if alpha is present in the color.
 *
 *  Input:
 *    CGPsetstate* setState - the SetState command to send down
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpIssueSetState(CGPsetstate *setState)
{
    CGubyte bits;

    curState = 0;
    bits = CGP_SET_STATE;

    if (setState->state & CGP_CMD_SET_STATE_RNV)
    {
        bits |= 0x4;
    }

    if (setState->state & CGP_CMD_SET_STATE_RCV)
    {
        bits |= 0x2;
    }

    if (setState->state & CGP_CMD_SET_STATE_BNV)
    {
        curState |= CGP_STATE_BNV;
        bits |= 0x1;
    }

    cgpWriteHeader(8, bits);

    /* re-use bits since only have 4 bits left to worry about */
    bits = 0;

    if (setState->state & CGP_CMD_SET_STATE_CAP)
    {
        curState |= CGP_STATE_CAP;
        bits |= 0x2;
    }

    if (setState->state & CGP_CMD_SET_STATE_BCV)
    {
        curState |= CGP_STATE_BCV;
        bits |= 0x4;
    }


    cgpWriteData(3, (CGuint) bits);

    /* get rid of this data */
    free(setState);
}


/*******************
 * cgpIssueSetTable
 *
 * Issues a set table command.  A SetTable command loads an entry into
 * the huffman table.
 *
 *  Input:
 *    CGPsettable* setTable - the SetTable command to send down
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpIssueSetTable(CGPsettable *setTable)
{
    CGubyte     hdr;
    CGPsettable *tmpTable = setTable;

    while (setTable)
    {
        hdr = CGP_SET_TABLE;
	BuffTableSize += 23;

        hdr |= (setTable->table << 1);
        hdr |= (setTable->address >> 6);

if (setTable->address == 0x1 && !getenv("HACK"))
    fprintf(stderr, "Table %1d writing 64 entries\n", setTable->table);

        cgpWriteHeader(8, hdr);

        cgpWriteData(6, setTable->address & 0x3f);

        /* a dataLen of 16 is represented as 0 */
        cgpWriteData(4, setTable->dataLen & 0xf);

        cgpWriteData(1, setTable->abs);
        cgpWriteData(4, setTable->shift);

        setTable = setTable->nextTableElement;
    }

    free(tmpTable);
}


/*******************
 * cgpIssueVertex
 *
 * Issues a vertex command.  A Vertex command sends down geometry information.
 * 
 *
 *  Input:
 *    CGPvertex* vtx
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpIssueVertex(CGPvertex *vtx)
{
    CGubyte hdr, meshHdr;
    CGint hdrBitsRemaining;
    CGshort gLen = vtx->geomLen - vtx->geomShift;
    CGint numVtx;
    CGint bitsRemaining;
    int temp;

    temp = (int) (gLen *3 + vtx->geomTagLen);

    /* Valid value */
    if ((temp > 0) && (temp < 200)) 
      BuffVertSize += temp;
    if (gLen <= 0)
      gLen = 1;

    hdr = CGP_VERTEX;
    hdrBitsRemaining = 6;

    hdrBitsRemaining -= vtx->geomTagLen;
    hdr |= (vtx->geomTag <<  hdrBitsRemaining);

    numVtx = hdrBitsRemaining / gLen;

    /* if greater than max set to max value */
    if (numVtx > 3)
        numVtx = 3;

    zdo6 printf("issuing vertex\n");

    switch (numVtx)
    {
        case 3:
            /* all data fits in header word! */
            hdrBitsRemaining -= gLen;
            hdr |= (((vtx->ix >> vtx->geomShift) &
              BMASK[gLen]) << hdrBitsRemaining);

            hdrBitsRemaining -= gLen;
            hdr |= (((vtx->iy >> vtx->geomShift) &
              BMASK[gLen]) << hdrBitsRemaining);

            hdrBitsRemaining -= gLen;
            hdr |= (((vtx->iz >> vtx->geomShift) &
              BMASK[gLen]) << hdrBitsRemaining);

        break;

        case 2:
            hdrBitsRemaining -= gLen;
            hdr |=
             (((vtx->ix >> vtx->geomShift) & BMASK[gLen]) << hdrBitsRemaining);

            hdrBitsRemaining -= gLen;
            hdr |= (((vtx->iy >> vtx->geomShift) &
              BMASK[gLen]) << hdrBitsRemaining);

            if (hdrBitsRemaining)
            {
                bitsRemaining = gLen - hdrBitsRemaining;
                hdr |= (((vtx->iz >> vtx->geomShift) &
                  BMASK[gLen]) >> bitsRemaining);
                hdrBitsRemaining = 0;
            }
            else
                bitsRemaining = gLen; 
        break;

        case 1:
            hdrBitsRemaining -= gLen;
            hdr |= (((vtx->ix >> vtx->geomShift) &
              BMASK[gLen]) << hdrBitsRemaining);

            if (hdrBitsRemaining)
            {
                bitsRemaining = gLen - hdrBitsRemaining;
                hdr |= (((vtx->iy >> vtx->geomShift) &
                  BMASK[gLen]) >> bitsRemaining);
                hdrBitsRemaining = 0;
            }
            else
                bitsRemaining = gLen; 
        break;

        case 0:
            if (hdrBitsRemaining)
            {
                bitsRemaining = gLen - hdrBitsRemaining;
                hdr |= (((vtx->ix >> vtx->geomShift) &
                  BMASK[gLen]) >> bitsRemaining);
                hdrBitsRemaining = 0;
            }
            else
                bitsRemaining = gLen; 
        break;
    }

    /* extract tri-mesh header and mesh buffer push bits */
    meshHdr = 0;
    switch (vtx->header & 0xF)
    {
        case CGP_POINT_HDR_RESTART_CW:
            meshHdr = 0;
            break;

        case CGP_POINT_HDR_RESTART_CCW:
            meshHdr = 0x2;
            break;

        case CGP_POINT_HDR_REPLACE_MIDDLE:
            meshHdr = 0x4;
            break;

        case CGP_POINT_HDR_REPLACE_OLDEST:
            meshHdr = 0x6;
            break;

        default:
            fprintf(stderr, "Unknown header found in cgIssueVertex\n");
    }

    if (vtx->header & CGP_POINT_HDR_PUSH){
      zdo6 printf("mb push\n");
      meshHdr |= 0x1;
    }

    /* merge any meshHdr data that will fit into command header */
    if (hdrBitsRemaining)
    {
        CGint numBits = 3 - hdrBitsRemaining;

        /* for this to be true hdrBitsRemaining must be 2 or 3 */
        /*  because:                                           */
        /*    compression command                      2 bits  */
        /*    x, y, and z must be 1 bit in length      3 bits  */
        /*    huffman tag                            0 or 1 bit*/
        /* For a total of 5 or 6 bits                          */

        hdr |= (meshHdr >> numBits);
        cgpWriteHeader(8, hdr);

       /* for this to be true, still have 1 bit left to write */
       if (numBits)
           cgpWriteData(1, meshHdr & 0x1);
    }
    else
    {
        cgpWriteHeader(8, hdr);
        cgpWriteData(3, meshHdr);
    }

    /* write out the rest of the vertex data */
    switch (numVtx)
    {
        case 3:
            /* nothing to do here - all data is in header */
        break;

        case 2:
            cgpWriteData(bitsRemaining,
              (vtx->iz >> vtx->geomShift) & BMASK[bitsRemaining]);
        break;

        case 1:
            cgpWriteData(bitsRemaining,
              (vtx->iy >> vtx->geomShift) & BMASK[bitsRemaining]);
            cgpWriteData(gLen, (vtx->iz >> vtx->geomShift) & BMASK[gLen]);
        break;

        case 0:
            cgpWriteData(bitsRemaining,
              (vtx->ix >> vtx->geomShift) & BMASK[bitsRemaining]);
            cgpWriteData(gLen, (vtx->iy >> vtx->geomShift) & BMASK[gLen]);
            cgpWriteData(gLen, (vtx->iz >> vtx->geomShift) & BMASK[gLen]);
        break;
    }

    /* get rid of this data */
    free(vtx);
}


/*******************
 * cgpIssueVnop
 *
 * Issues a variable length no-op command.  A Vnop command sets a specified
 * range of data to 0's.
 *
 *  Input:
 *    CGPvnop*	vnop	- the vnop command to send down (number of 0's)
 *    CGboolean freeMem - needed because a vnop command can be in the
 *                        command stream (in which case freeMem should be set
 *                        1 so that the memory is freed) and is also used by
 *                        cgpBuildCompressedBuffer to align the compressed
 *                        geometry buffer to 32 bits (can't free that command)
 *                        
 *  Output:
 *    None.
 *
 */

CGvoid
cgpIssueVnop(CGPvnop *vnop, CGboolean freeMem)
{
    CGubyte hdr = CGP_V_NO_OP;
    CGuint  zeros = 0;

    cgpWriteHeader(8, hdr);

    cgpWriteData(5, vnop->numZeros);
    cgpWriteData(vnop->numZeros, zeros);

    /* get rid of this data */
    if (freeMem)
       free(vnop);
}


/*******************
 * cgpIssueSetColor
 *
 * Issues a set color command.  A set color command can be from an explicit
 * setcolor in the stream or from a color encountered in a vertex.
 *
 *
 *  Input:
 *    CGPsetcolor* color
 *    CGboolean    inVtx - if this color is from a vertex only send down a
 *                         a 6 bit header, otherwise send down 8 bit header
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpIssueSetColor(CGPsetcolor *color, CGboolean inVtx)
{
    CGubyte hdr;
    CGint hdrBitsRemaining;
    CGint cLen = color->colorLen - color->colorShift;
    CGint numVtx;
    CGint bitsRemaining;
    CGint shift = color->colorShift;

    /* Check for alpha */
    if (curState & CGP_STATE_CAP)
    {
        cgpIssueSetColor4(color, inVtx);
        return;
    }

    /* if this is a vtx color, don't send op code */
    if (inVtx)
    {
        hdr = 0;
        hdrBitsRemaining = 6;
    }
    else
    {
        hdr = CGP_SET_COLOR;
        hdrBitsRemaining = 6;
    }

    hdrBitsRemaining -= color->colorTagLen;
    hdr |= (color->colorTag <<  hdrBitsRemaining);

    numVtx = hdrBitsRemaining / cLen;

    /* if greater than max set to max value */
    if (numVtx > 3)
        numVtx = 3;

    switch (numVtx)
    {
        case 3:
            /* all data fits in header word! */
            hdrBitsRemaining -= cLen;
            hdr |= (((color->ir >> shift) & BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ig >> shift)& BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ib >> shift) & BMASK[cLen]) << cLen);

        break;

        case 2:
            hdrBitsRemaining -= cLen;
            hdr |= (((color->ir >> shift) & BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ig >> shift) & BMASK[cLen]) << cLen);

            if (hdrBitsRemaining)
            {
                bitsRemaining = cLen - hdrBitsRemaining;
                hdr |= (((color->ib >> shift) & BMASK[cLen]) >> bitsRemaining);
            }
            else
                bitsRemaining = cLen;
        break;

        case 1:
            hdrBitsRemaining -= cLen;
            hdr |= (((color->ir >> shift) & BMASK[cLen]) << cLen);

            if (hdrBitsRemaining)
            {
                bitsRemaining = cLen - hdrBitsRemaining;
                hdr |= (((color->ig >> shift) & BMASK[cLen]) >> bitsRemaining);
            }
            else
                bitsRemaining = cLen;
        break;

        case 0:
            if (hdrBitsRemaining)
            {
                bitsRemaining = cLen - hdrBitsRemaining;
                hdr |= (((color->ir >> shift) & BMASK[cLen]) >> bitsRemaining);
            }
            else
                bitsRemaining = cLen;
        break;
    }

    /* write out header and process the rest */
    if (inVtx)
        cgpWriteHeader(6, hdr);
    else
        cgpWriteHeader(8, hdr);

    /* write out the rest of the vertex data */
    switch (numVtx)
    {
        case 3:
            /* nothing to do here - all data is in header */
        break;

        case 2:
            cgpWriteData(bitsRemaining,
               (color->ib >> shift) & BMASK[bitsRemaining]);
        break;

        case 1:
            cgpWriteData(bitsRemaining,
               (color->ig >> shift) & BMASK[bitsRemaining]);
            cgpWriteData(cLen, (color->ib >> shift) & BMASK[cLen]);
        break;

        case 0:
            cgpWriteData(bitsRemaining,
               (color->ir >> shift) & BMASK[bitsRemaining]);
            cgpWriteData(cLen, (color->ig >> shift) & BMASK[cLen]);
            cgpWriteData(cLen, (color->ib >> shift) & BMASK[cLen]);
        break;
    }

    /* get rid of this data */
    free(color);
}


/*******************
 * cgpIssueSetColor4
 *
 * Issues a set color command with alpha.  A set color command can be from an
 * explicit setcolor in the stream or from a color encountered in a vertex.
 *
 *
 *  Input:
 *    CGPsetcolor* color
 *    CGboolean    inVtx - if this color is from a vertex only send down a
 *                         a 6 bit header, otherwise send down 8 bit header
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpIssueSetColor4(CGPsetcolor *color, CGboolean inVtx)
{
    CGubyte hdr;
    CGint hdrBitsRemaining;
    CGint cLen = color->colorLen - color->colorShift;
    CGint numVtx;
    CGint bitsRemaining;
    CGint shift = color->colorShift;

    /* if this is a vtx color, don't send op code */
    if (inVtx)
    {
        hdr = 0;
        hdrBitsRemaining = 6;
    }
    else
    {
        hdr = CGP_SET_COLOR;
        hdrBitsRemaining = 6;
    }

    hdrBitsRemaining -= color->colorTagLen;
    hdr |= (color->colorTag <<  hdrBitsRemaining);

    numVtx = hdrBitsRemaining / cLen;

    /* if greater than max set to max value */
    if (numVtx > 4)
        numVtx = 4;

    switch (numVtx)
    {
        case 4:
            /* all data fits in header word! */
            hdrBitsRemaining -= cLen;
            hdr |= (((color->ir >> shift) & BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ig >> shift) & BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ib >> shift) & BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ia >> shift) & BMASK[cLen]) << cLen);
        break;

        case 3:
            hdrBitsRemaining -= cLen;
            hdr |= (((color->ir >> shift) & BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ig >> shift) & BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ib >> shift) & BMASK[cLen]) << cLen);

            if (hdrBitsRemaining)
            {
                bitsRemaining = cLen - hdrBitsRemaining;
                hdr |= (((color->ia >> shift)  & BMASK[cLen]) >> bitsRemaining);
            }
            else
                bitsRemaining = cLen;


        break;

        case 2:
            hdrBitsRemaining -= cLen;
            hdr |= (((color->ir >> shift) &  BMASK[cLen]) << cLen);

            hdrBitsRemaining -= cLen;
            hdr |= (((color->ig >> shift) &  BMASK[cLen]) << cLen);

            if (hdrBitsRemaining)
            {
                bitsRemaining = cLen - hdrBitsRemaining;
                hdr |= (((color->ib >> shift) &  BMASK[cLen]) >> bitsRemaining);
            }
            else
                bitsRemaining = cLen;
        break;

        case 1:
            hdrBitsRemaining -= cLen;
            hdr |= (((color->ir >> shift) &  BMASK[cLen]) << cLen);

            if (hdrBitsRemaining)
            {
                bitsRemaining = cLen - hdrBitsRemaining;
                hdr |= (((color->ig >> shift) &  BMASK[cLen]) >> bitsRemaining);
            }
            else
                bitsRemaining = cLen;
        break;

        case 0:
            if (hdrBitsRemaining)
            {
                bitsRemaining = cLen - hdrBitsRemaining;
                hdr |= (((color->ir >> shift) &  BMASK[cLen]) >> bitsRemaining);
            }
            else
                bitsRemaining = cLen;
        break;
    }

    /* write out header and process the rest */
    if (inVtx)
        cgpWriteHeader(6, hdr);
    else
        cgpWriteHeader(8, hdr);

    /* write out the rest of the vertex data */
    switch (numVtx)
    {
        case 4:
            /* nothing to do here - all data is in header */
        break;

        case 3:
            cgpWriteData(bitsRemaining,
               (color->ia >> shift) & BMASK[bitsRemaining]);
        break;

        case 2:
            cgpWriteData(bitsRemaining,
               (color->ib >> shift) & BMASK[bitsRemaining]);
            cgpWriteData(cLen, (color->ia >> shift) & BMASK[cLen]);
        break;

        case 1:
            cgpWriteData(bitsRemaining,
               (color->ig >> shift) & BMASK[bitsRemaining]);
            cgpWriteData(cLen, (color->ib >> shift) & BMASK[cLen]);
            cgpWriteData(cLen, (color->ia >> shift) & BMASK[cLen]);
        break;

        case 0:
            cgpWriteData(bitsRemaining,
               (color->ir >> shift) & BMASK[bitsRemaining]);
            cgpWriteData(cLen, (color->ig >> shift) & BMASK[cLen]);
            cgpWriteData(cLen, (color->ib >> shift) & BMASK[cLen]);
            cgpWriteData(cLen, (color->ia >> shift) & BMASK[cLen]);
        break;
    }

    /* get rid of this data */
    free(color);
}


/*******************
 * cgpIssueSetNormal
 *
 * Issues a set normal command.  A set normal command can be from an explicit
 * setnormal in the stream or from a normal encountered in a vertex.
 *
 *
 *  Input:
 *    CGPsetnormal* normal
 *    CGboolean    inVtx - if this normal is from a vertex only send down a
 *                         a 6 bit header, otherwise send down 8 bit header
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpIssueSetNormal(CGPsetnormal *normal, CGboolean inVtx)
{
    CGubyte hdr;
    CGint hdrBitsRemaining;
    CGint nLen;
    CGint numVtx;
    CGint bitsRemaining;

    BuffNormSize += normal->normalTagLen + normal->normalLen * 3;

    /* if this is a vtx normal, don't send op code */
    if (inVtx)
        hdr = 0;
    else
        hdr = CGP_SET_NORMAL;

    hdrBitsRemaining = 6;
    hdrBitsRemaining -= normal->normalTagLen;
    hdr |= (normal->normalTag <<  hdrBitsRemaining);

    if (normal->flags & CGP_SET_NORMAL_FLAG_SPECIAL ||
        normal->flags & CGP_SET_NORMAL_FLAG_ABSOLUTE)
    {
        nLen = normal->normalLen - normal->normalShift;

        bitsRemaining = 6 + nLen * 2;

        if (hdrBitsRemaining)
        {
            hdr |= (normal->index >> (bitsRemaining - hdrBitsRemaining));
            bitsRemaining -= hdrBitsRemaining;
        }

        /* write out header and process the rest */
        if (inVtx)
            cgpWriteHeader(6, hdr);
        else
            cgpWriteHeader(8, hdr);

        cgpWriteData(bitsRemaining, normal->index & BMASK[bitsRemaining]);
    }
    else
    {
        CGint numDeltas;

        nLen = normal->normalLen - normal->normalShift;

        numDeltas = hdrBitsRemaining / nLen;

        if (numDeltas > 2)
            printf("Error - Relative normal tag + len too small!\n");

        switch (numDeltas)
        {
            case 0:
                if (hdrBitsRemaining)
                {
                    bitsRemaining = nLen - hdrBitsRemaining;
                    hdr |= (((normal->u >> normal->normalShift) &
                       BMASK[nLen]) >> bitsRemaining);
                    hdrBitsRemaining = 0;
                }
                else
                    bitsRemaining = nLen;
            break;

            case 1:
                hdrBitsRemaining -= nLen;
                hdr |= (((normal->u >> normal->normalShift) &
                        BMASK[nLen]) << hdrBitsRemaining);

                if (hdrBitsRemaining)
                {
                    bitsRemaining = nLen - hdrBitsRemaining;
                    hdr |= (((normal->v >> normal->normalShift) &
                       BMASK[nLen]) >> bitsRemaining);
                    hdrBitsRemaining = 0;
                }
                else
                    bitsRemaining = nLen;
            break;

            case 2:
                /* must exactly fit in */
                hdrBitsRemaining -= nLen;
                hdr |= (((normal->u >> normal->normalShift) &
                        BMASK[nLen]) << hdrBitsRemaining);

                hdrBitsRemaining -= nLen;
                hdr |= (((normal->v >> normal->normalShift) &
                        BMASK[nLen]) << hdrBitsRemaining);
            break;
        }


        /* write out header */
        if (inVtx)
            cgpWriteHeader(6, hdr);
        else
            cgpWriteHeader(8, hdr);

        /* process the rest of the data */
        if (numDeltas == 0)
        {
            cgpWriteData(bitsRemaining,
               (normal->u >> normal->normalShift) & BMASK[bitsRemaining]);
            cgpWriteData(nLen,
               (normal->v >> normal->normalShift) & BMASK[nLen]);
        }
        else
        {
            cgpWriteData(bitsRemaining,
               (normal->v >> normal->normalShift) & BMASK[nLen]);
        }
    }

    free(normal);
}


/*******************
 * cgpIssueMBR
 *
 * Issues a mesh buffer reference command.
 *
 *
 *  Input:
 *    CGint*	vtxHdr - holds the veretx header that contains the mesh buffer
 *                       reference number.
 *
 *  Output:
 *    None.
 *
 */

CGvoid
cgpIssueMBR(CGPmbr *vtxHdr)
{
    CGuint   vHdr = vtxHdr->mbRef;
    CGubyte hdr;
    CGbyte  rep;
    int ref;

    BuffMBRSize += 9;
    hdr = CGP_MESH_B_R;
#if 0
    ref =  (CGP_GET_POINT_HDR_REF_NUMBER(vHdr));
    if (ref < 0)
      ref = ref * -1;
#endif

#if 0
    ref = CurrBuffRefs[CurrBuffIndex++];
#endif

    ref = CGP_GET_POINT_HDR_REF_NUMBER(vHdr);
    hdr |= (ref << 1);
    zdo6 printf("cgpIssueMBR %d \n", ref);

    switch (vHdr & 0xf)
    {
        case CGP_POINT_HDR_RESTART_CW:
            rep = 0;
            break;

        case CGP_POINT_HDR_RESTART_CCW:
            rep = 0x1;
            break;

        case CGP_POINT_HDR_REPLACE_MIDDLE:
            rep = 0x2;
            break;

        case CGP_POINT_HDR_REPLACE_OLDEST:
            rep = 0x3;
            break;

        default:
            fprintf(stderr,"IssueMBR:Unknown header found in cgIssueVertex\n");
    }

    hdr |= (rep >> 1);
    cgpWriteHeader(8, hdr);

    cgpWriteData(1, rep & 0x1);

    free(vtxHdr);
}
