/*
 * $Id: draw.c,v 1.1.1.1 2000/04/07 19:44:51 hfrieden Exp $
 *
 * $Date: 2000/04/07 19:44:51 $
 * $Revision: 1.1.1.1 $
 *
 * (C) 1999 by Hyperion
 * All rights reserved
 *
 * This file is part of the MiniGL library project
 * See the file Licence.txt for more details
 *
 */

#include "sysinc.h"
#include <math.h>
#include <stdlib.h>

static char rcsid[] = "$Id: draw.c,v 1.1.1.1 2000/04/07 19:44:51 hfrieden Exp $";

#define DUMP_VERTEX(vert) \
 mykprintf("x:%6.3f y:%6.3f z:%6.3f w:%6.3f\nR:%6.3f G:%6.3f B:%6.3f A:%6.3f\nU:%6.3f V:%6.3f\noutcode=0x%x\n",\
         vert.bx, vert.by, vert.bz, vert.bw,                                                                                      \
         vert.v.color.r, vert.v.color.g, vert.v.color.b, vert.v.color.a,                                                          \
         vert.v.u, vert.v.v, vert.outcode)

#define DUMP_VERTEX2(vert) \
 mykprintf("X:%6.3f Y:%6.3f Z:%6.3f W:%6.3f\nR:%6.3f G:%6.3f B:%6.3f A:%6.3f\nU:%6.3f V:%6.3f\n",\
         vert.x, vert.y, vert.z, vert.w,                                                                                  \
         vert.color.r, vert.color.g, vert.color.b, vert.color.a,                                                          \
         vert.u, vert.v);                                                                                                         \


void d_DrawPoly(GLcontext context, MGLPolygon *poly);
void dh_DrawPolyFF(GLcontext context, MGLPolygon *poly);
void d_DrawPolyExt(GLcontext context, MGLPolygon *poly);
void d_DrawPoints        (GLcontext);
void d_DrawLines         (GLcontext);
void d_DrawLineStrip     (GLcontext);
void d_DrawTriangles     (GLcontext);
void d_DrawTriangleFan   (GLcontext);
void d_DrawTriangleStrip (GLcontext);
void d_DrawQuads         (GLcontext);
void d_DrawPolygon       (GLcontext);
void d_DrawFlatFan       (GLcontext);
void d_DrawQuadStrip     (GLcontext);

INLINE void v_ApplyMatrixElCheapo(GLcontext context, Matrix *pA, int vnum);
INLINE GLvoid v_Transform(GLcontext context);
INLINE void v_ToScreen(GLcontext context, int i);
void m_CombineMatrices(GLcontext context);

extern void m_BuildInverted(GLcontext context);
extern void hc_ClipAndDrawPolyFF(GLcontext context, MGLPolygon *poly, GLubyte or_codes);


INLINE void v_ApplyMatrixElCheapo(GLcontext context, Matrix *pA, int vnum)
{
    // El Cheapo implementation ATM
    #define a(x) (pA->v[OF_##x])
    float x=context->VertexBuffer[vnum].bx;
    float y=context->VertexBuffer[vnum].by;
    float z=context->VertexBuffer[vnum].bz;
    float w=context->VertexBuffer[vnum].bw;

    if (pA->flags == MGLMAT_IDENTITY) return;
    context->VertexBuffer[vnum].bx = a(11)*x + a(12)*y + a(13)*z + a(14)*w;
    context->VertexBuffer[vnum].by = a(21)*x + a(22)*y + a(23)*z + a(24)*w;
    context->VertexBuffer[vnum].bz = a(31)*x + a(32)*y + a(33)*z + a(34)*w;
    context->VertexBuffer[vnum].bw = a(41)*x + a(42)*y + a(43)*z + a(44)*w;

    return;
    #undef a
}


