/******************************Module*Header*******************************\
* Module Name: mcdprim.c
*
* These routines process the OpenGL rendering commands that appear in an
* MCDrvDraw() batch.  Note that the only OpenGL primitive which is invalid
* is LineLoop.  This gets decomposed by the caller into a LineStrip command.
*
* Copyright (c) 1996 Microsoft Corporation
\**************************************************************************/

#include "precomp.h"

#include "mcdhw.h"
#include "mcdutil.h"

#define MEMCHECK_VERTEX(p)\
    if (((UCHAR *)p < pRc->pMemMin) ||\
        ((UCHAR *)p > pRc->pMemMax)) {\
        MCDBG_PRINT("Invalid MCD vertex pointer!");\
        return NULL;\
    }

////////////////////////////////////////////////////////////////////////
//
// The functions below are local rendering-helper functions which call
// the real rendering routines in the driver.
//
////////////////////////////////////////////////////////////////////////


VOID static FASTCALL __MCDRenderPoint(DEVRC *pRc, MCDVERTEX *v)
{
    if (v->clipCode == 0)
	(*pRc->renderPoint)(pRc, v);
}

VOID static FASTCALL __MCDRenderLine(DEVRC *pRc, MCDVERTEX *v0,
                                     MCDVERTEX *v1, BOOL bResetLine)
{
    if (v0->clipCode | v1->clipCode)
    {
	/*
	 * The line must be clipped more carefully.  Cannot
	 * trivially accept the lines.
	 *
	 * If anding the codes is non-zero then every vertex
	 * in the line is outside of the same set of clipping
	 * planes (at least one).  Trivially reject the line.  
	 */
	if ((v0->clipCode & v1->clipCode) == 0)
	    (*pRc->clipLine)(pRc, v0, v1, bResetLine);
    }
    else
    {
	// Line is trivially accepted so render it
        (*pRc->renderLine)(pRc, v0, v1, bResetLine);
    }
}

VOID static FASTCALL __MCDRenderTriangle(DEVRC *pRc, MCDVERTEX *v0, 
                                         MCDVERTEX *v1, MCDVERTEX *v2)
{
    ULONG orCodes;

    /* Clip check */
    orCodes = v0->clipCode | v1->clipCode | v2->clipCode;
    if (orCodes)
    {
	/* Some kind of clipping is needed.
	 *
	 * If anding the codes is non-zero then every vertex
	 * in the triangle is outside of the same set of
	 * clipping planes (at least one).  Trivially reject
	 * the triangle.
	 */
	if (!(v0->clipCode & v1->clipCode & v2->clipCode))
	    (*pRc->clipTri)(pRc, v0, v1, v2, orCodes);
    }
    else
    {
	(*pRc->renderTri)(pRc, v0, v1, v2);
    }
}

VOID static FASTCALL __MCDRenderQuad(DEVRC *pRc, MCDVERTEX *v0, 
                                     MCDVERTEX *v1, MCDVERTEX *v2, MCDVERTEX *v3)
{
// Vertex ordering is important.  Line stippling uses it.

    ULONG savedTag;

    /* Render the quad as two triangles */
    savedTag = v2->flags & MCDVERTEX_EDGEFLAG;
    v2->flags &= ~MCDVERTEX_EDGEFLAG;
    (*pRc->renderTri)(pRc, v0, v1, v2);
    v2->flags |= savedTag;
    savedTag = v0->flags & MCDVERTEX_EDGEFLAG;
    v0->flags &= ~MCDVERTEX_EDGEFLAG;
    (*pRc->renderTri)(pRc, v2, v3, v0);
    v0->flags |= savedTag;
}

VOID static FASTCALL __MCDRenderClippedQuad(DEVRC *pRc, MCDVERTEX *v0, 
                                            MCDVERTEX *v1, MCDVERTEX *v2, MCDVERTEX *v3)
{
    ULONG orCodes;

    orCodes = v0->clipCode | v1->clipCode | v2->clipCode | v3->clipCode;

    if (orCodes)
    {
	/* Some kind of clipping is needed.
	 *
	 * If anding the codes is non-zero then every vertex
	 * in the quad is outside of the same set of
	 * clipping planes (at least one).  Trivially reject
	 * the quad.
	 */
        if (!(v0->clipCode & v1->clipCode & v2->clipCode & v3->clipCode))
        {
            /* Clip the quad as a polygon */
            MCDVERTEX *iv[4];

            iv[0] = v0;
            iv[1] = v1;
            iv[2] = v2;
            iv[3] = v3;
            (pRc->doClippedPoly)(pRc, &iv[0], 4, orCodes);
        }
    }
    else
    {
	__MCDRenderQuad(pRc, v0, v1, v2, v3);
    }
}

