/******************************Module*Header*******************************\
* Module Name: mcdclip.c
*
* Contains the line and polygon clipping routines for an MCD driver.
*
* Copyright (c) 1996 Microsoft Corporation
\**************************************************************************/

#include "precomp.h"
#include "mcdhw.h"
#include "mcdutil.h"
#include "mcdmath.h"

MCDCOORD __MCD_frustumClipPlanes[6] = {
    {(MCDFLOAT) 1.0, (MCDFLOAT) 0.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0 }, // left
    {(MCDFLOAT)-1.0, (MCDFLOAT) 0.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0 }, // right
    {(MCDFLOAT) 0.0, (MCDFLOAT) 1.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0 }, // bottom
    {(MCDFLOAT) 0.0, (MCDFLOAT)-1.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0 }, // top
    {(MCDFLOAT) 0.0, (MCDFLOAT) 0.0, (MCDFLOAT) 1.0, (MCDFLOAT) 1.0 }, // zNear
    {(MCDFLOAT) 0.0, (MCDFLOAT) 0.0, (MCDFLOAT)-1.0, (MCDFLOAT) 1.0 }, // zFar
};


////////////////////////////////////////////////////////////////////////
// Clipping macros used to build clip functions below.
////////////////////////////////////////////////////////////////////////


#define __MCD_CLIP_POS(v, a, b, t) \
    v->clipCoord.x = t*(a->clipCoord.x - b->clipCoord.x) + b->clipCoord.x;  \
    v->clipCoord.y = t*(a->clipCoord.y - b->clipCoord.y) + b->clipCoord.y;  \
    v->clipCoord.z = t*(a->clipCoord.z - b->clipCoord.z) + b->clipCoord.z;  \
    v->clipCoord.w = t*(a->clipCoord.w - b->clipCoord.w) + b->clipCoord.w

// Note that we compute the values needed for both "cheap" fog only...

#define __MCD_CLIP_FOG(v, a, b, t) \
    v->fog = t * (a->fog - b->fog) + b->fog;

#define __MCD_CLIP_COLOR(v, a, b, t) \
    v->colors[__MCD_FRONTFACE].r = t*(a->colors[__MCD_FRONTFACE].r      \
        - b->colors[__MCD_FRONTFACE].r) + b->colors[__MCD_FRONTFACE].r; \
    v->colors[__MCD_FRONTFACE].g = t*(a->colors[__MCD_FRONTFACE].g      \
        - b->colors[__MCD_FRONTFACE].g) + b->colors[__MCD_FRONTFACE].g; \
    v->colors[__MCD_FRONTFACE].b = t*(a->colors[__MCD_FRONTFACE].b      \
        - b->colors[__MCD_FRONTFACE].b) + b->colors[__MCD_FRONTFACE].b; \
    v->colors[__MCD_FRONTFACE].a = t*(a->colors[__MCD_FRONTFACE].a      \
        - b->colors[__MCD_FRONTFACE].a) + b->colors[__MCD_FRONTFACE].a

#define __MCD_CLIP_BACKCOLOR(v, a, b, t) \
    v->colors[__MCD_BACKFACE].r = t*(a->colors[__MCD_BACKFACE].r        \
        - b->colors[__MCD_BACKFACE].r) + b->colors[__MCD_BACKFACE].r;   \
    v->colors[__MCD_BACKFACE].g = t*(a->colors[__MCD_BACKFACE].g        \
        - b->colors[__MCD_BACKFACE].g) + b->colors[__MCD_BACKFACE].g;   \
    v->colors[__MCD_BACKFACE].b = t*(a->colors[__MCD_BACKFACE].b        \
        - b->colors[__MCD_BACKFACE].b) + b->colors[__MCD_BACKFACE].b;   \
    v->colors[__MCD_BACKFACE].a = t*(a->colors[__MCD_BACKFACE].a        \
        - b->colors[__MCD_BACKFACE].a) + b->colors[__MCD_BACKFACE].a

#define __MCD_CLIP_INDEX(v, a, b, t) \
    v->colors[__MCD_FRONTFACE].r = t*(a->colors[__MCD_FRONTFACE].r      \
        - b->colors[__MCD_FRONTFACE].r) + b->colors[__MCD_FRONTFACE].r

#define __MCD_CLIP_BACKINDEX(v, a, b, t) \
    v->colors[__MCD_BACKFACE].r = t*(a->colors[__MCD_BACKFACE].r        \
        - b->colors[__MCD_BACKFACE].r) + b->colors[__MCD_BACKFACE].r

#define __MCD_CLIP_TEXTURE(v, a, b, t) \
    v->texCoord.x = t*(a->texCoord.x - b->texCoord.x) + b->texCoord.x; \
    v->texCoord.y = t*(a->texCoord.y - b->texCoord.y) + b->texCoord.y;