INLINE void v_ApplyMatrixWOne(GLcontext context, Matrix *pA, int vnum)
{
    // W assumed to be 1.0
    #define a(x) (pA->v[OF_##x])
    float x=context->VertexBuffer[vnum].bx;
    float y=context->VertexBuffer[vnum].by;
    float z=context->VertexBuffer[vnum].bz;

    context->VertexBuffer[vnum].bx = a(11)*x + a(12)*y + a(13)*z + a(14);
    context->VertexBuffer[vnum].by = a(21)*x + a(22)*y + a(23)*z + a(24);
    context->VertexBuffer[vnum].bz = a(31)*x + a(32)*y + a(33)*z + a(34);
    context->VertexBuffer[vnum].bw = a(41)*x + a(42)*y + a(43)*z + a(44);

    return;
    #undef a
}


/*
** This computationally intensive piece of code generates coordinates
** for the reflective image used in spherical environment mapping.
** This is a good place for optimizations.
** After generation, transforms into the clipping space...
**
** Note that this is after the book. It is probably a big time-eater.
**
** (Turned out not to be so time intensive after all...)
*/
void v_GenTexCoords(GLcontext context, int vertex)
{
    float u[4];
    float ul;
    float nx,ny,nz;
    float nu;
    float rx,ry,rz;
    float m;
    float s,t;

    if (context->InvRotValid == GL_FALSE) m_BuildInverted(context);

    #define a(x)    (CurrentMV->v[OF_##x])
    #define b(x)    (CurrentP->v[OF_##x])
    #define verx  (context->VertexBuffer[vertex].bx)
    #define very  (context->VertexBuffer[vertex].by)
    #define verz  (context->VertexBuffer[vertex].bz)
    #define verw  (context->VertexBuffer[vertex].bw)

    u[0] = a(11)*verx + a(12)*very + a(13)*verz + a(14)*verw;
    u[1] = a(21)*verx + a(22)*very + a(23)*verz + a(24)*verw;
    u[2] = a(31)*verx + a(32)*very + a(33)*verz + a(34)*verw;
    u[3] = a(41)*verx + a(42)*very + a(43)*verz + a(44)*verw;

    verx = b(11)*u[0] + b(12)*u[1] + b(13)*u[2] + b(14)*u[3];
    very = b(21)*u[0] + b(22)*u[1] + b(23)*u[2] + b(24)*u[3];
    verz = b(31)*u[0] + b(32)*u[1] + b(33)*u[2] + b(34)*u[3];
    verw = b(41)*u[0] + b(42)*u[1] + b(43)*u[2] + b(44)*u[3];

    #undef a
    #undef b
    #undef verx
    #undef very
    #undef verz
    #undef verw

    #define ver(c)  (context->VertexBuffer[vertex].Normal.c)
    #define a(x)    (context->InvRot[x])

    // InvRot is row-major
    nx = ver(x)*a(0) + ver(y)*a(3) + ver(z)*a(6);
    ny = ver(x)*a(1) + ver(y)*a(4) + ver(z)*a(7);
    nz = ver(x)*a(2) + ver(y)*a(5) + ver(z)*a(8);

    #undef ver
    #undef a
      

    if (u[3] != 1.f)
    {
    u[0] /= u[3]; u[1] /= u[3]; u[2] /= u[3];
    }

    // nomalize unit vector
    ul = (float)sqrt((double)(u[0]*u[0] + u[1]*u[1] + u[2]*u[2]));
    if (ul == 0.f) return;
    u[0] /= ul; u[1] /= ul; u[2] /= ul;


    nu = nx*u[0] + ny*u[1]+nz*u[2]; nu *= 2.f;

    rx = u[0] - nx*nu;
    ry = u[1] - ny*nu;
    rz = u[2] - nz*nu;


    m = 0.5f / (float)sqrt((double)(rx*rx + ry*ry + (rz+1.f)*(rz+1.f)));

    s = rx*m + 0.5;
    t = ry*m + 0.5;

    if (context->TextureGenS_State == GL_TRUE)
    {
    context->VertexBuffer[vertex].v.u = context->w3dTexBuffer[context->CurrentBinding]->texwidth * s;
    }
    if (context->TextureGenT_State == GL_TRUE)
    {
    context->VertexBuffer[vertex].v.v = context->w3dTexBuffer[context->CurrentBinding]->texheight * t;
    }
}


