/****************************************************************************
 * RAGE128 Chapter 6 Sample Code                                            *
 *                                                                          *
 * prim.c - Functions to draw primitives using Type-3 packets.				*	
 *                                                                          *
 * Copyright (c) 1999 ATI Technologies Inc.  All rights reserved.           *
 ****************************************************************************/

#include <stdio.h>
#include <string.h>
#include "..\..\util\regdef.h"
#include "..\..\util\defines.h"
#include "..\..\util\main.h"
#include "..\..\util\cce.h"
#include "prim.h"
#include "cntx3d.h"

extern DWORD gCommandBuf[];
extern BOOL gbFlushTextureCache;

extern void FlushTextureCache (void); // texture.c
extern void R128_Flush (void);


/****************************************************************************
 * R128_DrawTriangle														*
 *  Function: Draw one triangle using a CCE_PACKET3_3D_RNDR_GEN_PRIM packet.*
 *    Inputs: ptriverts - pointer to an array of three TLVERTEX2 structs.   *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.											*
 ****************************************************************************/

BOOL R128_DrawTriangle (LPTLVERTEX2 ptriverts)
{
	DWORD size=0;
	TLVERTEX2* pv;
	DWORD* pBuf = gCommandBuf;

	if (ptriverts == NULL)
		return FALSE;

    // If texture mapping is enabled and a new texture has been loaded,
    // set up the texture cache to flush at the start of the next primitive.

    if (gbFlushTextureCache)
    {
        FlushTextureCache();
        gbFlushTextureCache = FALSE;
    }

	// Set the packet HEADER, VC_FORMAT, and VC_CNTL fields.

	*pBuf++ = CCE_PACKET3_3D_RNDR_GEN_PRIM;
	*pBuf++ = VC_FORMAT_TLVERTEX2;
	*pBuf++ = CCE_VC_CNTL_PRIM_TYPE_TRI_LIST |
		  CCE_VC_CNTL_PRIM_WALK_RING	 |
		  (0x00000003L << 16);

	pv = (TLVERTEX2*) pBuf;

	// Copy triangle vertices into command buffer.

	// Vertex 0:

	pv->x = ptriverts[0].x;
	pv->y = ptriverts[0].y;
	pv->z = ptriverts[0].z;
	pv->rhw = ptriverts[0].rhw;
	pv->diffuse = ptriverts[0].diffuse;
	pv->specular = ptriverts[0].specular;
	pv->s1 = ptriverts[0].s1;
	pv->t1 = ptriverts[0].t1;
    pv->s2 = ptriverts[0].s2;
    pv->t2 = ptriverts[0].t2;
	pv++;

	// Vertex 1:

	pv->x = ptriverts[1].x;
	pv->y = ptriverts[1].y;
	pv->z = ptriverts[1].z;
	pv->rhw = ptriverts[1].rhw;
	pv->diffuse = ptriverts[1].diffuse;
	pv->specular = ptriverts[1].specular;
	pv->s1 = ptriverts[1].s1;
	pv->t1 = ptriverts[1].t1;
    pv->s2 = ptriverts[1].s2;
    pv->t2 = ptriverts[1].t2;
	pv++;

	// Vertex 2:

	pv->x = ptriverts[2].x;
	pv->y = ptriverts[2].y;
	pv->z = ptriverts[2].z;
	pv->rhw = ptriverts[2].rhw;
	pv->diffuse = ptriverts[2].diffuse;
	pv->specular = ptriverts[2].specular;
	pv->s1 = ptriverts[2].s1;
	pv->t1 = ptriverts[2].t1;
    pv->s2 = ptriverts[2].s2;
    pv->t2 = ptriverts[2].t2;
	pv++;

	// Compute size of buffer.

    size = ((DWORD)pv - (DWORD)(&gCommandBuf[0]))/sizeof (DWORD);

	// Submit buffer.

    gCommandBuf[0] |= ((size - 2) << 16);
    R128_CCESubmitPackets (gCommandBuf, size);

    // Flush queued packet data to the CCE FIFO. Depending on the watermark
    // thresholds written to the PM4_BUFFER_WM_CNTL register, some triangle
    // data may still be queued up in the ring buffer. This data may not be
    // sent to the CCE FIFO until a future write to the PM4_BUFFER_DL_WPTR
    // register satisfies the watermark conditions, resulting in remaining
    // primitives being rendered later than expected. Flushing the ring
    // buffer here ensures all primitive data is rendered before this
    // function returns. However, note that this approach causes some loss
    // of concurency as the function won't return until all triangle data
    // has been flushed to the CCE FIFO. 

    R128_Flush ();

	return TRUE;
}