////////////////////////////////////////////////////////////////////////
//
// The functions below handle the processing of all of the primitives
// which may appear in an MCDCOMMAND.  This includes all of the OpenGL
// primitives, with the exception of line loops which are handled as
// line strips.
//
////////////////////////////////////////////////////////////////////////


MCDCOMMAND * FASTCALL __MCDPrimDrawPoints(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    LONG i, nIndices;
    MCDVERTEX *pv;
    VOID (FASTCALL *rp)(DEVRC *pRc, MCDVERTEX *v);

// Index mapping is always identity in Points.

//    ASSERTOPENGL(!pa->aIndices, "Index mapping must be identity\n");

    if (pCmd->clipCodes)
	rp = __MCDRenderPoint;
    else
	rp = pRc->renderPoint;

    (*pRc->beginPointDrawing)(pRc);

    // Render the points:

    pv = pCmd->pStartVertex;
    MEMCHECK_VERTEX(pv);
    i = pCmd->numIndices;
    MEMCHECK_VERTEX(pv + (i - 1));

    for (; i > 0; i--, pv++)
	(*rp)(pRc, pv);

    return pCmd->pNextCmd;
}


MCDCOMMAND * FASTCALL __MCDPrimDrawLines(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    LONG i, iLast2;
    UCHAR *pIndices;
    MCDVERTEX *pv, *pv0, *pv1;
    VOID (FASTCALL *rl)(DEVRC *pRc, MCDVERTEX *pv1, MCDVERTEX *pv2, BOOL bResetLine);

    iLast2 = pCmd->numIndices - 2;
    pv     = pCmd->pStartVertex;
    rl = pCmd->clipCodes ? __MCDRenderLine : pRc->renderLine;

    (*pRc->beginLineDrawing)(pRc);

    if (!(pIndices = pCmd->pIndices))
    {
	// Identity mapping

        MEMCHECK_VERTEX(pv);
        MEMCHECK_VERTEX(pv + (iLast2 + 1));
   
	for (i = 0; i <= iLast2; i += 2)
	{
	    /* setup for rendering this line */

            pRc->resetLineStipple = TRUE;

	    (*rl)(pRc, &pv[i], &pv[i+1], TRUE);
	}
    }
    else
    {
	for (i = 0; i <= iLast2; i += 2)
	{
            pv0 = &pv[pIndices[i]];
            pv1 = &pv[pIndices[i+1]];
            MEMCHECK_VERTEX(pv0);
            MEMCHECK_VERTEX(pv1);

	    /* setup for rendering this line */

            pRc->resetLineStipple = TRUE;

	    (*rl)(pRc, pv0, pv1, TRUE);
	}
    }

    (*pRc->endLineDrawing)(pRc);

    return pCmd->pNextCmd;
}


MCDCOMMAND * FASTCALL __MCDPrimDrawLineLoop(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    // NOTE:
    // Line loops are always converted tp line strips at the OpenGL
    // API level.  This routine is currently not used.

    MCDBG_PRINT("MCDPrimLineLoop: Invalid MCD command!");

    return pCmd->pNextCmd;
}


MCDCOMMAND * FASTCALL __MCDPrimDrawLineStrip(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    LONG i, iLast;
    UCHAR *pIndices;
    MCDVERTEX *pv, *pv0, *pv1;
    MCDVERTEX *vOld;
    VOID (FASTCALL *rl)(DEVRC *pRc, MCDVERTEX *pv1, MCDVERTEX *pv2, BOOL bResetLine);

    iLast = pCmd->numIndices - 1;
    pv    = pCmd->pStartVertex;
    rl = pCmd->clipCodes ? __MCDRenderLine : pRc->renderLine;
    if (iLast <= 0)
	return pCmd->pNextCmd;

    if (pCmd->flags & MCDCOMMAND_RESET_STIPPLE)
        pRc->resetLineStipple = TRUE;

    (*pRc->beginLineDrawing)(pRc);

    if (!(pIndices = pCmd->pIndices))
    {
	// Identity mapping
	// Add first line segment (NOTE: 0, 1)
       
        MEMCHECK_VERTEX(pv);
        MEMCHECK_VERTEX(pv + iLast);

	(*rl)(pRc, &pv[0], &pv[1], TRUE);

	// Add subsequent line segments (NOTE: i, i+1)
	for (i = 1; i < iLast; i++) {
	    (*rl)(pRc, &pv[i], &pv[i+1], FALSE);
        }
    }
    else
    {
	// Add first line segment (NOTE: 0, 1)

        pv0 = &pv[pIndices[0]];
        pv1 = &pv[pIndices[1]];
	(*rl)(pRc, pv0, pv1, TRUE);

	// Add subsequent line segments (NOTE: i, i+1)

	for (i = 1; i < iLast; i++) {
            pv0 = pv1;
            pv1 = &pv[pIndices[i+1]];
            MEMCHECK_VERTEX(pv1);
	    (*rl)(pRc, pv0, pv1, FALSE);
        }
    }

    (*pRc->endLineDrawing)(pRc);

    return pCmd->pNextCmd;
}