INLINE void v_Transform(GLcontext context)
{
    int i;

    if (context->TextureGenS_State == GL_TRUE || context->TextureGenT_State == GL_TRUE)
    {
    for (i=0; i<context->VertexBufferPointer; i++)
    {
        v_GenTexCoords(context, i);
    }
    }
    else
    {
    if (context->CombinedValid == GL_FALSE)
    {
        m_CombineMatrices(context);
    }
    if (context->WOne_Hint == GL_FALSE)
    {
        for (i=0; i<context->VertexBufferPointer; i++)
        {
        v_ApplyMatrixElCheapo(context, &(context->CombinedMatrix), i);
        }
    }
    else
    {
        for (i=0; i<context->VertexBufferPointer; i++)
        {
        v_ApplyMatrixWOne(context, &(context->CombinedMatrix), i);
        }
    }

    }
}

INLINE void v_ToScreen(GLcontext context, int i)
{
    float w = 1.0 / context->VertexBuffer[i].bw;

    context->VertexBuffer[i].v.x = context->ax + context->VertexBuffer[i].bx * w * context->sx;
    context->VertexBuffer[i].v.y = context->ay - context->VertexBuffer[i].by * w * context->sy;
    context->VertexBuffer[i].v.z = context->az + context->VertexBuffer[i].bz * w * context->sz;
    if (context->ZOffset_State == GL_TRUE)
        context->VertexBuffer[i].v.z += (W3D_Float)context->ZOffset;
    context->VertexBuffer[i].v.w = w;
}

void d_DrawPoints(GLcontext context)
{
}

void d_DrawLines(GLcontext context)
{
}

void d_DrawLineStrip(GLcontext context)
{
}

#if 0
void d_DumpPolygon(GLcontext context, MGLPolygon *poly)
{
    int i;
    mykprintf("d_DumpPolygon: %d vertices\n", poly->numverts);
    for (i=0; i<poly->numverts; i++)
    {
        DUMP_VERTEX(context->VertexBuffer[poly->verts[i]]);
    }
    mykprintf("-- \n");
}
#endif