/****************************************************************************
 * R128_DrawTriangleList 												    *
 *  Function: Draw a triangle list using a CCE_PACKET3_3D_RNDR_GEN_PRIM     *
 *            packet.                                                       *
 *    Inputs: ptrilist - pointer to a PRIMDATA struct containing list       *
 *                        data.                                             *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.										    *
 ****************************************************************************/

BOOL R128_DrawTriangleList (LPPRIMDATA ptrilist)
{
    DWORD maxverts, numvertsdrawn, numvertstodraw, size;
	LPTLVERTEX2 pwritevert;
	LPTLVERTEX2 preadvert;

    // Validate argument.

    if ((ptrilist == NULL) ||				// NULL pointer
		(ptrilist->numverts < 3) ||			// less than 3 vertices
		((ptrilist->numverts % 3) != 0) ||	// vertex count not multiple of 3
		(ptrilist->verts == NULL))			// NULL vertex buffer pointer
        return FALSE;

    // If texture mapping is enabled and a new texture has been loaded,
    // set up the texture cache to flush at the start of the next primitive.

    if (gbFlushTextureCache)
    {
        FlushTextureCache();
        gbFlushTextureCache = FALSE;
    }

    // Determine the maximum number of vertices that can be sent in the
    // command buffer. This number is the largest integer multiple of
    // three TLVERTEX2 structures that will fit in the command buffer
    // after the HEADER, VC_FORMAT, and VC_CNTL fields. If the triangle list
    // is larger than this number, it must be broken up into smaller batches
    // that are sent seperately.

    maxverts = ((int)((CMD_BUF_SIZE - 3)/
        (sizeof (TLVERTEX2)/sizeof (DWORD)))/3)*3;

    // Set the HEADER, VC_FORMAT, and VC_CNTL fields.

    gCommandBuf[0] = CCE_PACKET3_3D_RNDR_GEN_PRIM;
    gCommandBuf[1] = VC_FORMAT_TLVERTEX2;
    gCommandBuf[2] = CCE_VC_CNTL_PRIM_TYPE_TRI_LIST |
        CCE_VC_CNTL_PRIM_WALK_RING;

    // Set the vertex write pointer. This is where vertices will be
    // copied into the command buffer.

	pwritevert = (TLVERTEX2*) &gCommandBuf[3];

    // Set the vertex read pointer. This is where vertices will be
    // read from in the triangle list.

	preadvert = ptrilist->verts;

    // Initialize the count of vertices to submit.

    numvertsdrawn = 0;

    // Loop to process vertices until all have been processed.

    while (numvertsdrawn < ptrilist->numverts)
    {
        // Compute the number of vertices left to submit.

        numvertstodraw = ptrilist->numverts - numvertsdrawn;

        // Clamp to the maximum allowable number.

        numvertstodraw = numvertstodraw > maxverts ? maxverts: numvertstodraw;

        // Set the vertex count in the VC_CNTL field.

		gCommandBuf[2] &= 0x0000ffff;
        gCommandBuf[2] |= (numvertstodraw << 16);

		// Compute size of data to copy in bytes.

        size = numvertstodraw * sizeof (TLVERTEX2);

		// Copy data from trilist into command buffer.

		memcpy (pwritevert, preadvert, size);

		// Advance the read pointer. 

        preadvert += numvertstodraw;

        // Convert size to DWORD count.

		size = size / sizeof (DWORD);

        // Adjust size for HEADER, VC_FORMAT and VC_CNTL in packet.

        size += 3;

        // Set packet size parameter in packet HEADER.

        gCommandBuf[0] |= ((size - 2) << 16);

        // Submit the packet to draw the batch.

		R128_CCESubmitPackets (gCommandBuf, size);

        numvertsdrawn += numvertstodraw;
	}

    // Flush queued packet data to the CCE FIFO. Depending on the watermark
    // thresholds written to the PM4_BUFFER_WM_CNTL register, some triangle
    // data may still be queued up in the ring buffer. This data may not be
    // sent to the CCE FIFO until a future write to the PM4_BUFFER_DL_WPTR
    // register satisfies the watermark conditions, resulting in remaining
    // primitives being rendered later than expected. Flushing the ring
    // buffer here ensures all primitive data is rendered before this
    // function returns. However, note that this approach causes some loss
    // of concurency as the function won't return until all triangle data
    // has been flushed to the CCE FIFO. 


    R128_Flush ();

	return TRUE;
}