MCDCOMMAND * FASTCALL __MCDPrimDrawTriangles(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    LONG i, iLast3;
    UCHAR *pIndices;
    MCDVERTEX *pv, *pv0, *pv1, *pv2;
    VOID (FASTCALL *rt)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2);

    iLast3 = pCmd->numIndices - 3;
    pv     = pCmd->pStartVertex;
    if (pCmd->clipCodes)
	rt = __MCDRenderTriangle;
    else
	rt = pRc->renderTri;


    if (!(pIndices = pCmd->pIndices))
    {
	// Identity mapping

        MEMCHECK_VERTEX(pv);
        MEMCHECK_VERTEX(pv + (iLast3 + 2));

	for (i = 0; i <= iLast3; i += 3)
	{
	    /* setup for rendering this triangle */

            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = &pv[i+2];

	    /* Render the triangle (NOTE: i, i+1, i+2) */
	    (*rt)(pRc, &pv[i], &pv[i+1], &pv[i+2]);
	}
    }
    else
    {
	for (i = 0; i <= iLast3; i += 3)
	{
	    /* setup for rendering this triangle */

            pv0 = &pv[pIndices[i  ]];
            pv1 = &pv[pIndices[i+1]];
            pv2 = &pv[pIndices[i+2]];

            MEMCHECK_VERTEX(pv0);
            MEMCHECK_VERTEX(pv1);
            MEMCHECK_VERTEX(pv2);

            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = pv2;

	    /* Render the triangle (NOTE: i, i+1, i+2) */
	    (*rt)(pRc, pv0, pv1, pv2);
	}
    }

    return pCmd->pNextCmd;
}


MCDCOMMAND * FASTCALL __MCDPrimDrawTriangleStrip(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    LONG i, iLast3;
    UCHAR *pIndices;
    MCDVERTEX *pv, *pv0, *pv1, *pv2;
    VOID (FASTCALL *rt)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2);

    iLast3 = pCmd->numIndices - 3;
    pv     = pCmd->pStartVertex;
    if (pCmd->clipCodes)
	rt = __MCDRenderTriangle;
    else
	rt = pRc->renderTri;

    if (iLast3 < 0)
	return pCmd->pNextCmd;

    if (!(pIndices = pCmd->pIndices))
    {
	// Identity mapping

        MEMCHECK_VERTEX(pv);
        MEMCHECK_VERTEX(pv + (iLast3 + 2));

        pv[0].flags |= MCDVERTEX_EDGEFLAG;
        pv[1].flags |= MCDVERTEX_EDGEFLAG;

	for (i = 0; i <= iLast3; i++)
	{
	    /* setup for rendering this triangle */

            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = &pv[i+2];
            pv[i+2].flags |= MCDVERTEX_EDGEFLAG;

	    /* Render the triangle (NOTE: i, i+1, i+2) */
	    (*rt)(pRc, &pv[i], &pv[i+1], &pv[i+2]);

	    if (++i > iLast3)
		break;

            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = &pv[i+2];
            pv[i+2].flags |= MCDVERTEX_EDGEFLAG;

	    /* Render the triangle (NOTE: i+1, i, i+2) */
	    (*rt)(pRc, &pv[i+1], &pv[i], &pv[i+2]);
	}
    }
    else
    {

	pv1 = &pv[pIndices[0]];
        MEMCHECK_VERTEX(pv1);
        pv1->flags |= MCDVERTEX_EDGEFLAG;

	pv2 = &pv[pIndices[1]];
        MEMCHECK_VERTEX(pv2);
        pv2->flags |= MCDVERTEX_EDGEFLAG;

	for (i = 0; i <= iLast3; i++)
	{
	    /* setup for rendering this triangle */

            pRc->resetLineStipple = TRUE;

            pv0 = pv1;
            pv1 = pv2;

            pv2 = &pv[pIndices[i+2]];
            MEMCHECK_VERTEX(pv2);
	    pv2->flags |= MCDVERTEX_EDGEFLAG;

            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = pv2;

	    /* Render the triangle (NOTE: i, i+1, i+2) */
	    (*rt)(pRc, pv0, pv1, pv2);

	    if (++i > iLast3)
		break;

            pv0 = pv1;
            pv1 = pv2;

            pv2 = &pv[pIndices[i+2]];
            MEMCHECK_VERTEX(pv2);
	    pv2->flags |= MCDVERTEX_EDGEFLAG;

	    /* setup for rendering this triangle */

            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = pv2;

	    /* Render the triangle (NOTE: i+1, i, i+2) */
	    (*rt)(pRc, pv1, pv0, pv2);
	}
    }

    return pCmd->pNextCmd;
}