#ifdef CLIP_TEXTURE_XFORM
    v->texCoord.z = t*(a->texCoord.z - b->texCoord.z) + b->texCoord.z; \
    v->texCoord.w = t*(a->texCoord.w - b->texCoord.w) + b->texCoord.w
#endif

////////////////////////////////////////////////////////////////////////
// Clipping functions to clip vertices:
////////////////////////////////////////////////////////////////////////

static VOID FASTCALL Clip(MCDVERTEX *dst, const MCDVERTEX *a,
                          const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
}

static VOID FASTCALL ClipC(MCDVERTEX *dst, const MCDVERTEX *a, 
                           const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_COLOR(dst,a,b,t);
}

static VOID FASTCALL ClipI(MCDVERTEX *dst, const MCDVERTEX *a, 
                           const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_INDEX(dst,a,b,t);
}

static VOID FASTCALL ClipBC(MCDVERTEX *dst, const MCDVERTEX *a, 
                            const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_COLOR(dst,a,b,t);
    __MCD_CLIP_BACKCOLOR(dst,a,b,t);
}

static VOID FASTCALL ClipBI(MCDVERTEX *dst, const MCDVERTEX *a, 
                            const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_INDEX(dst,a,b,t);
    __MCD_CLIP_BACKINDEX(dst,a,b,t);
}

static VOID FASTCALL ClipT(MCDVERTEX *dst, const MCDVERTEX *a, 
                           const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}