/****************************************************************************
 * R128_DrawTriangleStrip 												    *
 *  Function: Draw a triangle strip using a CCE_PACKET3_3D_RNDR_GEN_PRIM    *
 *            packet.                                                       *
 *    Inputs: ptristrip - pointer to a PRIMDATA struct containing strip     *
 *                        data.                                             *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.										    *
 ****************************************************************************/

BOOL R128_DrawTriangleStrip (LPPRIMDATA ptristrip)
{
    DWORD maxverts, size;
	LPTLVERTEX2 pwritevert;
	LPTLVERTEX2 preadvert;
    int numvertsremaining, numvertstodraw;

    // Validate argument.

    if ((ptristrip == NULL) ||               // NULL pointer
        (ptristrip->numverts < 3) ||         // less than 3 vertices
        (ptristrip->verts == NULL))          // NULL vertex buffer pointer
        return FALSE;

    // If texture mapping is enabled and a new texture has been loaded,
    // set up the texture cache to flush at the start of the next primitive.

    if (gbFlushTextureCache)
    {
        FlushTextureCache();
        gbFlushTextureCache = FALSE;
    }

    // Determine the maximum number of vertices that can be sent in the
    // command buffer. This number is the largest even number of TLVERTEX2 
    // structures that will fit in the command buffer after the HEADER, 
	// VC_FORMAT, and VC_CNTL fields. The triangle strip batch must be broken
	// up into even quantities so that the next strip segment has the same
	// front facing orientation as the original (i.e. CW or CCW). The 
	// previous two vertices from the last strip segment are used as the 
	// first two vertices for the next strip segment.

    maxverts = ((CMD_BUF_SIZE - 3)/(sizeof (TLVERTEX2)/sizeof (DWORD)))
        & 0xfffffffe;

    // Set HEADER, VC_FORMAT, and VC_CNTL.

    gCommandBuf[0] = CCE_PACKET3_3D_RNDR_GEN_PRIM;
    gCommandBuf[1] = VC_FORMAT_TLVERTEX2;
    gCommandBuf[2] = CCE_VC_CNTL_PRIM_TYPE_TRI_STRIP |
        CCE_VC_CNTL_PRIM_WALK_RING;

    // Set the vertex write pointer. This points to where vertex data will
    // be copied into the command buffer.

	pwritevert = (TLVERTEX2*) &gCommandBuf[3];

    // Set the vertex read pointer. This points to where vertex data will
    // be read from the triangle strip.

    preadvert = ptristrip->verts;

    // Set the number of vertices left to process.

    numvertsremaining = (int)ptristrip->numverts;


    // Submit vertices until all have been processed.

    while (numvertsremaining > 2)
    {
        // Only submit as many as the buffer can hold.

        numvertstodraw = numvertsremaining > maxverts ? maxverts :
            numvertsremaining;
		gCommandBuf[2] &= 0x0000ffff;
        gCommandBuf[2] |= (numvertstodraw << 16);

		// Compute size of data to copy in bytes.

        size = numvertstodraw * sizeof (TLVERTEX2);

        // Copy data from tristrip into command buffer.

		memcpy (pwritevert, preadvert, size);

        // Advance the read pointer: Since the next vertex in the strip
        // needs the previous two vertices to complete a triangle (the
        // starting three vertex triangle for the next strip batch to
        // be submitted), set the read pointer to point to the second
        // last vertex from the previous batch.

        preadvert += (numvertstodraw - 2);

        // Convert size to DWORD count.

		size = size / sizeof (DWORD);

        // Adjust size for HEADER, VC_FORMAT and VC_CNTL in packet.

        size += 3;

        // Write the size parameter to the packet header.

        gCommandBuf[0] |= ((size - 2) << 16);

        // Submit the packet.

		R128_CCESubmitPackets (gCommandBuf, size);

        // Adjust the count of remaining vertices to process.

        numvertsremaining -= (numvertstodraw - 2);
	}

    // Flush queued packet data to the CCE FIFO. Depending on the watermark
    // thresholds written to the PM4_BUFFER_WM_CNTL register, some triangle
    // data may still be queued up in the ring buffer. This data may not be
    // sent to the CCE FIFO until a future write to the PM4_BUFFER_DL_WPTR
    // register satisfies the watermark conditions, resulting in remaining
    // primitives being rendered later than expected. Flushing the ring
    // buffer here ensures all primitive data is rendered before this
    // function returns. However, note that this approach causes some loss
    // of concurency as the function won't return until all triangle data
    // has been flushed to the CCE FIFO. 


    R128_Flush ();

	return TRUE;
}