MCDCOMMAND * FASTCALL __MCDPrimDrawTriangleFan(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    LONG i, iLast2;
    UCHAR *pIndices;
    MCDVERTEX *pv, *pv0, *pv1, *pv2;
    VOID (FASTCALL *rt)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2);

    iLast2 = pCmd->numIndices - 2;
    pv     = pCmd->pStartVertex;
    if (pCmd->clipCodes)
	rt = __MCDRenderTriangle;
    else
	rt = pRc->renderTri;

    if (iLast2 <= 0)
	return pCmd->pNextCmd;

    if (!(pIndices = pCmd->pIndices))
    {
	// Identity mapping

        MEMCHECK_VERTEX(pv);
        MEMCHECK_VERTEX(pv + (iLast2 + 1));

        pv[0].flags |= MCDVERTEX_EDGEFLAG;
        pv[1].flags |= MCDVERTEX_EDGEFLAG;

	for (i = 1; i <= iLast2; i++)
	{
	    /* setup for rendering this triangle */

            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = &pv[i+1];
            pv[i+1].flags |= MCDVERTEX_EDGEFLAG;

	    /* Render the triangle (NOTE: 0, i, i+1) */
	    (*rt)(pRc, &pv[0], &pv[i], &pv[i+1]);
	}
    }
    else
    {
	// Initialize first 2 vertices so we can start rendering the tfan
	// below.  The edge flags are not modified by our lower level routines.

        pv0 = &pv[pIndices[0]];
        MEMCHECK_VERTEX(pv0);
	pv0->flags |= MCDVERTEX_EDGEFLAG;

        pv2 = &pv[pIndices[1]];
        MEMCHECK_VERTEX(pv2);
	pv2->flags |= MCDVERTEX_EDGEFLAG;

	for (i = 1; i <= iLast2; i++)
	{
            pv1 = pv2;

            pv2 = &pv[pIndices[i+1]];
            MEMCHECK_VERTEX(pv2);
            pv2->flags |= MCDVERTEX_EDGEFLAG;

	    /* setup for rendering this triangle */
            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = pv2;

	    /* Render the triangle (NOTE: 0, i, i+1) */
	    (*rt)(pRc, pv0, pv1, pv2);
	}
    }

    return pCmd->pNextCmd;    
}


MCDCOMMAND * FASTCALL __MCDPrimDrawQuads(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    LONG i, iLast4;
    UCHAR *pIndices;
    MCDVERTEX *pv, *pv0, *pv1, *pv2, *pv3;
    VOID (FASTCALL *rq)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2,
                        MCDVERTEX *v3);

    iLast4 = pCmd->numIndices - 4;
    pv     = pCmd->pStartVertex;

    if (pCmd->clipCodes)
	rq = __MCDRenderClippedQuad;
    else
	rq = __MCDRenderQuad;

    if (!(pIndices = pCmd->pIndices))
    {

        MEMCHECK_VERTEX(pv);
        MEMCHECK_VERTEX(pv + (iLast4 + 3));

	// Identity mapping
	for (i = 0; i <= iLast4; i += 4)
	{
            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = &pv[i+3];

	    /* Render the quad (NOTE: i, i+1, i+2, i+3) */
	    (*rq)(pRc, &pv[i], &pv[i+1], &pv[i+2], &pv[i+3]);
	}
    }
    else
    {
	for (i = 0; i <= iLast4; i += 4)
	{

            pv0 = &pv[pIndices[i  ]];
            pv1 = &pv[pIndices[i+1]];
            pv2 = &pv[pIndices[i+2]];
            pv3 = &pv[pIndices[i+3]];

            MEMCHECK_VERTEX(pv0);
            MEMCHECK_VERTEX(pv1);
            MEMCHECK_VERTEX(pv2);
            MEMCHECK_VERTEX(pv3);


            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = pv3;

	    /* Render the quad (NOTE: i, i+1, i+2, i+3) */
	    (*rq)(pRc, pv0, pv1, pv2, pv3);
	}
    }

    return pCmd->pNextCmd;
}