void d_DrawTriangleFan(GLcontext context)
{
    int i;
    GLubyte and_code, local_or, local_and;
    ULONG error;
    static W3D_Vertex **verts = NULL;
    static GLboolean visible[MGL_MAXVERTS];     // Should be enough...?
    static GLubyte   complete[MGL_MAXVERTS];    // Ditto
    static W3D_TrianglesV tris;
    int triangle = 0;

    if (verts == NULL)
    {
        verts = (W3D_Vertex **)malloc(sizeof(W3D_Vertex *) * context->VertexBufferSize);
        if (!verts) return;
    }

    v_Transform(context); // Transform world to clipping coordinates

    and_code = 0xFF;
    for (i=0; i<context->VertexBufferPointer; i++)
    {
        hc_CodePoint(&(context->VertexBuffer[i]));
        and_code &= context->VertexBuffer[i].outcode;
    }

    if (and_code) return;

    /*
    ** Up to now everything is like a polygon. Unlike a polygon, however,
    ** triangle fans and strips may be concave, and each individual triangle
    ** might be backfacing.
    **
    ** We calculate how many of the triangles are visible.
    */
    for (i=1,triangle=0; i<context->VertexBufferPointer - 1; i++, triangle++)
    {
        local_or = context->VertexBuffer[0].outcode
            | context->VertexBuffer[i].outcode
            | context->VertexBuffer[i+1].outcode;

        local_and = context->VertexBuffer[0].outcode
            & context->VertexBuffer[i].outcode
            & context->VertexBuffer[i+1].outcode;

        if (local_and == 0) // if the local and code is zero, we're not
        {                   // completely off screen.
            visible[triangle] = hc_DecideFrontface(context,
                &(context->VertexBuffer[0]),
                &(context->VertexBuffer[i]),
                &(context->VertexBuffer[i+1]), local_or);
            // if our local or codes are zero, we are completely
            // visible
            complete[triangle] = local_or;
        }
        else
        {
            visible[triangle] = GL_FALSE;
        }
    }

    /*
    ** Draw...
    ** There are three cases:
    **
    ** 1) Triangle is partially visible
    ** 2) Triangle is fully visible
    ** 3) Triangle is invisible
    **
    ** In case 1, we draw the triangle with clip_ClipAndDrawPoly.
    ** In case 2, we collect a triangle fan/strip of maximum length
    ** and draw that directly
    ** in case 3... WE IGNORE IT! HA!
    */
    triangle = 0; i=1;

    do
    {
        if (visible[triangle] == GL_FALSE) // case 3
        {
            triangle ++;
            i        ++;
        }
        else
        {
            if (complete[triangle]) // case 1
            {
                static MGLPolygon poly;
                poly.numverts = 3;
                poly.verts[0] = 0;
                poly.verts[1] = i;
                poly.verts[2] = i + 1;
                // HERE
                hc_ClipAndDrawPoly(context, &poly, complete[triangle] );
                triangle++;
                i++;
            }
            else
            {   // case 2 (the difficult part)
                int k=3;
                tris.vertexcount = 3;
                verts[0] = &(context->VertexBuffer[0].v);
                verts[1] = &(context->VertexBuffer[i].v);
                verts[2] = &(context->VertexBuffer[i+1].v);
                v_ToScreen(context, 0);
                v_ToScreen(context, i);
                v_ToScreen(context, i+1);

                triangle ++;
                i        ++;

                while (complete[triangle]==0 && visible[triangle] && i<context->VertexBufferPointer - 1)
                {
                    verts[k] = &(context->VertexBuffer[i+1].v);
                    v_ToScreen(context, i+1);
                    i++; k++; triangle++;
                    tris.vertexcount ++;
                }
                tris.tex = context->w3dTexBuffer[context->CurrentBinding];
                tris.st_pattern = NULL;
                tris.v = verts;
                error = W3D_DrawTriFanV(context->w3dContext, &tris);
                if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);

            }
        }
    } while (i<context->VertexBufferPointer-1);

}