/****************************************************************************
 * R128_DrawTriangleFan 												    *
 *  Function: Draw a triangle fan using a CCE_PACKET3_3D_RNDR_GEN_PRIM      *
 *            packet.                                                       *
 *    Inputs: ptrifan - pointer to a PRIMDATA struct containing strip       *
 *                        data.                                             *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.										    *
 ****************************************************************************/

BOOL R128_DrawTriangleFan (LPPRIMDATA ptrifan)
{
    DWORD maxverts, size;
	LPTLVERTEX2 pwritevert;
	LPTLVERTEX2 preadvert;
    int numvertsremaining, numvertstodraw;

    // Validate argument.

    if ((ptrifan == NULL) ||               // NULL pointer
        (ptrifan->numverts < 3) ||         // less than 3 vertices
        (ptrifan->verts == NULL))          // NULL vertex buffer pointer
        return FALSE;

    // If texture mapping is enabled and a new texture has been loaded,
    // set up the texture cache to flush at the start of the next primitive.

    if (gbFlushTextureCache)
    {
        FlushTextureCache();
        gbFlushTextureCache = FALSE;
    }

    // Determine the maximum number of vertices that can be sent in the
    // command buffer (i.e. the vertex batch size). If the number of vertices
    // to process are greater than this number, the triangle fan must be
    // broken up and sent in seperate batches. The maximum number is the
    // size, in DWORDs, of the command buffer minus the DWORDs for the
    // packet HEADER, VC_FORMAT, and VC_CNTL fields.

    maxverts = (CMD_BUF_SIZE - 3)/(sizeof (TLVERTEX2)/sizeof (DWORD));

    // Set HEADER, VC_FORMAT, and VC_CNTL.

    gCommandBuf[0] = CCE_PACKET3_3D_RNDR_GEN_PRIM;
    gCommandBuf[1] = VC_FORMAT_TLVERTEX2;
    gCommandBuf[2] = CCE_VC_CNTL_PRIM_TYPE_TRI_FAN |
        CCE_VC_CNTL_PRIM_WALK_RING;

    // Set the vertex write pointer. This points to where vertex data will
    // be copied into the command buffer.

	pwritevert = (TLVERTEX2*) &gCommandBuf[3];

    // Set the vertex read pointer. This points to where vertex data will
    // be read from the triangle strip.

    preadvert = ptrifan->verts;

    // Copy vertex 0. This will always be the first vertex in the
    // fan, so only copy it once.

    pwritevert->x = preadvert->x;
    pwritevert->y = preadvert->y;
    pwritevert->z = preadvert->z;
    pwritevert->rhw = preadvert->rhw;
    pwritevert->diffuse = preadvert->diffuse;
    pwritevert->specular = preadvert->specular;
    pwritevert->s1 = preadvert->s1;
    pwritevert->t1 = preadvert->t1;
    pwritevert->s2 = preadvert->s2;
    pwritevert->t2 = preadvert->t2;

    // Advance the read and write pointers.

	preadvert++;
    pwritevert++;

    // Set the number of vertices left to process.

    numvertsremaining = (int)ptrifan->numverts;

    // Submit vertices until all have been processed.

    while (numvertsremaining > 2)
    {
        // Only submit as many as the buffer can hold.

        numvertstodraw = numvertsremaining > maxverts ? maxverts :
            numvertsremaining;
		gCommandBuf[2] &= 0x0000ffff;
        gCommandBuf[2] |= (numvertstodraw << 16);

		// Compute size of data to copy in bytes.

        size = numvertstodraw * sizeof (TLVERTEX2);

        // Copy data from tristrip into command buffer. size copied
        // is less one vertex to acount for vertex 0, which is already at
        // the top of the list.

        memcpy (pwritevert, preadvert, size - sizeof(TLVERTEX2));

        // Advance the read pointer: Since the next vertex in the fan
        // needs the last vertex and vertex 0 to complete a triangle (the
        // starting three vertex triangle for the next fan batch to
        // be submitted), set the read pointer to point to the
        // last vertex from the previous batch.

        preadvert += (numvertstodraw - 2);

        // Convert size to DWORD count.

		size = size / sizeof (DWORD);

        // Adjust size for HEADER, VC_FORMAT and VC_CNTL in packet.

        size += 3;

        // Write the size parameter to the packet header.

        gCommandBuf[0] |= ((size - 2) << 16);

        // Submit the packet.

		R128_CCESubmitPackets (gCommandBuf, size);

        // Adjust the count of remaining vertices to process.

        numvertsremaining -= (numvertstodraw - 2);
	}

    // Flush queued packet data to the CCE FIFO. Depending on the watermark
    // thresholds written to the PM4_BUFFER_WM_CNTL register, some triangle
    // data may still be queued up in the ring buffer. This data may not be
    // sent to the CCE FIFO until a future write to the PM4_BUFFER_DL_WPTR
    // register satisfies the watermark conditions, resulting in remaining
    // primitives being rendered later than expected. Flushing the ring
    // buffer here ensures all primitive data is rendered before this
    // function returns. However, note that this approach causes some loss
    // of concurency as the function won't return until all triangle data
    // has been flushed to the CCE FIFO. 


    R128_Flush ();

	return TRUE;
}