static VOID FASTCALL ClipIT(MCDVERTEX *dst, const MCDVERTEX *a, 
                            const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_INDEX(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}

static VOID FASTCALL ClipBIT(MCDVERTEX *dst, const MCDVERTEX *a, 
                             const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_INDEX(dst,a,b,t);
    __MCD_CLIP_BACKINDEX(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}

static VOID FASTCALL ClipCT(MCDVERTEX *dst, const MCDVERTEX *a, 
                            const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_COLOR(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}


static VOID FASTCALL ClipBCT(MCDVERTEX *dst, const MCDVERTEX *a, 
                             const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_COLOR(dst,a,b,t);
    __MCD_CLIP_BACKCOLOR(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}

static VOID FASTCALL ClipF(MCDVERTEX *dst, const MCDVERTEX *a, 
                           const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
}

static VOID FASTCALL ClipIF(MCDVERTEX *dst, const MCDVERTEX *a, 
                            const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_INDEX(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
}

static VOID FASTCALL ClipCF(MCDVERTEX *dst, const MCDVERTEX *a, 
                            const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_COLOR(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
}

static VOID FASTCALL ClipBCF(MCDVERTEX *dst, const MCDVERTEX *a, 
                             const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_COLOR(dst,a,b,t);
    __MCD_CLIP_BACKCOLOR(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
}

static VOID FASTCALL ClipBIF(MCDVERTEX *dst, const MCDVERTEX *a, 
                             const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_INDEX(dst,a,b,t);
    __MCD_CLIP_BACKINDEX(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
}

static VOID FASTCALL ClipFT(MCDVERTEX *dst, const MCDVERTEX *a, 
                            const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}

static VOID FASTCALL ClipIFT(MCDVERTEX *dst, const MCDVERTEX *a, 
                             const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_INDEX(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}

static VOID FASTCALL ClipBIFT(MCDVERTEX *dst, const MCDVERTEX *a, 
                              const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_INDEX(dst,a,b,t);
    __MCD_CLIP_BACKINDEX(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}


static VOID FASTCALL ClipCFT(MCDVERTEX *dst, const MCDVERTEX *a, 
                             const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_COLOR(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}

static VOID FASTCALL ClipBCFT(MCDVERTEX *dst, const MCDVERTEX *a, 
                              const MCDVERTEX *b, MCDFLOAT t)
{
    __MCD_CLIP_POS(dst,a,b,t);
    __MCD_CLIP_COLOR(dst,a,b,t);
    __MCD_CLIP_BACKCOLOR(dst,a,b,t);
    __MCD_CLIP_FOG(dst,a,b,t);
    __MCD_CLIP_TEXTURE(dst,a,b,t);
}

static VOID (FASTCALL *clipProcs[20])(MCDVERTEX*, const MCDVERTEX*, 
                                      const MCDVERTEX*, MCDFLOAT) =
{
    Clip,   ClipI,   ClipC,   ClipBI,   ClipBC,
    ClipF,  ClipIF,  ClipCF,  ClipBIF,  ClipBCF,
    ClipT,  ClipIT,  ClipCT,  ClipBIT,  ClipBCT,
    ClipFT, ClipIFT, ClipCFT, ClipBIFT, ClipBCFT,
};


VOID FASTCALL __MCDPickClipFuncs(DEVRC *pRc)
{
    LONG line = 0, poly = 0;
    BOOL twoSided = (pRc->MCDState.enables & MCD_LIGHTING_ENABLE) &&
	            (pRc->MCDState.twoSided);

    if (pRc->bRGBMode) {
	if (pRc->MCDState.shadeModel != GL_FLAT) {
	    line = 2;
            poly = (twoSided ? 4 : 2);
	}
    } else {
	if (pRc->MCDState.shadeModel != GL_FLAT) {
	    line = 1;
            poly = (twoSided ? 3 : 1);
	}
    }
    if ((pRc->bCheapFog) && !(pRc->MCDState.shadeModel == GL_SMOOTH)) {
	{
	    line += 5;
	    poly += 5;
	}
    }

    if (pRc->MCDState.textureEnabled) {
	line += 10;
	poly += 10;
    }

    pRc->lineClipParam = clipProcs[line];
    pRc->polyClipParam = clipProcs[poly];
}


////////////////////////////////////////////////////////////////////////
// The real primitive clippers:
////////////////////////////////////////////////////////////////////////

/*
** The following is a discussion of the math used to do edge clipping against
** a clipping plane.
** 
**     P1 is an end point of the edge
**     P2 is the other end point of the edge
** 
**     Q = t*P1 + (1 - t)*P2
**     That is, Q lies somewhere on the line formed by P1 and P2.
** 
**     0 <= t <= 1
**     This constrains Q to lie between P1 and P2.
** 
**     C is the plane equation for the clipping plane
** 
**     D1 = P1 dot C
**     D1 is the distance between P1 and C.  If P1 lies on the plane
**     then D1 will be zero.  The sign of D1 will determine which side
**     of the plane that P1 is on, with negative being outside.
** 
**     D2 = P2 dot C
**     D2 is the distance between P2 and C.  If P2 lies on the plane
**     then D2 will be zero.  The sign of D2 will determine which side
**     of the plane that P2 is on, with negative being outside.
** 
** Because we are trying to find the intersection of the P1 P2 line
** segment with the clipping plane we require that:
** 
**     Q dot C = 0
** 
** Therefore
** 
**     (t*P1 + (1 - t)*P2) dot C = 0
** 
**     (t*P1 + P2 - t*P2) dot C = 0
** 
**     t*P1 dot C + P2 dot C - t*P2 dot C = 0
** 
** Substituting D1 and D2 in
** 
**     t*D1 + D2 - t*D2 = 0
** 
** Solving for t
** 
**     t = -D2 / (D1 - D2)
** 
**     t = D2 / (D2 - D1)
*/


static LONG clipToPlane(DEVRC *pRc, MCDVERTEX **iv, LONG niv,
                        MCDVERTEX **ov, MCDCOORD *plane)
{
    LONG i, nout, generated;
    MCDVERTEX *s, *p, *newVertex, *temp;
    MCDFLOAT pDist, sDist, t;
    MCDFLOAT zero = ZERO;
    VOID (FASTCALL *clip)(MCDVERTEX*, const MCDVERTEX*, const MCDVERTEX*, MCDFLOAT);

    nout = 0;
    generated = 0;
    temp = pRc->pNextClipTemp;
    clip = pRc->polyClipParam;

    s = iv[niv-1];
    sDist = (s->clipCoord.x * plane->x) + (s->clipCoord.y * plane->y) +
	    (s->clipCoord.z * plane->z) + (s->clipCoord.w * plane->w);
    for (i = 0; i < niv; i++) {
	p = iv[i];
	pDist = (p->clipCoord.x * plane->x) + (p->clipCoord.y * plane->y) +
		(p->clipCoord.z * plane->z) + (p->clipCoord.w * plane->w);
	if (pDist >= zero) {
	    /* p is inside the clipping plane half space */
	    if (sDist >= zero) {
		/* s is inside the clipping plane half space */
		*ov++ = p;
		nout++;
	    } else {
		/* s is outside the clipping plane half space */
		t = pDist / (pDist - sDist);
		newVertex = temp++;
		(*clip)(newVertex, s, p, t);
                newVertex->flags = s->flags;
                newVertex->clipCode = s->clipCode;
		*ov++ = newVertex;
		*ov++ = p;
		nout += 2;

		if (++generated >= 3) {
		    /* Toss the non-convex polygon */
		    return 0;
		}
	    }
	} else {
	    /* p is outside the clipping plane half space */
	    if (sDist >= zero) {
		/*
		** s is inside the clipping plane half space
		**
		** NOTE: To avoid cracking in polygons with shared
		** clipped edges we always compute "t" from the out
		** vertex to the in vertex.  The above clipping code gets
		** this for free (p is in and s is out).  In this code p
		** is out and s is in, so we reverse the t computation
		** and the argument order to __MCDDoClip.
		*/
		t = sDist / (sDist - pDist);
		newVertex = temp++;
		(*clip)(newVertex, p, s, t);
		newVertex->flags = s->flags | MCDVERTEX_EDGEFLAG;
                newVertex->clipCode = p->clipCode;
		*ov++ = newVertex;
		nout++;

		if (++generated >= 3) {
		    /* Toss the non-convex polygon */
		    return 0;
		}
	    } else {
		/* both points are outside */
	    }
	}
	s = p;
	sDist = pDist;
    }
    pRc->pNextClipTemp = temp;
    return nout;
}

/* 
** Identical to clipToPlane(), except that the clipping is done in eye
** space.
*/
static LONG clipToPlaneEye(DEVRC *pRc, MCDVERTEX **iv, LONG niv,
			   MCDVERTEX **ov, MCDCOORD *plane)
{
    LONG i, nout, generated;
    MCDVERTEX *s, *p, *newVertex, *temp;
    MCDFLOAT pDist, sDist, t;
    MCDFLOAT zero = __MCDZERO;
    VOID (FASTCALL *clip)(MCDVERTEX*, const MCDVERTEX*, const MCDVERTEX*, MCDFLOAT);

    nout = 0;
    generated = 0;
    temp = pRc->pNextClipTemp;
    clip = pRc->polyClipParam;

    s = iv[niv-1];
    sDist = (s->eyeCoord.x * plane->x) +
	    (s->eyeCoord.y * plane->y) +
	    (s->eyeCoord.z * plane->z) +
	    (s->eyeCoord.w * plane->w);
    for (i = 0; i < niv; i++) {
	p = iv[i];
	pDist = (p->eyeCoord.x * plane->x) +
		(p->eyeCoord.y * plane->y) +
		(p->eyeCoord.z * plane->z) +
		(p->eyeCoord.w * plane->w);
	if (pDist >= zero) {
	    /* p is inside the clipping plane half space */
	    if (sDist >= zero) {
		/* s is inside the clipping plane half space */
		*ov++ = p;
		nout++;
	    } else {
		/* s is outside the clipping plane half space */
		t = pDist / (pDist - sDist);
		newVertex = temp++;
		(*clip)(newVertex, s, p, t);
		newVertex->eyeCoord.x = t*(s->eyeCoord.x - p->eyeCoord.x) + p->eyeCoord.x;
		newVertex->eyeCoord.y = t*(s->eyeCoord.y - p->eyeCoord.y) + p->eyeCoord.y;
		newVertex->eyeCoord.z = t*(s->eyeCoord.z - p->eyeCoord.z) + p->eyeCoord.z;
		newVertex->eyeCoord.w = t*(s->eyeCoord.w - p->eyeCoord.w) + p->eyeCoord.w;
		newVertex->flags = s->flags;
                newVertex->clipCode = s->clipCode;      //!!!!
		*ov++ = newVertex;
		*ov++ = p;
		nout += 2;

		if (++generated >= 3) {
		    /* Toss the non-convex polygon */
		    return 0;
		}
	    }
	} else {
	    /* p is outside the clipping plane half space */
	    if (sDist >= zero) {
		/*
		** s is inside the clipping plane half space
		**
		** NOTE: To avoid cracking in polygons with shared
		** clipped edges we always compute "t" from the out
		** vertex to the in vertex.  The above clipping code gets
		** this for free (p is in and s is out).  In this code p
		** is out and s is in, so we reverse the t computation
		** and the argument order to __MCDDoClip.
		*/
		t = sDist / (sDist - pDist);
		newVertex = temp++;
		(*clip)(newVertex, p, s, t);
		newVertex->eyeCoord.x = t*(p->eyeCoord.x - s->eyeCoord.x) + s->eyeCoord.x;
		newVertex->eyeCoord.y = t*(p->eyeCoord.y - s->eyeCoord.y) + s->eyeCoord.y;
		newVertex->eyeCoord.z = t*(p->eyeCoord.z - s->eyeCoord.z) + s->eyeCoord.z;
		newVertex->eyeCoord.w = t*(p->eyeCoord.w - s->eyeCoord.w) + s->eyeCoord.w;
		newVertex->flags = s->flags | MCDVERTEX_EDGEFLAG;
                newVertex->clipCode = p->clipCode;
		*ov++ = newVertex;
		nout++;

		if (++generated >= 3) {
		    /* Toss the non-convex polygon */
		    return 0;
		}
	    } else {
		/* both points are outside */
	    }
	}
	s = p;
	sDist = pDist;
    }
    pRc->pNextClipTemp = temp;
    return nout;
}

/*
** Each clipping plane can add at most one vertex to a convex polygon (it may
** remove up to all of the vertices).  The clipping will leave a polygon
** convex.  Because of this the maximum number of verticies output from
** the clipToPlane procedure will be total number of clip planes (assuming
** each plane adds one new vertex) plus the original number of verticies
** (3 since this if for triangles).
*/

#define __MCD_TOTAL_CLIP_PLANES 6 + MCD_MAX_USER_CLIP_PLANES
#define __MCD_MAX_POLYGON_CLIP_SIZE     256

#define	__MCD_MAX_CLIP_VERTEX (__MCD_TOTAL_CLIP_PLANES + __MCD_MAX_POLYGON_CLIP_SIZE)


void FASTCALL __MCDDoClippedPolygon(DEVRC *pRc, MCDVERTEX **iv, ULONG nout,
                                    ULONG allClipCodes)
{
    MCDVERTEX *ov[__MCD_TOTAL_CLIP_PLANES][__MCD_MAX_CLIP_VERTEX];
    MCDVERTEX **ivp;
    MCDVERTEX **ovp;
    MCDVERTEX *p0, *p1, *p2;
    MCDCOORD *plane;
    LONG i;
    MCDFLOAT one;
    VOID (FASTCALL *rt)(DEVRC*, MCDVERTEX*, MCDVERTEX*, MCDVERTEX*);
    MCDFLOAT llx, lly, urx, ury;
    MCDFLOAT winx, winy;
    ULONG clipCodes;

    /*
    ** Reset nextClipTemp pointer for any new verticies that are generated
    ** during the clipping.
    */

    pRc->pNextClipTemp = &pRc->clipTemp[0];

    ivp = &iv[0];

    /*
    ** Check each of the clipping planes by examining the allClipCodes
    ** mask. Note that no bits will be set in allClipCodes for clip
    ** planes that are not enabled.
    */
    if (allClipCodes) {

	/* Now clip against the clipping planes */
	ovp = &ov[0][0];

	/* 
	** Do user clip planes first, because we will maintain eye coordinates
	** only while doing user clip planes.  They are ignored for the 
	** frustum clipping planes.
	*/
	clipCodes = (allClipCodes >> 6) & __MCD_USER_CLIP_MASK;
	if (clipCodes) {
	    plane = &pRc->MCDState.userClipPlanes[0];
	    do {
		if (clipCodes & 1) {
		    nout = clipToPlaneEye(pRc, ivp, nout, ovp, plane);
		    if (nout < 3) {
			return;
		    }
		    ivp = ovp;
		    ovp += __MCD_MAX_CLIP_VERTEX;
		}
		clipCodes >>= 1;
		plane++;
	    } while (clipCodes);
	}

	allClipCodes &= MCD_CLIP_MASK;
	if (allClipCodes) {
	    plane = &__MCD_frustumClipPlanes[0];
	    do {
		if (allClipCodes & 1) {
		    nout = clipToPlane(pRc, ivp, nout, ovp, plane);
		    if (nout < 3) {
			return;
		    }
		    ivp = ovp;
		    ovp += __MCD_MAX_CLIP_VERTEX;
		}
		allClipCodes >>= 1;
		plane++;
	    } while (allClipCodes);
	}

	/*
	** Calculate final screen coordinates.  Next phase of polygon
	** processing assumes that window coordinates are already computed.
	*/

	ovp = ivp;
	one = __MCDONE;

	llx = pRc->MCDViewport.xCenter - pRc->MCDViewport.xScale;
	urx = pRc->MCDViewport.xCenter + pRc->MCDViewport.xScale;

	if (pRc->MCDViewport.yScale > 0) {
	    lly = pRc->MCDViewport.yCenter - pRc->MCDViewport.yScale;
	    ury = pRc->MCDViewport.yCenter + pRc->MCDViewport.yScale;
	} else {
	    lly = pRc->MCDViewport.yCenter + pRc->MCDViewport.yScale;
	    ury = pRc->MCDViewport.yCenter - pRc->MCDViewport.yScale;
	}

	for (i = nout; --i >= 0; ) {
	    MCDFLOAT wInv;

	    p0 = *ovp++;

	    if (p0->clipCoord.w == (MCDFLOAT) 0.0)
		wInv = (MCDFLOAT) 0.0; 
	    else 
		wInv = one / p0->clipCoord.w;

	    winx = p0->clipCoord.x * pRc->MCDViewport.xScale * wInv + 
                   pRc->MCDViewport.xCenter;

	    winy = p0->clipCoord.y * pRc->MCDViewport.yScale * wInv + 
                   pRc->MCDViewport.yCenter;

	    /* 
	    ** Check if these window coordinates are legal.  At this 
	    ** point, it is quite possible that they are not.  Trivially
	    ** pull them into the legal viewport region if necessary.
	    */

	    if (winx < llx) winx = llx;
	    else if (winx > urx) winx = urx;
	    if (winy < lly) winy = lly;
	    else if (winy > ury) winy = ury;

	    p0->windowCoord.x = winx;
	    p0->windowCoord.y = winy;
	    p0->windowCoord.z = p0->clipCoord.z * pRc->MCDViewport.zScale * wInv + 
                                pRc->MCDViewport.zCenter;
	    p0->windowCoord.w = wInv;
	}
    }

    /*
    ** Subdivide the clipped polygon into triangles.  Only convex polys
    ** are supported so this is okay to do.  Non-convex polys will do
    ** something odd here, but thats the clients fault.
    */
    p0 = *ivp++;
    p1 = *ivp++;
    p2 = *ivp++;
    rt = pRc->renderTri;
    if (nout == 3) {
	(*rt)(pRc, p0, p1, p2);
    } else {
	for (i = 0; i < (LONG)nout - 2; i++) {
	    ULONG t1, t2;
	    if (i == 0) {
		/*
		** Third edge of first sub-triangle is always non-boundary
		*/
		t1 = p2->flags & MCDVERTEX_EDGEFLAG;
		p2->flags &= ~MCDVERTEX_EDGEFLAG;
		(*rt)(pRc, p0, p1, p2);
		p2->flags |= t1;
	    } else
	    if (i == (LONG)nout - 3) {
		/*
		** First edge of last sub-triangle is always non-boundary
		*/
		t1 = p0->flags & MCDVERTEX_EDGEFLAG;
		p0->flags &= ~MCDVERTEX_EDGEFLAG;
		(*rt)(pRc, p0, p1, p2);
		p0->flags |= t1;
	    } else {
		/*
		** Interior sub-triangles have the first and last edge
		** marked non-boundary
		*/

		t1 = p0->flags & MCDVERTEX_EDGEFLAG;
		t2 = p2->flags & MCDVERTEX_EDGEFLAG;
		p0->flags &= ~MCDVERTEX_EDGEFLAG;
		p2->flags &= ~MCDVERTEX_EDGEFLAG;
		(*rt)(pRc, p0, p1, p2);
		p0->flags |= t1;
		p2->flags |= t2;
	    }
	    p1 = p2;
	    p2 = (MCDVERTEX *) *ivp++;
	}
    }
}


VOID FASTCALL __MCDClipPolygon(DEVRC *pRc, MCDVERTEX *v0, ULONG nv)
{
    MCDVERTEX *iv[__MCD_MAX_POLYGON_CLIP_SIZE];

    MCDVERTEX **ivp;
    LONG i;
    ULONG andCodes, orCodes;

    pRc->pvProvoking = v0;

    /*
    ** Generate array of addresses of the verticies.  And all the
    ** clip codes together while we are at it.
    */
    ivp = &iv[0];
    andCodes = 0;
    orCodes = 0;
    for (i = nv; --i >= 0; ) {
	andCodes &= v0->clipCode;
	orCodes |= v0->clipCode;
	*ivp++ = v0++;
    }

    if (andCodes != 0) {
	/*
	** Trivially reject the polygon.  If andCodes is non-zero then
	** every vertex in the polygon is outside of the same set of
	** clipping planes (at least one).
	*/
	return;
    }
    __MCDDoClippedPolygon(pRc, &iv[0], nv, orCodes);
}

void FASTCALL __MCDClipTriangle(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b,
                                MCDVERTEX *c, ULONG orCodes)
{
    MCDVERTEX *iv[3];

    iv[0] = a;
    iv[1] = b;
    iv[2] = c;

    __MCDDoClippedPolygon(pRc, &iv[0], 3, orCodes);
}


////////////////////////////////////////////////////////////////////////
// Line clipping:
////////////////////////////////////////////////////////////////////////

//
// Clip a line against the frustum clip planes and any user clipping planes.
// If an edge remains after clipping then compute the window coordinates
// and invoke the renderer.
//
// Notice:  This algorithim is an example of an implementation that is
// different than what the spec says.  This is equivalent in functionality
// and meets the spec, but doesn't clip in eye space.  This clipper clips
// in NTVP (clip) space.
//
// Trivial accept/reject has already been dealt with.
//

VOID FASTCALL __MCDClipLine(DEVRC *pRc, MCDVERTEX *a, MCDVERTEX *b,
                           BOOL bResetLine)
{
    MCDVERTEX *provokingA = a;
    MCDVERTEX *provokingB = b;
    MCDVERTEX np1, np2;
    MCDCOORD *plane;
    ULONG needs, allClipCodes, clipCodes;
    VOID (FASTCALL *clip)(MCDVERTEX*, const MCDVERTEX*, const MCDVERTEX*, MCDFLOAT);
    MCDFLOAT zero;
    MCDFLOAT winx, winy;
    MCDFLOAT vpXCenter, vpYCenter, vpZCenter;
    MCDFLOAT vpXScale, vpYScale, vpZScale;
    MCDVIEWPORT *vp;
    MCDFLOAT x, y, z, wInv;

    allClipCodes = a->clipCode | b->clipCode;

    /*
    ** For each clipping plane that something is out on, clip
    ** check the verticies.  Note that no bits will be set in
    ** allClipCodes for clip planes that are not enabled.
    */
    zero = __MCDZERO;
    clip = pRc->lineClipParam;

    /* 
    ** Do user clip planes first, because we will maintain eye coordinates
    ** only while doing user clip planes.  They are ignored for the
    ** frustum clipping planes.
    */
    clipCodes = (allClipCodes >> 6) & __MCD_USER_CLIP_MASK;
    if (clipCodes) {
        plane = &pRc->MCDState.userClipPlanes[0];
        do {
            /*
            ** See if this clip plane has anything out of it.  If not,
            ** press onward to check the next plane.  Note that we
            ** shift this mask to the right at the bottom of the loop.
            */
            if (clipCodes & 1) {
                MCDFLOAT t, d1, d2;

                d1 = (plane->x * a->eyeCoord.x) + 
                     (plane->y * a->eyeCoord.y) +
                     (plane->z * a->eyeCoord.z) + 
                     (plane->w * a->eyeCoord.w);
                d2 = (plane->x * b->eyeCoord.x) + 
                     (plane->y * b->eyeCoord.y) +
                     (plane->z * b->eyeCoord.z) + 
                     (plane->w * b->eyeCoord.w);
                if (d1 < zero) {
                    /* a is out */
                    if (d2 < zero) {
                        /* a & b are out */
                        return;
                    }

                    /*
                    ** A is out and B is in.  Compute new A coordinate
                    ** clipped to the plane.
                    */
                    t = d2 / (d2 - d1);
                    (*clip)(&np1, a, b, t);
                    (&np1)->eyeCoord.x = 
                        t*(a->eyeCoord.x - b->eyeCoord.x) + b->eyeCoord.x;
                    (&np1)->eyeCoord.y = 
                        t*(a->eyeCoord.y - b->eyeCoord.y) + b->eyeCoord.y;
                    (&np1)->eyeCoord.z = 
                        t*(a->eyeCoord.z - b->eyeCoord.z) + b->eyeCoord.z;
                    (&np1)->eyeCoord.w = 
                        t*(a->eyeCoord.w - b->eyeCoord.w) + b->eyeCoord.w;
                    a = &np1;
                    a->flags = b->flags;

                    if (pRc->MCDState.shadeModel == GL_FLAT)
                    {
                        COPY_COLOR(a->colors[0], provokingA->colors[0]);
                    }

                } else {
                    /* a is in */
                    if (d2 < zero) {
                        /*
                        ** A is in and B is out.  Compute new B
                        ** coordinate clipped to the plane.
                        **
                        ** NOTE: To avoid cracking in polygons with
                        ** shared clipped edges we always compute "t"
                        ** from the out vertex to the in vertex.  The
                        ** above clipping code gets this for free (b is
                        ** in and a is out).  In this code b is out and a
                        ** is in, so we reverse the t computation and the
                        ** argument order to (*clip).
                        */
                        t = d1 / (d1 - d2);
                        (*clip)(&np2, b, a, t);
                        (&np2)->eyeCoord.x =
                            t*(b->eyeCoord.x - a->eyeCoord.x) + a->eyeCoord.x;
                        (&np2)->eyeCoord.y =
                            t*(b->eyeCoord.y - a->eyeCoord.y) + a->eyeCoord.y;
                        (&np2)->eyeCoord.z =
                            t*(b->eyeCoord.z - a->eyeCoord.z) + a->eyeCoord.z;
                        (&np2)->eyeCoord.w =
                            t*(b->eyeCoord.w - a->eyeCoord.w) + a->eyeCoord.w;
                        b = &np2;
                        b->flags = a->flags;

                        if (pRc->MCDState.shadeModel == GL_FLAT)
                        {
                            COPY_COLOR(b->colors[0], provokingB->colors[0]);
                        }

                    } else {
                        /* A and B are in */
                    }
                }
            }
            plane++;
            clipCodes >>= 1;
        } while (clipCodes);
    }

    allClipCodes &= MCD_CLIP_MASK;
    if (allClipCodes) {
        plane = &__MCD_frustumClipPlanes[0];
        do {
            /*
            ** See if this clip plane has anything out of it.  If not,
            ** press onward to check the next plane.  Note that we
            ** shift this mask to the right at the bottom of the loop.
            */
            if (allClipCodes & 1) {
                MCDFLOAT t, d1, d2;

                d1 = (plane->x * a->clipCoord.x) + (plane->y * a->clipCoord.y) +
                     (plane->z * a->clipCoord.z) + (plane->w * a->clipCoord.w);
                d2 = (plane->x * b->clipCoord.x) + (plane->y * b->clipCoord.y) +
                     (plane->z * b->clipCoord.z) + (plane->w * b->clipCoord.w);
                if (d1 < zero) {
                    /* a is out */
                    if (d2 < zero) {
                        /* a & b are out */
                        return;
                    }

                    /*
                    ** A is out and B is in.  Compute new A coordinate
                    ** clipped to the plane.
                    */
                    t = d2 / (d2 - d1);
                    (*clip)(&np1, a, b, t);
                    a = &np1;
                    a->flags = b->flags;

                    if (pRc->MCDState.shadeModel == GL_FLAT)
                    {
                        COPY_COLOR(a->colors[0], provokingA->colors[0]);
                    }

                } else {
                    /* a is in */
                    if (d2 < zero) {
                        /*
                        ** A is in and B is out.  Compute new B
                        ** coordinate clipped to the plane.
                        **
                        ** NOTE: To avoid cracking in polygons with
                        ** shared clipped edges we always compute "t"
                        ** from the out vertex to the in vertex.  The
                        ** above clipping code gets this for free (b is
                        ** in and a is out).  In this code b is out and a
                        ** is in, so we reverse the t computation and the
                        ** argument order to (*clip).
                        */
                        t = d1 / (d1 - d2);
                        (*clip)(&np2, b, a, t);
                        b = &np2;
                        b->flags = a->flags;

                        if (pRc->MCDState.shadeModel == GL_FLAT)
                        {
                            COPY_COLOR(b->colors[0], provokingB->colors[0]);
                        }

                    } else {
                        /* A and B are in */
                    }
                }
            }
            plane++;
            allClipCodes >>= 1;
        } while (allClipCodes);
    }

    vp = &pRc->MCDViewport;
    vpXCenter = vp->xCenter;
    vpYCenter = vp->yCenter;
    vpZCenter = vp->zCenter;
    vpXScale = vp->xScale;
    vpYScale = vp->yScale;
    vpZScale = vp->zScale;

    /* Compute window coordinates for both vertices. */
    wInv = __MCDONE / a->clipCoord.w;
    x = a->clipCoord.x; 
    y = a->clipCoord.y; 
    z = a->clipCoord.z;
    winx = x * vpXScale * wInv + vpXCenter;
    winy = y * vpYScale * wInv + vpYCenter;
    a->windowCoord.z = z * vpZScale * wInv + vpZCenter;
    a->windowCoord.w = wInv;
    a->windowCoord.x = winx;
    a->windowCoord.y = winy;

    wInv = __MCDONE / b->clipCoord.w;
    x = b->clipCoord.x; 
    y = b->clipCoord.y; 
    z = b->clipCoord.z;
    winx = x * vpXScale * wInv + vpXCenter;
    winy = y * vpYScale * wInv + vpYCenter;
    b->windowCoord.z = z * vpZScale * wInv + vpZCenter;
    b->windowCoord.w = wInv;
    b->windowCoord.x = winx;
    b->windowCoord.y = winy;

    /* Validate line state */
    if (pRc->MCDState.shadeModel == GL_FLAT) {

        // Add the vertices then restore the b color pointer
        //
        // Note that although b is the only new vertex, up
        // to two vertices can be added because each new vertex
        // generated by clipping must be added.  For a line where
        // both endpoints are out of the clipping region, both
        // an entry and an exit vertex must be added
        if (provokingA->clipCode != 0)
        {
            // a was out so a new vertex was added at the point of
            // entry
            bResetLine = TRUE;
        }
        // b is always added since either:
        // b was in and is new so it needs to be added
        // b was out so a new vertex was added at the exit point

        (*pRc->renderLine)(pRc, a, b, bResetLine);
        
    } else {

        if (provokingA->clipCode != 0)
        {
            bResetLine = TRUE;
        }
        (*pRc->renderLine)(pRc, a, b, bResetLine);
    }
}