void d_DrawTriangleStrip(GLcontext context)
{
    int i;
    GLubyte and_code, local_or, local_and;
    ULONG error;
    static W3D_Vertex **verts = NULL;
    static GLboolean visible[MGL_MAXVERTS];     // Should be enough...?
    static GLubyte   complete[MGL_MAXVERTS];    // Ditto
    static W3D_TrianglesV tris;
    int triangle = 0;
    GLenum CurrentFrontFace = context->CurrentFrontFace;



    if (verts == NULL)
    {
        verts = (W3D_Vertex **)malloc(sizeof(W3D_Vertex *) * context->VertexBufferSize);
        if (!verts) return;
    }

    v_Transform(context);

    and_code = 0xFF;
    for (i=0; i<context->VertexBufferPointer; i++)
    {
        hc_CodePoint(&(context->VertexBuffer[i]));
        and_code &= context->VertexBuffer[i].outcode;
    }

    if (and_code) return;

    /*
    ** Up to now everything is like a polygon. Unlike a polygon, however,
    ** triangle fans and strips may be concave, and each individual triangle
    ** might be backfacing.
    **
    ** We calculate how many of the triangles are visible.
    */
    for (i=0,triangle=0; i<context->VertexBufferPointer - 2; i++, triangle++)
    {
        local_or = context->VertexBuffer[i+0].outcode
                 | context->VertexBuffer[i+1].outcode
                 | context->VertexBuffer[i+2].outcode;

        local_and = context->VertexBuffer[i+0].outcode
                  & context->VertexBuffer[i+1].outcode
                  & context->VertexBuffer[i+2].outcode;

        if (local_and == 0) // if the local and code is zero, we're not
        {                   // completely off screen.
            visible[triangle] = hc_DecideFrontface(context,
                &(context->VertexBuffer[i+0]),
                &(context->VertexBuffer[i+1]),
                &(context->VertexBuffer[i+2]), local_or);
            // if our local or codes are zero, we are completely
            // visible
            complete[triangle] = local_or;
        }
        else
        {
            visible[triangle] = GL_FALSE;
        }

        if (context->CurrentFrontFace == GL_CCW) context->CurrentFrontFace = GL_CW;
        else                                     context->CurrentFrontFace = GL_CCW;

    }
    /*
    ** Draw...
    ** There are three cases:
    **
    ** 1) Triangle is partially visible
    ** 2) Triangle is fully visible
    ** 3) Triangle is invisible
    **
    ** In case 1, we draw the triangle with clip_ClipAndDrawPoly.
    ** In case 2, we collect a triangle fan/strip of maximum length
    ** and draw that directly
    ** in case 3... WE IGNORE IT! HA!
    */
    triangle = 0; i=0;

    context->CurrentFrontFace = CurrentFrontFace;

    do
    {

        if (visible[triangle] == GL_FALSE) // case 3
        {
            triangle ++;
            i        ++;
        }
        else
        {
            if (complete[triangle]) // case 1
            {
                static MGLPolygon poly;

                poly.numverts = 3;
                poly.verts[0] = i + 0;
                poly.verts[1] = i + 1;
                poly.verts[2] = i + 2;

                hc_ClipAndDrawPoly(context, &poly, complete[triangle] );
                triangle++;
                i++;
            }
            else
            {   // case 2 (the difficult part)
                int k=3;
                tris.vertexcount = 3;
                verts[0] = &(context->VertexBuffer[i+0].v);
                verts[1] = &(context->VertexBuffer[i+1].v);
                verts[2] = &(context->VertexBuffer[i+2].v);
                v_ToScreen(context, i);
                v_ToScreen(context, i+1);
                v_ToScreen(context, i+2);
                triangle ++;
                i        ++;

                while (complete[triangle]==0 && visible[triangle] && i<context->VertexBufferPointer - 2)
                {
                    verts[k] = &(context->VertexBuffer[i+2].v);
                    v_ToScreen(context, i+2);
                    i++; k++;
                    triangle++;
                    tris.vertexcount ++;
                }

                tris.tex = context->w3dTexBuffer[context->CurrentBinding];
                tris.st_pattern = NULL;
                tris.v = verts;
                error = W3D_DrawTriStripV(context->w3dContext, &tris);
                if (error != W3D_SUCCESS) kprintf("W3D_DrawTriStrip = %ld\n", error);

            }
        }
        if (context->CurrentFrontFace == GL_CCW) context->CurrentFrontFace = GL_CW;
        else                                     context->CurrentFrontFace = GL_CCW;
    } while (i<context->VertexBufferPointer - 2);

    context->CurrentFrontFace = CurrentFrontFace;

}