/****************************************************************************
 * R128_DrawPointList    												    *
 *  Function: Draw a point list using a CCE_PACKET3_3D_RNDR_GEN_PRIM        *
 *            packet.                                                       *
 *    Inputs: ppointlist - pointer to a PRIMDATA struct containing list     *
 *                        data.                                             *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.										    *
 ****************************************************************************/

BOOL R128_DrawPointList (LPPRIMDATA ppointlist)
{
    DWORD maxverts, size;
	LPTLVERTEX2 pwritevert;
	LPTLVERTEX2 preadvert;
    int numvertsdrawn, numvertstodraw;

	if ((ppointlist == NULL) ||
		(ppointlist->verts == NULL) ||
		(ppointlist->numverts < 1))
		return FALSE;

    // If texture mapping is enabled and a new texture has been loaded,
    // set up the texture cache to flush at the start of the next primitive.

    if (gbFlushTextureCache)
    {
        FlushTextureCache();
        gbFlushTextureCache = FALSE;
    }

    // Determine the maximum number of vertices that can be sent in the
    // command buffer (i.e. the vertex batch size). If the number of vertices
    // to process are greater than this number, the point list must be
    // broken up and sent in seperate batches. The maximum number is the
    // size, in DWORDs, of the command buffer minus the DWORDs for the
    // packet HEADER, VC_FORMAT, and VC_CNTL fields.

    maxverts = (CMD_BUF_SIZE - 3)/(sizeof (TLVERTEX2)/sizeof (DWORD));

    // Set HEADER, VC_FORMAT, and VC_CNTL.

    gCommandBuf[0] = CCE_PACKET3_3D_RNDR_GEN_PRIM;
    gCommandBuf[1] = VC_FORMAT_TLVERTEX2;
    gCommandBuf[2] = CCE_VC_CNTL_PRIM_TYPE_POINT |
        CCE_VC_CNTL_PRIM_WALK_RING;

    // Set the vertex write pointer. This points to where vertex data will
    // be copied into the command buffer.

	pwritevert = (TLVERTEX2*) &gCommandBuf[3];

    // Set the vertex read pointer. This points to where vertex data will
    // be read from the triangle strip.

    preadvert = ppointlist->verts;

    numvertsdrawn = 0;

    // Loop to process vertices until all have been processed.

    while (numvertsdrawn < ppointlist->numverts)
    {
        // Compute the number of vertices left to submit.

        numvertstodraw = ppointlist->numverts - numvertsdrawn;

        // Clamp to the maximum allowable number.

        numvertstodraw = numvertstodraw > maxverts ? maxverts : 
			numvertstodraw;

        // Set the vertex count in the VC_CNTL field.

		gCommandBuf[2] &= 0x0000ffff;
        gCommandBuf[2] |= (numvertstodraw << 16);

		// Compute size of data to copy in bytes.

        size = numvertstodraw * sizeof (TLVERTEX2);

		// Copy data from trilist into command buffer.

		memcpy (pwritevert, preadvert, size);

		// Advance the read pointer. 

        preadvert += numvertstodraw;

        // Convert size to DWORD count.

		size = size / sizeof (DWORD);

        // Adjust size for HEADER, VC_FORMAT and VC_CNTL in packet.

        size += 3;

        // Set packet size parameter in packet HEADER.

        gCommandBuf[0] |= ((size - 2) << 16);

        // Submit the packet to draw the batch.

		R128_CCESubmitPackets (gCommandBuf, size);

        numvertsdrawn += numvertstodraw;
	}

    // Flush queued packet data to the CCE FIFO. Depending on the watermark
    // thresholds written to the PM4_BUFFER_WM_CNTL register, some primitive
    // data may still be queued up in the ring buffer. This data may not be
    // sent to the CCE FIFO until a future write to the PM4_BUFFER_DL_WPTR
    // register satisfies the watermark conditions, resulting in remaining
    // primitives being rendered later than expected. Flushing the ring
    // buffer here ensures all primitive data is rendered before this
    // function returns. However, note that this approach causes some loss
    // of concurency as the function won't return until all triangle data
    // has been flushed to the CCE FIFO. 

    R128_Flush ();

	return TRUE;
}