MCDCOMMAND * FASTCALL __MCDPrimDrawQuadStrip(DEVRC *pRc, MCDCOMMAND *pCmd)
{
    ULONG i, iLast4;
    UCHAR *pIndices;
    MCDVERTEX *pv, *pv0, *pv1, *pv2, *pv3;
    VOID (FASTCALL *rq)(DEVRC *pRc, MCDVERTEX *v0, MCDVERTEX *v1, MCDVERTEX *v2,
	MCDVERTEX *v3);

    iLast4 = pCmd->numIndices - 4;
    pv     = pCmd->pStartVertex;
    if (pCmd->clipCodes)
	rq = __MCDRenderClippedQuad;
    else
	rq = __MCDRenderQuad;

    // Vertex ordering is important.  Line stippling uses it.

    if (!(pIndices = pCmd->pIndices))
    {
	// Identity mapping

        MEMCHECK_VERTEX(pv);
        MEMCHECK_VERTEX(pv + (iLast4 + 3));

        pv[0].flags |= MCDVERTEX_EDGEFLAG;
        pv[1].flags |= MCDVERTEX_EDGEFLAG;

	for (i = 0; i <= iLast4; i += 2)
	{
            pv[i+2].flags |= MCDVERTEX_EDGEFLAG;
            pv[i+3].flags |= MCDVERTEX_EDGEFLAG;

	    /* setup for rendering this quad */

            pRc->pvProvoking = &pv[i+3];
            pRc->resetLineStipple = TRUE;

	    /* Render the quad (NOTE: i, i+1, i+3, i+2) */
	    (*rq)(pRc, &pv[i], &pv[i+1], &pv[i+3], &pv[i+2]);
	}
    }
    else
    {
	// Initialize first 2 vertices so we can start rendering the quad
	// below.  The edge flags are not modified by our lower level routines.

        pv2 = &pv[pIndices[0]];
        MEMCHECK_VERTEX(pv2);
        pv2->flags |= MCDVERTEX_EDGEFLAG;

        pv3 = &pv[pIndices[1]];
        MEMCHECK_VERTEX(pv3);
        pv3->flags |= MCDVERTEX_EDGEFLAG;

	for (i = 0; i <= iLast4; i += 2)
	{

            pv0 = pv2;
            pv1 = pv3;

            pv2 = &pv[pIndices[i+2]];
            MEMCHECK_VERTEX(pv2);
            pv2->flags |= MCDVERTEX_EDGEFLAG;

            pv3 = &pv[pIndices[i+3]];
            MEMCHECK_VERTEX(pv3);
            pv3->flags |= MCDVERTEX_EDGEFLAG;

	    /* setup for rendering this quad */

            pRc->resetLineStipple = TRUE;
            pRc->pvProvoking = pv3;

	    /* Render the quad (NOTE: i, i+1, i+3, i+2) */

	    (*rq)(pRc, pv0, pv1, pv3, pv2);
	}
    }

    return pCmd->pNextCmd;
}


MCDCOMMAND * FASTCALL __MCDPrimDrawPolygon(DEVRC *pRc, MCDCOMMAND *pCmd)
{

//    ASSERTOPENGL(!pCmd->pIndices, "Index mapping must be identity\n");

    // Reset the line stipple if this is a new polygon:

    if (pCmd->flags & MCDCOMMAND_RESET_STIPPLE)
        pRc->resetLineStipple = TRUE;

    // Note that the provoking vertex is set in clipPolygon:

    MEMCHECK_VERTEX(pCmd->pStartVertex);
    MEMCHECK_VERTEX(pCmd->pStartVertex + (pCmd->numIndices-1));

    (*pRc->clipPoly)(pRc, pCmd->pStartVertex, pCmd->numIndices);

    return pCmd->pNextCmd;
}