void d_DrawQuadStrip(GLcontext context)
{
    int i;
    GLubyte or_code, and_code;
    ULONG error;
    static W3D_Vertex *verts[4];
    static W3D_TrianglesV tris;

    v_Transform(context);

    for (i=0; i<context->VertexBufferPointer-2; i+=2)
    {
        hc_CodePoint(&(context->VertexBuffer[i]));
        hc_CodePoint(&(context->VertexBuffer[i+1]));
        hc_CodePoint(&(context->VertexBuffer[i+2]));
        hc_CodePoint(&(context->VertexBuffer[i+3]));

        or_code = context->VertexBuffer[i+0].outcode
                | context->VertexBuffer[i+1].outcode
                | context->VertexBuffer[i+2].outcode
                | context->VertexBuffer[i+3].outcode;

        and_code = context->VertexBuffer[i+0].outcode
                 & context->VertexBuffer[i+1].outcode
                 & context->VertexBuffer[i+2].outcode
                 & context->VertexBuffer[i+3].outcode;

        if (and_code) continue;

        if (hc_DecideFrontface(context, &(context->VertexBuffer[i]),
            &(context->VertexBuffer[i+1]),
            &(context->VertexBuffer[i+2]), or_code) == GL_FALSE) continue;

        if (or_code == 0)
        {
            v_ToScreen(context, i);
            v_ToScreen(context, i+1);
            v_ToScreen(context, i+2);
            v_ToScreen(context, i+3);

            verts[0] = &(context->VertexBuffer[i+0].v);
            verts[1] = &(context->VertexBuffer[i+1].v);
            verts[2] = &(context->VertexBuffer[i+3].v);
            verts[3] = &(context->VertexBuffer[i+2].v);
            tris.tex = context->w3dTexBuffer[context->CurrentBinding];
            tris.st_pattern = NULL;
            tris.vertexcount = 4;
            tris.v = verts;
            if (context->ShadeModel == GL_FLAT)
            {
                W3D_SetCurrentColor(context->w3dContext, &(context->VertexBuffer[i].v.color));
            }

            error = W3D_DrawTriFanV(context->w3dContext, &tris);
            if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
        }
        else
        {
            static MGLPolygon poly;
            poly.numverts = 4;
            poly.verts[0] = i;
            poly.verts[1] = i+1;
            poly.verts[2] = i+3;
            poly.verts[3] = i+2;
            if (context->ShadeModel == GL_FLAT)
            {
                W3D_SetCurrentColor(context->w3dContext, &(context->VertexBuffer[i].v.color));
            }
            hc_ClipAndDrawPoly(context, &poly, or_code);
        }
    }
}


void d_DrawQuads(GLcontext context)
{
    int i;
    GLubyte or_code, and_code;
    ULONG error;
    static W3D_Vertex *verts[4];
    static W3D_TrianglesV tris;


    v_Transform(context);

    for (i=0; i<context->VertexBufferPointer; i+=4)
    {
        hc_CodePoint(&(context->VertexBuffer[i]));
        hc_CodePoint(&(context->VertexBuffer[i+1]));
        hc_CodePoint(&(context->VertexBuffer[i+2]));
        hc_CodePoint(&(context->VertexBuffer[i+3]));

        or_code = context->VertexBuffer[i+0].outcode
                | context->VertexBuffer[i+1].outcode
                | context->VertexBuffer[i+2].outcode
                | context->VertexBuffer[i+3].outcode;

        and_code = context->VertexBuffer[i+0].outcode
                 & context->VertexBuffer[i+1].outcode
                 & context->VertexBuffer[i+2].outcode
                 & context->VertexBuffer[i+3].outcode;

        if (and_code) continue;

        if (hc_DecideFrontface(context, &(context->VertexBuffer[i]),
            &(context->VertexBuffer[i+1]),
            &(context->VertexBuffer[i+2]),or_code) == GL_FALSE) continue;

        if (context->ShadeModel == GL_FLAT)
        {
            W3D_SetCurrentColor(context->w3dContext, &(context->VertexBuffer[i].v.color));
        }

        if (or_code == 0)
        {
            v_ToScreen(context, i);
            v_ToScreen(context, i+1);
            v_ToScreen(context, i+2);
            v_ToScreen(context, i+3);

            verts[0] = &(context->VertexBuffer[i+0].v);
            verts[1] = &(context->VertexBuffer[i+1].v);
            verts[2] = &(context->VertexBuffer[i+2].v);
            verts[3] = &(context->VertexBuffer[i+3].v);
            tris.tex = context->w3dTexBuffer[context->CurrentBinding];
            tris.st_pattern = NULL;
            tris.vertexcount = 4;
            tris.v = verts;
            #ifndef NODRAW
            error = W3D_DrawTriFanV(context->w3dContext, &tris);
            if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
            #else
            printf("d_DrawQuads\n");
            DUMP_VERTEX(context->VertexBuffer[i+0]);
            DUMP_VERTEX(context->VertexBuffer[i+1]);
            DUMP_VERTEX(context->VertexBuffer[i+2]);
            DUMP_VERTEX(context->VertexBuffer[i+3]);
            printf("-----------------------------------\n");
            #endif
            #if 1
            if (error != W3D_SUCCESS) kprintf("[MiniGL::d_DrawQuads] Duh! Error %ld\n", error);
            #endif
        }
        else
        {
            static MGLPolygon poly;
            poly.numverts = 4;
            poly.verts[0] = i;
            poly.verts[1] = i+1;
            poly.verts[2] = i+2;
            poly.verts[3] = i+3;
            hc_ClipAndDrawPoly(context, &poly, or_code);
        }
    }
}