/****************************************************************************
 * R128_DrawLineList    												    *
 *  Function: Draw a line list using a CCE_PACKET3_3D_RNDR_GEN_PRIM         *
 *            packet.                                                       *
 *    Inputs: plinelist - pointer to a PRIMDATA struct containing list      *
 *                        data.                                             *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.										    *
 ****************************************************************************/

BOOL R128_DrawLineList (LPPRIMDATA plinelist)
{
    DWORD maxverts, size;
	LPTLVERTEX2 pwritevert;
	LPTLVERTEX2 preadvert;
    int numvertsdrawn, numvertstodraw;

	if ((plinelist == NULL) ||
		(plinelist->verts == NULL) ||
		(plinelist->numverts < 2))
		return FALSE;

    // If texture mapping is enabled and a new texture has been loaded,
    // set up the texture cache to flush at the start of the next primitive.

    if (gbFlushTextureCache)
    {
        FlushTextureCache();
        gbFlushTextureCache = FALSE;
    }

    // Determine the maximum number of vertices that can be sent in the
    // command buffer (i.e. the vertex batch size). If the number of vertices
    // to process are greater than this number, the line list must be
    // broken up and sent in seperate batches. The maximum number is the 
	// largest even number of vertices that will fit in the buffer after the 
    // packet HEADER, VC_FORMAT, and VC_CNTL fields.

    maxverts = ((CMD_BUF_SIZE - 3)/(sizeof (TLVERTEX2)/sizeof (DWORD))) 
		& 0xfffffffe;

    // Set HEADER, VC_FORMAT, and VC_CNTL.

    gCommandBuf[0] = CCE_PACKET3_3D_RNDR_GEN_PRIM;
    gCommandBuf[1] = VC_FORMAT_TLVERTEX2;
    gCommandBuf[2] = CCE_VC_CNTL_PRIM_TYPE_LINE |
        CCE_VC_CNTL_PRIM_WALK_RING;

    // Set the vertex write pointer. This points to where vertex data will
    // be copied into the command buffer.

	pwritevert = (TLVERTEX2*) &gCommandBuf[3];

    // Set the vertex read pointer. This points to where vertex data will
    // be read from the triangle strip.

    preadvert = plinelist->verts;

    numvertsdrawn = 0;

    // Loop to process vertices until all have been processed.

    while (numvertsdrawn < plinelist->numverts)
    {
        // Compute the number of vertices left to submit.

        numvertstodraw = plinelist->numverts - numvertsdrawn;

        // Clamp to the maximum allowable number.

        numvertstodraw = numvertstodraw > maxverts ? maxverts : 
			numvertstodraw;

        // Set the vertex count in the VC_CNTL field.

		gCommandBuf[2] &= 0x0000ffff;
        gCommandBuf[2] |= (numvertstodraw << 16);

		// Compute size of data to copy in bytes.

        size = numvertstodraw * sizeof (TLVERTEX2);

		// Copy data from trilist into command buffer.

		memcpy (pwritevert, preadvert, size);

		// Advance the read pointer. 

        preadvert += numvertstodraw;

        // Convert size to DWORD count.

		size = size / sizeof (DWORD);

        // Adjust size for HEADER, VC_FORMAT and VC_CNTL in packet.

        size += 3;

        // Set packet size parameter in packet HEADER.

        gCommandBuf[0] |= ((size - 2) << 16);

        // Submit the packet to draw the batch.

		R128_CCESubmitPackets (gCommandBuf, size);

        numvertsdrawn += numvertstodraw;
	}

    // Flush queued packet data to the CCE FIFO. Depending on the watermark
    // thresholds written to the PM4_BUFFER_WM_CNTL register, some primitive
    // data may still be queued up in the ring buffer. This data may not be
    // sent to the CCE FIFO until a future write to the PM4_BUFFER_DL_WPTR
    // register satisfies the watermark conditions, resulting in remaining
    // primitives being rendered later than expected. Flushing the ring
    // buffer here ensures all primitive data is rendered before this
    // function returns. However, note that this approach causes some loss
    // of concurency as the function won't return until all triangle data
    // has been flushed to the CCE FIFO. 

    R128_Flush ();

	return TRUE;
}