void d_DrawPolygon(GLcontext context)
{
    int i;
    GLubyte or_code, and_code;
    MGLPolygon poly;

    v_Transform(context);
    or_code = 0; and_code = 0xFF;

    for (i=0; i<context->VertexBufferPointer; i++)
    {
        hc_CodePoint(&(context->VertexBuffer[i]));
        or_code  |= context->VertexBuffer[i].outcode;
        and_code &= context->VertexBuffer[i].outcode;
        poly.verts[i] = i;
    }


    if (and_code) return;
    #if 0
    if (hc_DecideFrontface(context, &(context->VertexBuffer[0]),
        &(context->VertexBuffer[1]),
        &(context->VertexBuffer[2]),or_code) == GL_FALSE) return;
    #endif

    poly.numverts = context->VertexBufferPointer;

    if (or_code == 0)
    {
        dh_DrawPolyFF(context, &poly);
    }
    else
    {
        hc_ClipAndDrawPolyFF(context, &poly, or_code);
    }
}

void d_DrawTriangles(GLcontext context)
{
    int i;
    W3D_TriangleV tri;
    GLubyte or_code, and_code;
    ULONG error;

    v_Transform(context);

    for (i=0; i<context->VertexBufferPointer; i+=3)
    {
        hc_CodePoint(&(context->VertexBuffer[i]));
        hc_CodePoint(&(context->VertexBuffer[i+1]));
        hc_CodePoint(&(context->VertexBuffer[i+2]));

        or_code = context->VertexBuffer[i+0].outcode
                | context->VertexBuffer[i+1].outcode
                | context->VertexBuffer[i+2].outcode;

        and_code = context->VertexBuffer[i+0].outcode
                 & context->VertexBuffer[i+1].outcode
                 & context->VertexBuffer[i+2].outcode;
        if (and_code) continue;

        if (hc_DecideFrontface(context, &(context->VertexBuffer[i]),
            &(context->VertexBuffer[i+1]),
            &(context->VertexBuffer[i+2]),or_code) == GL_FALSE) continue;


        if (or_code == 0)
        {
            v_ToScreen(context, i);
            v_ToScreen(context, i+1);
            v_ToScreen(context, i+2);

            tri.v1 = &(context->VertexBuffer[i+0].v);
            tri.v2 = &(context->VertexBuffer[i+1].v);
            tri.v3 = &(context->VertexBuffer[i+2].v);
            tri.tex = context->w3dTexBuffer[context->CurrentBinding];
            tri.st_pattern = NULL;
            error = W3D_DrawTriangleV(context->w3dContext, &tri);
            if (error != W3D_SUCCESS) kprintf("W3D_DrawTriangle = %ld\n", error);
        }
        else
        {
            static MGLPolygon poly;
            poly.numverts = 3;
            poly.verts[0] = i;
            poly.verts[1] = i+1;
            poly.verts[2] = i+2;
            hc_ClipAndDrawPoly(context, &poly, or_code);
        }
    }
}

/*
** A FlatFan is a triangle fan that is specified in device corrdinates and
** is drawn regardless of current matrix or other
*/
void d_DrawFlatFan(GLcontext context)
{
    ULONG error;
    int i;
    static W3D_Vertex *verts[MGL_MAXVERTS];
    static W3D_TrianglesV tris;

    for (i=0; i<context->VertexBufferPointer; i++)
    {
        verts[i] = &(context->VertexBuffer[i].v);
        context->VertexBuffer[i].v.x = (W3D_Float) context->VertexBuffer[i].bx;
        context->VertexBuffer[i].v.y = (W3D_Float) context->VertexBuffer[i].by;
        context->VertexBuffer[i].v.z = (W3D_Double)context->VertexBuffer[i].bz;
        context->VertexBuffer[i].v.w = (W3D_Float) context->VertexBuffer[i].bw;
    }
    tris.tex = context->w3dTexBuffer[context->CurrentBinding];
    tris.st_pattern = NULL;
    tris.v = verts;
    tris.vertexcount = context->VertexBufferPointer;
    #ifndef NODRAW
    error = W3D_DrawTriFanV(context->w3dContext, &tris);
    if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFanV = %ld\n", error);
    #endif
}

void dh_DrawPoly(GLcontext context, MGLPolygon *poly)
{
    int i;
    ULONG error;
    static W3D_Vertex *verts[MGL_MAXVERTS];
    static W3D_TrianglesV tris;

    for (i=0; i<poly->numverts; i++)
    {
        v_ToScreen(context, poly->verts[i]);
        verts[i] = &(context->VertexBuffer[poly->verts[i]].v);
    }

    tris.tex = context->w3dTexBuffer[context->CurrentBinding];
    tris.st_pattern = NULL;
    tris.vertexcount = poly->numverts;
    tris.v = verts;
    #ifndef NODRAW
    error = W3D_DrawTriFanV(context->w3dContext, &tris);
    if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
    #endif
}

inline GLfloat dh_AreaSign(GLcontext context, MGLPolygon *poly)
{
    int i,j;
    GLfloat area=0.0;
    #define x(i) (context->VertexBuffer[poly->verts[i]].v.x)
    #define y(i) (context->VertexBuffer[poly->verts[i]].v.y)

    for (i=0; i<poly->numverts; i++)
    {
        j=(i+1)%(poly->numverts);
        area += (x(i)*y(j) - x(j)*y(i));
    }

    #undef x
    #undef y

    return area;
}

void dh_DrawPolyFF(GLcontext context, MGLPolygon *poly)
{
    int i;
    ULONG error;
    static W3D_Vertex *verts[MGL_MAXVERTS];
    static W3D_TrianglesV tris;
    GLfloat area;

    for (i=0; i<poly->numverts; i++)
    {
        v_ToScreen(context, poly->verts[i]);
        verts[i] = &(context->VertexBuffer[poly->verts[i]].v);
    }

    if (context->CullFace_State == GL_TRUE)
    {
        area = dh_AreaSign(context, poly);

        if (context->CurrentFrontFace == GL_CW) area *= -1.f;
        if (area < 0.0) return;
    }

    tris.tex = context->w3dTexBuffer[context->CurrentBinding];
    tris.st_pattern = NULL;
    tris.vertexcount = poly->numverts;
    tris.v = verts;
    #ifndef NODRAW
    error = W3D_DrawTriFanV(context->w3dContext, &tris);
    if (error != W3D_SUCCESS) kprintf("W3D_DrawTriFan = %ld\n", error);
    #endif
}