/****************************************************************************
 * R128_DrawPolyLine    												    *
 *  Function: Draw a polyline list using a CCE_PACKET3_3D_RNDR_GEN_PRIM     *
 *            packet.                                                       *
 *    Inputs: ppolyline - pointer to a PRIMDATA struct containing polyline  *
 *                        data.                                             *
 *   Outputs: TRUE - successful.                                            *
 *			  FALSE - unsuccessful.										    *
 ****************************************************************************/

BOOL R128_DrawPolyLine (LPPRIMDATA ppolyline)
{
    DWORD maxverts, size;
	LPTLVERTEX2 pwritevert;
	LPTLVERTEX2 preadvert;
    int numvertsremaining, numvertstodraw;

	if ((ppolyline == NULL) ||
		(ppolyline->verts == NULL) ||
		(ppolyline->numverts < 2))
		return FALSE;

    // If texture mapping is enabled and a new texture has been loaded,
    // set up the texture cache to flush at the start of the next primitive.

    if (gbFlushTextureCache)
    {
        FlushTextureCache();
        gbFlushTextureCache = FALSE;
    }

    // Determine the maximum number of vertices that can be sent in the
    // command buffer (i.e. the vertex batch size). If the number of vertices
    // to process are greater than this number, the line list must be
    // broken up and sent in seperate batches. The maximum number is the 
    // number of vertices that will fit in the buffer after the 
    // packet HEADER, VC_FORMAT, and VC_CNTL fields.

    maxverts = (CMD_BUF_SIZE - 3)/(sizeof (TLVERTEX2)/sizeof (DWORD));

    // Set HEADER, VC_FORMAT, and VC_CNTL.

    gCommandBuf[0] = CCE_PACKET3_3D_RNDR_GEN_PRIM;
    gCommandBuf[1] = VC_FORMAT_TLVERTEX2;
    gCommandBuf[2] = CCE_VC_CNTL_PRIM_TYPE_POLY_LINE |
        CCE_VC_CNTL_PRIM_WALK_RING;

    // Set the vertex write pointer. This points to where vertex data will
    // be copied into the command buffer.

	pwritevert = (TLVERTEX2*) &gCommandBuf[3];

    // Set the vertex read pointer. This points to where vertex data will
    // be read from the triangle strip.

    // Set the vertex write pointer. This points to where vertex data will
    // be copied into the command buffer.

	pwritevert = (TLVERTEX2*) &gCommandBuf[3];

    // Set the vertex read pointer. This points to where vertex data will
    // be read from the triangle strip.

    preadvert = ppolyline->verts;

    // Set the number of vertices left to process.

    numvertsremaining = (int)ppolyline->numverts;


    // Submit vertices until all have been processed.

    while (numvertsremaining > 1)
    {
        // Only submit as many as the buffer can hold.

        numvertstodraw = numvertsremaining > maxverts ? maxverts :
            numvertsremaining;
		gCommandBuf[2] &= 0x0000ffff;
        gCommandBuf[2] |= (numvertstodraw << 16);

		// Compute size of data to copy in bytes.

        size = numvertstodraw * sizeof (TLVERTEX2);

        // Copy data from tristrip into command buffer.

		memcpy (pwritevert, preadvert, size);

        // Advance the read pointer: Since the next vertex in the polyline
        // needs the previous vertex to complete a line (the
        // starting two vertex line for the next polyline batch to
        // be submitted), set the read pointer to point to the 
        // last vertex from the previous batch.

        preadvert += (numvertstodraw - 1);

        // Convert size to DWORD count.

		size = size / sizeof (DWORD);

        // Adjust size for HEADER, VC_FORMAT and VC_CNTL in packet.

        size += 3;

        // Write the size parameter to the packet header.

        gCommandBuf[0] |= ((size - 2) << 16);

        // Submit the packet.

		R128_CCESubmitPackets (gCommandBuf, size);

        // Adjust the count of remaining vertices to process.

        numvertsremaining -= (numvertstodraw - 1);
	}

    // Flush queued packet data to the CCE FIFO. Depending on the watermark
    // thresholds written to the PM4_BUFFER_WM_CNTL register, some primitive
    // data may still be queued up in the ring buffer. This data may not be
    // sent to the CCE FIFO until a future write to the PM4_BUFFER_DL_WPTR
    // register satisfies the watermark conditions, resulting in remaining
    // primitives being rendered later than expected. Flushing the ring
    // buffer here ensures all primitive data is rendered before this
    // function returns. However, note that this approach causes some loss
    // of concurency as the function won't return until all triangle data
    // has been flushed to the CCE FIFO. 

    R128_Flush ();

	return TRUE;
}

