/*
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    Macro and function that manage with Glide2 vertex
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 */

#define __MSC__

#include <GLIDE.H>
#include "TextuDFX.h"
#include "PRF.h"
#include "GliBench.h"

/* #include "drawflags.h" */

/* Globals */
#define GLI_GLIDE1_CurrentTmu   0
GrChipID_t  GLI_GLIDE1_xTmuNumber = 0;

void        GLI_DRV_xSendSpriteToClip_TRIANGLES
            (
                GLI_tdstAligned2DVector                 *a4_st2DVertex,
                GLI_tdstInternalGlobalValuesFor3dEngine *p_stGlobaleMT
            );

#define GLI_tdScreenVertx   GrVertex
GrVertex CurrentDestXYZ[4];

#define GLI_C_3dfxBugConstant   65536.0f * 8.0f


/* Vertex Access Macros */

/*
 ===================================================================================================
    GET
 ===================================================================================================
 */
#define GetX(a)             a.x
#define GetY(a)             a.y
#define GetZ(a)             a.oow
#define GetPackedColor(a)   * (unsigned long *) &a.r
#define GetPackedSpecular(a, b)
#define GetU(a) a.tmuvtx[GLI_GLIDE1_xTmuNumber].sow
#define GetV(a) a.tmuvtx[GLI_GLIDE1_xTmuNumber].tow

/*
 ===================================================================================================
    SET
 ===================================================================================================
 */
#define SetX(a, b)              a.x = b
#define SetY(a, b)              a.y = b
#define SetZ(a, b)              a.oow = b
#define SetPackedColor(a, b)    * (unsigned long *) &a.r = b

/*$F #define SetPackedSpecular(a,b) *(unsigned long *)&a.g = b */
#define SetPackedSpecular(a, b)
#define SetU(a, b)  a.tmuvtx[GLI_GLIDE1_xTmuNumber].sow = b
#define SetV(a, b)  a.tmuvtx[GLI_GLIDE1_xTmuNumber].tow = b
#define SetU_Cpy(a, b) (*(unsigned long *) &a.tmuvtx[GLI_GLIDE1_xTmuNumber].sow = *(unsigned long *) &b)
#define SetV_Cpy(a, b) (*(unsigned long *) &a.tmuvtx[GLI_GLIDE1_xTmuNumber].tow = *(unsigned long *) &b)

/* Constante à ne définir que pour débugger ou pour anti_bug temporaire */
/* #define GLI_DEBUG_TESTALLVERTICES */

#ifdef GLI_BENCH
void BENCH_M_Triangle( GrVertex *a, GrVertex *b, GrVertex *c )
{
    /*
     * compute triangle surface
     */
    float   xb, xc, yb, yc;
    long    surface;
    xb = GetX( (*b) ) - GetX( (*a) );
    yb = GetY( (*b) ) - GetY( (*a) );
    xc = GetX( (*c) ) - GetX( (*a) );
    yc = GetY( (*c) ) - GetY( (*a) );

    surface = (long) ceil((fabs( (xb * yc) - (xc * yb) ) / 2));
    
    if (surface> 1023)
        surface= 1023;
        
    GLI_MDRV_g_p_stBench->lTrianglePerSurface[ surface ]++;
}
#else
#define BENCH_M_Triangle(a,b,c)
#endif

    
#if defined(USE_PROFILER) && !defined(PRESS_DEMO)

    #define M_grDrawTriangle(a, b, c) \
        { \
            BENCH_M_Triangle( (a), (b), (c) );\
            grDrawTriangle((a), (b), (c)); \
            PRF_MDRV_vSetIndependantVariable(PRF_C_ulIdpDisplayedTriangle, 1); \
        }
#else

    #define M_grDrawTriangle    grDrawTriangle
    
#endif

    

#define GLI_M_RestoreCW()

/*
 ===================================================================================================
    function called before displayind severall triangle with same render states
 ===================================================================================================
 */
void GLI_BeforeDraw(void)
{
};

/*
 ===================================================================================================
    function called after displaying severall triangle with same render states
 ===================================================================================================
 */
void GLI_AfterDraw(void)
{
}

#define GLI_M_Correct3DFXBug2(a) \
    { \
        a.x -= GLI_C_3dfxBugConstant; \
        a.y -= GLI_C_3dfxBugConstant; \
    }

#define GLI_M_Correct3DFXBug1(a) \
    { \
        a.x += GLI_C_3dfxBugConstant; \
        a.y += GLI_C_3dfxBugConstant; \
    }

/*
 ===================================================================================================
 Correct Glide 2 bug
 ===================================================================================================
 */
void GLI_fn_vCorrect3DFXBug1(GrVertex *a)
{
    a->x += GLI_C_3dfxBugConstant;
    a->y += GLI_C_3dfxBugConstant;
}

#define GLI_M_Restore3DFXColor(P) \
    { \
        P.g = (float) ((unsigned char *) &(P.r))[1]; \
        P.b = (float) ((unsigned char *) &(P.r))[0]; \
        P.a = (float) ((unsigned char *) &(P.r))[3]; \
        P.r = (float) ((unsigned char *) &(P.r))[2]; \
        P.tmuvtx[GLI_GLIDE1_xTmuNumber].sow *= P.oow * GLI_BIG_GLOBALS->fMulV; \
        P.tmuvtx[GLI_GLIDE1_xTmuNumber].tow *= P.oow * GLI_BIG_GLOBALS->fMulU; \
    }

/*
 ===================================================================================================
    Draw normal triangle
 ===================================================================================================
 */
GLI_FuncNodeBegin(GLI_vDrawTriangle)
{
    /* 3DFX BUG */
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[0]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[1]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[2]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[0]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[1]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[2]);

#ifdef GLI_DEBUG_TESTALLVERTICES
    if
    (
        (CurrentDestXYZ[0].y < 0.0f) ||
        (CurrentDestXYZ[1].y < 0.0f) ||
        (CurrentDestXYZ[2].y < 0.0f) ||
        (CurrentDestXYZ[0].y > 480.0f) ||
        (CurrentDestXYZ[1].y > 480.0f) ||
        (CurrentDestXYZ[2].y > 480.0f) ||
        (CurrentDestXYZ[0].x < 0.0f) ||
        (CurrentDestXYZ[1].x < 0.0f) ||
        (CurrentDestXYZ[2].x < 0.0f) ||
        (CurrentDestXYZ[0].x > 640.0f) ||
        (CurrentDestXYZ[1].x > 640.0f) ||
        (CurrentDestXYZ[2].x > 640.0f)
    ) return;
#endif
    M_grDrawTriangle(&CurrentDestXYZ[0], &CurrentDestXYZ[1], &CurrentDestXYZ[2]);
}

/*
 ===================================================================================================
    draw wired triangle
 ===================================================================================================
 */
GLI_FuncNodeBegin(GLI_vDrawWiredTriangle)
{
    /* 3DFX BUG */
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[0]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[1]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[2]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[0]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[1]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[2]);

#ifdef GLI_DEBUG_TESTALLVERTICES
    if
    (
        (CurrentDestXYZ[0].y < 0.0f) ||
        (CurrentDestXYZ[1].y < 0.0f) ||
        (CurrentDestXYZ[2].y < 0.0f) ||
        (CurrentDestXYZ[0].y > 480.0f) ||
        (CurrentDestXYZ[1].y > 480.0f) ||
        (CurrentDestXYZ[2].y > 480.0f) ||
        (CurrentDestXYZ[0].x < 0.0f) ||
        (CurrentDestXYZ[1].x < 0.0f) ||
        (CurrentDestXYZ[2].x < 0.0f) ||
        (CurrentDestXYZ[0].x > 640.0f) ||
        (CurrentDestXYZ[1].x > 640.0f) ||
        (CurrentDestXYZ[2].x > 640.0f)
    ) return;
#endif
    grDrawLine(&CurrentDestXYZ[0], &CurrentDestXYZ[1]);
    grDrawLine(&CurrentDestXYZ[1], &CurrentDestXYZ[2]);
    grDrawLine(&CurrentDestXYZ[0], &CurrentDestXYZ[2]);
}

/*
 ===================================================================================================
    correct Glide2 bug
 ===================================================================================================
 */
GLI_FuncNodeBegin(GLI_vCorrect3DFXBug1)
{
    /* 3DFX BUG */
    GLI_M_Correct3DFXBug1(CurrentDestXYZ[0]);
    GLI_M_Correct3DFXBug1(CurrentDestXYZ[1]);
    GLI_M_Correct3DFXBug1(CurrentDestXYZ[2]);
    GLI_M_CallNextFunc();
}

/*
 ===================================================================================================
    sort translucent triangle
 ===================================================================================================
 */
GLI_FuncNodeBegin(GLI_vZSortTriangle)
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    float   ZMax;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    if
    (
        (unsigned long) GLI_BIG_GLOBALS->p_TheZListe->p_CurrentPoint >
        (unsigned long) GLI_BIG_GLOBALS->p_TheZListe->p_TableOfPoints + ((3L << GLI_MaxZListeTrianglesPO2) * sizeof(GrVertex)) - 3L * sizeof(GrVertex)
    ) 
        return;

    /* 3DFX BUG */
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[0]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[1]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[2]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[0]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[1]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[2]);

    memcpy(GLI_BIG_GLOBALS->p_TheZListe->p_CurrentPoint, CurrentDestXYZ, sizeof(GrVertex) * 3L);
    
    if(*(unsigned long *) &GetZ(CurrentDestXYZ[0]) < *(unsigned long *) &GetZ(CurrentDestXYZ[1]))
        *(unsigned long *) &ZMax = *(unsigned long *) &GetZ(CurrentDestXYZ[0]);
    else
        *(unsigned long *) &ZMax = *(unsigned long *) &GetZ(CurrentDestXYZ[1]);
    
    if(*(unsigned long *) &ZMax > *(unsigned long *) &GetZ(CurrentDestXYZ[2]))
        *(unsigned long *) &ZMax = *(unsigned long *) &GetZ(CurrentDestXYZ[2]);
    
    GLI_v_AddNodeInZList(ZMax);
    (unsigned long) GLI_BIG_GLOBALS->p_TheZListe->p_CurrentPoint += sizeof(GrVertex) * 3L;
    
#if !defined(PRESS_DEMO)
    PRF_MDRV_vSetIndependantVariable(PRF_C_ulIdpDisplayedTriangle, 1);
#endif /* PRESS_DEMO */
}

/*$F
 ===================================================================================================
    Description:: draw a quad stored in CurrentDestXYZ array or add it in Z list 
            1   0 
            |  /| Send two triangle:: 0 1 2 and 3 2 0 
            | / | 
            |/  | 
            2   3
 ===================================================================================================
 */
void GLI_DrawQuad(void)
{
    /* Draw 2 triangles */
    GLI_fn_vCorrect3DFXBug1(&CurrentDestXYZ[0]);
    GLI_fn_vCorrect3DFXBug1(&CurrentDestXYZ[1]);
    GLI_fn_vCorrect3DFXBug1(&CurrentDestXYZ[2]);
    GLI_fn_vCorrect3DFXBug1(&CurrentDestXYZ[3]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[0]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[1]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[2]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[3]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[0]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[1]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[2]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[3]);

    if(GLI_BIG_GLOBALS->lClippingModeMask & GLI_C_TrianglesMustBeSorted)
    {
        GLI_BIG_GLOBALS->lCurrentDrawMask &= ~GLI_C_lIsGouraud;
        if
        ( 
            (unsigned long) GLI_BIG_GLOBALS->p_TheZListe->p_CurrentPoint >
            (unsigned long) GLI_BIG_GLOBALS->p_TheZListe->p_TableOfPoints + ((3L << GLI_MaxZListeTrianglesPO2) * sizeof(GrVertex)) - 4L * sizeof(GrVertex)
        ) 
            return;
        
        memcpy(GLI_BIG_GLOBALS->p_TheZListe->p_CurrentPoint, CurrentDestXYZ, sizeof(GrVertex) * 4L);
        GLI_v_AddNodeInZList(GetZ(CurrentDestXYZ[0]));
        (unsigned long) GLI_BIG_GLOBALS->p_TheZListe->p_CurrentPoint += sizeof(GrVertex) * 4L;
    }
    else
    {
#ifdef GLI_DEBUG_TESTALLVERTICES
        if
        (
            (CurrentDestXYZ[0].y < 0.0f) ||
            (CurrentDestXYZ[1].y < 0.0f) ||
            (CurrentDestXYZ[2].y < 0.0f) ||
            (CurrentDestXYZ[3].y < 0.0f) ||
            (CurrentDestXYZ[0].y > 480.0f) ||
            (CurrentDestXYZ[1].y > 480.0f) ||
            (CurrentDestXYZ[2].y > 480.0f) ||
            (CurrentDestXYZ[3].y > 480.0f) ||
            (CurrentDestXYZ[0].x < 0.0f) ||
            (CurrentDestXYZ[1].x < 0.0f) ||
            (CurrentDestXYZ[2].x < 0.0f) ||
            (CurrentDestXYZ[3].x < 0.0f) ||
            (CurrentDestXYZ[0].x > 640.0f) ||
            (CurrentDestXYZ[1].x > 640.0f) ||
            (CurrentDestXYZ[2].x > 640.0f) ||
            (CurrentDestXYZ[3].x > 640.0f)
        ) return;
#endif
        M_grDrawTriangle(&CurrentDestXYZ[0], &CurrentDestXYZ[1], &CurrentDestXYZ[2]);
        M_grDrawTriangle(&CurrentDestXYZ[3], &CurrentDestXYZ[2], &CurrentDestXYZ[0]);
#if !defined(PRESS_DEMO)
        PRF_MDRV_vIncreaseVariable
        (
            PRF_C_ulVarFaces + PRF_MDRV_lGetIndependantVariable(PRF_C_ulIdpDynOrSta),
            PRF_C_pvDisplayed,
            2
        );
        PRF_MDRV_vSetIndependantVariable(PRF_C_ulIdpDisplayedFaces, 1);
#endif /* PRESS_DEMO */
    }
}

/*
 ===================================================================================================
    Description:: Draw a line
 ===================================================================================================
 */
void GLI_DrawLine(void)
{
    GLI_fn_vCorrect3DFXBug1(&CurrentDestXYZ[0]);
    GLI_fn_vCorrect3DFXBug1(&CurrentDestXYZ[1]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[0]);
    GLI_M_Correct3DFXBug2(CurrentDestXYZ[1]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[0]);
    GLI_M_Restore3DFXColor(CurrentDestXYZ[1]);

#ifdef GLI_DEBUG_TESTALLVERTICES
    if
    (
        (CurrentDestXYZ[0].y < 0.0f) ||
        (CurrentDestXYZ[1].y < 0.0f) ||
        (CurrentDestXYZ[0].y > 480.0f) ||
        (CurrentDestXYZ[1].y > 480.0f) ||
        (CurrentDestXYZ[0].x < 0.0f) ||
        (CurrentDestXYZ[1].x < 0.0f) ||
        (CurrentDestXYZ[0].x > 640.0f) ||
        (CurrentDestXYZ[1].x > 640.0f)
    ) return;
#endif

    grDrawLine(&CurrentDestXYZ[0], &CurrentDestXYZ[1]);
}

/*
 ===================================================================================================
    Description:: draw Zsorted triangle
 ===================================================================================================
 */
void GLI_vDrawZSortedTriangle(void *p_3Point)
{
#ifdef GLI_DEBUG_TESTALLVERTICES
    if
    (
        (((GrVertex *) p_3Point)->y < 0.0f) ||
        (((GrVertex *) p_3Point + 1)->y < 0.0f) ||
        (((GrVertex *) p_3Point + 2)->y < 0.0f) ||
        (((GrVertex *) p_3Point)->y > 480.0f) ||
        (((GrVertex *) p_3Point + 1)->y > 480.0f) ||
        (((GrVertex *) p_3Point + 2)->y > 480.0f) ||
        (((GrVertex *) p_3Point)->x < 0.0f) ||
        (((GrVertex *) p_3Point + 1)->x < 0.0f) ||
        (((GrVertex *) p_3Point + 2)->x < 0.0f) ||
        (((GrVertex *) p_3Point)->x > 640.0f) ||
        (((GrVertex *) p_3Point + 1)->x > 640.0f) ||
        (((GrVertex *) p_3Point + 2)->x > 640.0f)
    ) return;
#endif
    //grDrawTriangle((GrVertex *) p_3Point, ((GrVertex *) p_3Point) + 1, ((GrVertex *) p_3Point) + 2);
    M_grDrawTriangle((GrVertex *) p_3Point, ((GrVertex *) p_3Point) + 1, ((GrVertex *) p_3Point) + 2);
}

/*
 ===================================================================================================
    Description:: draw ZSorted quad
 ===================================================================================================
 */
void GLI_vDrawZSortedQuad(void *p_3Point)
{
#ifdef GLI_DEBUG_TESTALLVERTICES
    if
    (
        (((GrVertex *) p_3Point)->y < 0.0f) ||
        (((GrVertex *) p_3Point + 1)->y < 0.0f) ||
        (((GrVertex *) p_3Point + 2)->y < 0.0f) ||
        (((GrVertex *) p_3Point + 3)->y < 0.0f) ||
        (((GrVertex *) p_3Point)->y > 480.0f) ||
        (((GrVertex *) p_3Point + 1)->y > 480.0f) ||
        (((GrVertex *) p_3Point + 2)->y > 480.0f) ||
        (((GrVertex *) p_3Point + 3)->y > 480.0f) ||
        (((GrVertex *) p_3Point)->x < 0.0f) ||
        (((GrVertex *) p_3Point + 1)->x < 0.0f) ||
        (((GrVertex *) p_3Point + 2)->x < 0.0f) ||
        (((GrVertex *) p_3Point + 3)->x < 0.0f) ||
        (((GrVertex *) p_3Point)->x > 640.0f) ||
        (((GrVertex *) p_3Point + 1)->x > 640.0f) ||
        (((GrVertex *) p_3Point + 2)->x > 640.0f) ||
        (((GrVertex *) p_3Point + 3)->x > 640.0f)
    ) return;
#endif
    M_grDrawTriangle((GrVertex *) p_3Point, ((GrVertex *) p_3Point) + 1, ((GrVertex *) p_3Point) + 2);
    M_grDrawTriangle
    (
        ((GrVertex *) p_3Point) + 3,
        ((GrVertex *) p_3Point) + 2,
        ((GrVertex *) p_3Point) + 0
    );

}

/*
 ===================================================================================================
    Description:: choose good drawing function
 ===================================================================================================
 */
void GLI_vAddDrawFunc(void)
{
    GLI_M_AddFunc(GLI_vCorrect3DFXBug1);
    ulFuncMask |= Flag_GLI_vCorrect3DFXBug1;

    if(GLI_BIG_GLOBALS->lClippingModeMask & GLI_C_TrianglesMustBeSorted)
    {
        GLI_M_AddFunc(GLI_vZSortTriangle);
        ulFuncMask |= Flag_GLI_vZSortTriangle;
    }
    else if((GLI_BIG_GLOBALS->lCurrentDrawMask & GLI_C_lIsNotWired) == 0)
    {
        GLI_M_AddFunc(GLI_vDrawWiredTriangle);
        ulFuncMask |= Flag_GLI_vDrawWiredTriangle;
    }
    else
    {
        GLI_M_AddFunc(GLI_vDrawTriangle);
        ulFuncMask |= Flag_GLI_vDrawTriangle;
    }
}

/*
 ===================================================================================================
    Description:: compute clipping mask
 ===================================================================================================
 */
void GLI_ReComputeClippingMask(void)
{
    *(float *) &fX_CMP_Optimize = GLI_BIG_GLOBALS->fXMaxClipping;
    *(float *) &fY_CMP_Optimize = GLI_BIG_GLOBALS->fYMaxClipping;
    *(float *) &fXMin_CLIP_Optimize = GLI_BIG_GLOBALS->fXMinClipping;
    *(float *) &fYMin_CLIP_Optimize = GLI_BIG_GLOBALS->fYMinClipping;
    *(float *) &fXMax_CLIP_Optimize = GLI_BIG_GLOBALS->fXMaxClipping;
    *(float *) &fYMax_CLIP_Optimize = GLI_BIG_GLOBALS->fYMaxClipping;

    GLI_BIG_GLOBALS->lClippingModeMask |= (GLI_BIG_GLOBALS->lClippingModeMask & GLI_C_lCLIP_ALL) << 4;
}

/*
 ===================================================================================================
    Description:: clip a sprite
 ===================================================================================================
 */
void GLI_DRV_vSendSpriteToClip
(
    GLI_tdstAligned2DVector                 *a4_st2DVertex,
    MTH_tdxReal                             xZ,
    GLI_tdstInternalGlobalValuesFor3dEngine *p_stGlobaleMT
)
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    float   fClipYMax, fClipYMin, fClipXMax, fClipXMin;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    if(*(long *) &GLI_BIG_GLOBALS->xWaterPlaneDistance != 0)
    {
        GLI_DRV_xSendSpriteToClip_TRIANGLES(a4_st2DVertex, p_stGlobaleMT);
        return;
    }

    GLI_BeforeDraw();

    if(a4_st2DVertex[0].xY == a4_st2DVertex[1].xY)  /* Aligned sprite */
    {
        /* If (xZ < 0) */

        /* Return; */
        GLI_ReComputeClippingMask();

        if
        (
            (*(long *) &a4_st2DVertex[0].xY > fYMax_CLIP_Optimize) ||
            (*(long *) &a4_st2DVertex[2].xY < fYMin_CLIP_Optimize) ||
            (*(long *) &a4_st2DVertex[0].xX < fXMin_CLIP_Optimize) ||
            (*(long *) &a4_st2DVertex[1].xX > fXMax_CLIP_Optimize)
        ) return;

        /* Clip xMax */
        if(*(long *) &a4_st2DVertex[0].xX > fXMax_CLIP_Optimize)
        {
            fClipXMax = (GLI_BIG_GLOBALS->fXMaxClipping - a4_st2DVertex[1].xX) /
            (a4_st2DVertex[0].xX - a4_st2DVertex[1].xX);
            a4_st2DVertex[0].xX = a4_st2DVertex[3].xX = GLI_BIG_GLOBALS->fXMaxClipping;
        }
        else
            fClipXMax = 1.0f;

        /* Clip xMin */
        if(*(long *) &a4_st2DVertex[1].xX < fXMin_CLIP_Optimize)
        {
            fClipXMin = (fClipXMax * (GLI_BIG_GLOBALS->fXMinClipping - a4_st2DVertex[1].xX)) /
            (a4_st2DVertex[0].xX - a4_st2DVertex[1].xX);
            a4_st2DVertex[1].xX = a4_st2DVertex[2].xX = GLI_BIG_GLOBALS->fXMinClipping;
        }
        else
            fClipXMin = 0.0f;

        /* Clip yMin */
        if(*(long *) &a4_st2DVertex[0].xY < fYMin_CLIP_Optimize)
        {
            fClipYMin = (GLI_BIG_GLOBALS->fYMinClipping - a4_st2DVertex[2].xY) /
            (a4_st2DVertex[0].xY - a4_st2DVertex[2].xY);
            a4_st2DVertex[0].xY = a4_st2DVertex[1].xY = GLI_BIG_GLOBALS->fYMinClipping;
        }
        else
            fClipYMin = 1.0f;

        /* Clip yMax */
        if(*(long *) &a4_st2DVertex[2].xY > fYMax_CLIP_Optimize)
        {
            fClipYMax = (fClipYMin * (GLI_BIG_GLOBALS->fYMaxClipping - a4_st2DVertex[2].xY)) /
            (a4_st2DVertex[0].xY - a4_st2DVertex[2].xY);
            a4_st2DVertex[2].xY = a4_st2DVertex[3].xY = GLI_BIG_GLOBALS->fYMaxClipping;
        }
        else
            fClipYMax = 0.0f;
    }
    else
    {
        if
        (
            (a4_st2DVertex[0].xY < 0.0f) ||
            (a4_st2DVertex[1].xY < 0.0f) ||
            (a4_st2DVertex[2].xY < 0.0f) ||
            (a4_st2DVertex[3].xY < 0.0f)
        ) return;
        if
        (
            (a4_st2DVertex[0].xX < 0.0f) ||
            (a4_st2DVertex[1].xX < 0.0f) ||
            (a4_st2DVertex[2].xX < 0.0f) ||
            (a4_st2DVertex[3].xX < 0.0f)
        ) return;
        if
        (
            (a4_st2DVertex[0].xY > 480.0f) ||
            (a4_st2DVertex[1].xY > 480.0f) ||
            (a4_st2DVertex[2].xY > 480.0f) ||
            (a4_st2DVertex[3].xY > 480.0f)
        ) return;
        if
        (
            (a4_st2DVertex[0].xX > 640.0f) ||
            (a4_st2DVertex[1].xX > 640.0f) ||
            (a4_st2DVertex[2].xX > 640.0f) ||
            (a4_st2DVertex[3].xX > 640.0f)
        ) return;
        fClipXMin = fClipYMax = 0.0f;
        fClipYMin = fClipXMax = 1.0f;
    }

    GLI_M_InitSprite();

    SetU(CurrentDestXYZ[0], fClipXMax);
    SetV(CurrentDestXYZ[0], fClipYMin);
    SetU(CurrentDestXYZ[1], fClipXMin);
    SetV(CurrentDestXYZ[1], fClipYMin);
    SetU(CurrentDestXYZ[2], fClipXMin);
    SetV(CurrentDestXYZ[2], fClipYMax);
    SetU(CurrentDestXYZ[3], fClipXMax);
    SetV(CurrentDestXYZ[3], fClipYMax);

    GLI_DrawQuad();
    GLI_AfterDraw();
}

/*
 ===================================================================================================
    Description:: clip a sprite that have UV coordinates (it seems to be unused)
 ===================================================================================================
 */
void GLI_DRV_vSendSpriteToClipWithUV
(
    GLI_tdstAligned2DVector                 *a4_st2DVertex,
    MTH_tdxReal                             *a8_stUVVertex,
    MTH_tdxReal                             xZ,
    GLI_tdstInternalGlobalValuesFor3dEngine *p_stGlobaleMT
)
{
    GLI_BeforeDraw();
    GLI_M_InitSprite();
 
    if(a4_st2DVertex[0].xY < 0.0f) return;
    if(a4_st2DVertex[1].xY < 0.0f) return;
    if(a4_st2DVertex[2].xY < 0.0f) return;
    if(a4_st2DVertex[3].xY < 0.0f) return;
    if(a4_st2DVertex[0].xY > GLI_BIG_GLOBALS->fYMaxClipping) return;
    if(a4_st2DVertex[1].xY > GLI_BIG_GLOBALS->fYMaxClipping) return;
    if(a4_st2DVertex[2].xY > GLI_BIG_GLOBALS->fYMaxClipping) return;
    if(a4_st2DVertex[3].xY > GLI_BIG_GLOBALS->fYMaxClipping) return;
 
    if ( p_stGlobaleMT->p_stCurrentTexture->lIncrementIsEnable )
    {
        float fAddU, fAddV;
        fAddU = p_stGlobaleMT-> p_stCurrentTexture -> fAddU;
        fAddV = p_stGlobaleMT-> p_stCurrentTexture -> fAddV;
        SetU(CurrentDestXYZ[2], a8_stUVVertex[4] + fAddU );
        SetV(CurrentDestXYZ[2], a8_stUVVertex[4 + 1] + fAddV);
        SetU(CurrentDestXYZ[3], a8_stUVVertex[6] + fAddU);
        SetV(CurrentDestXYZ[3], a8_stUVVertex[6 + 1] + fAddV);
        SetU(CurrentDestXYZ[0], a8_stUVVertex[0] + fAddU);
        SetV(CurrentDestXYZ[0], a8_stUVVertex[0 + 1] + fAddV);
        SetU(CurrentDestXYZ[1], a8_stUVVertex[2] + fAddU);
        SetV(CurrentDestXYZ[1], a8_stUVVertex[2 + 1] + fAddV);
    }
    else
    {
        SetU(CurrentDestXYZ[2], a8_stUVVertex[4]);
        SetV(CurrentDestXYZ[2], a8_stUVVertex[4 + 1]);
        SetU(CurrentDestXYZ[3], a8_stUVVertex[6]);
        SetV(CurrentDestXYZ[3], a8_stUVVertex[6 + 1]);
        SetU(CurrentDestXYZ[0], a8_stUVVertex[0]);
        SetV(CurrentDestXYZ[0], a8_stUVVertex[0 + 1]);
        SetU(CurrentDestXYZ[1], a8_stUVVertex[2]);
        SetV(CurrentDestXYZ[1], a8_stUVVertex[2 + 1]);
    }
 
    GLI_DrawQuad();
    GLI_AfterDraw();
}

/*
 ===================================================================================================
    Description:: clip a line and draw it.
 ===================================================================================================
 */
void GLI_DRV_vSendSingleLineToClip
(
    GLD_tdstViewportAttributes              *p_stVpt,
    GLI_tdstAligned3DVector                 *p_stVertex1,
    GLI_tdstAligned2DVector                 *p_st2DVertex1,
    GLI_tdstAligned3DVector                 *p_stVertex2,
    GLI_tdstAligned2DVector                 *p_st2DVertex2,
    GLI_tdstInternalGlobalValuesFor3dEngine *p_stGlobaleMT,
    long                                    lDrawModeMask,
    GEO_tdstColor                           *p_stColor
)
{
#ifdef _DEBUG

    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    GLI_tdstAligned2DVector stP1, stP2;
    GLI_tdstAligned3DVector st3DP1, st3DP2;
    float                   fFactor;
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    grAlphaCombine( GR_COMBINE_FUNCTION_SCALE_OTHER,GR_COMBINE_FACTOR_ONE,GR_COMBINE_LOCAL_ITERATED,GR_COMBINE_OTHER_ITERATED,FXFALSE);
    grAlphaBlendFunction(GR_BLEND_ONE, GR_BLEND_ZERO, GR_BLEND_ONE, GR_BLEND_ZERO);
    guColorCombineFunction( GR_COLORCOMBINE_ITRGB );


    /* Z clipping */
    if( p_st2DVertex1->xOoZ < p_st2DVertex2->xOoZ )
    {
        stP1 = *p_st2DVertex1;
        st3DP1 = *p_stVertex1;
        stP2 = *p_st2DVertex2;
        st3DP2 = *p_stVertex2;
    }
    else
    {
        stP2 = *p_st2DVertex1;
        st3DP2 = *p_stVertex1;
        stP1 = *p_st2DVertex2;
        st3DP1 = *p_stVertex2;
    }

    if ( (*(unsigned long *)&stP2.xOoZ & 0x80000000) )
        return; 

    // At least one point out ?
    if ( (*(unsigned long *)&stP1.xOoZ & 0x80000000) )
    {
        // The point 1 (smallest xOoZ) is out.
        fFactor = ( p_stGlobaleMT->p_stCurrentCamera->xNear - st3DP2.xZ ) / (st3DP1.xZ - st3DP2.xZ);
        st3DP1.xX = st3DP2.xX + (st3DP1.xX - st3DP2.xX) * fFactor;
        st3DP1.xY = st3DP2.xY + (st3DP1.xY - st3DP2.xY) * fFactor;
        st3DP1.xZ = GLI_BIG_GLOBALS  -> fZClipping;
        GLI_MDRV_vSerialProjection( GLI_BIG_GLOBALS  -> p_stCurrentCamera , 1 , &st3DP1 , &stP1 );
    }

    /* XClipping */
    if(stP1.xX < stP2.xX)
    {
        p_st2DVertex1 = &stP1;
        p_stVertex1 = &st3DP1;
        p_st2DVertex2 = &stP2;
        p_stVertex2 = &st3DP2;
    }
    else
    {
        p_st2DVertex1 = &stP2;
        p_stVertex1 = &st3DP2;
        p_st2DVertex2 = &stP1;
        p_stVertex2 = &st3DP1;
    }

    if((p_st2DVertex2->xX < GLI_BIG_GLOBALS->fXMinClipping) || (p_st2DVertex1->xX > GLI_BIG_GLOBALS->fXMaxClipping)) return;

    if(p_st2DVertex1->xX < GLI_BIG_GLOBALS->fXMinClipping)
    {
        fFactor = (p_st2DVertex2->xX - GLI_BIG_GLOBALS->fXMinClipping) / (p_st2DVertex2->xX - p_st2DVertex1->xX);
        p_st2DVertex1->xY = p_st2DVertex2->xY - (p_st2DVertex2->xY - p_st2DVertex1->xY) * fFactor;
        p_stVertex1->xZ = p_stVertex2->xZ - (p_stVertex2->xY - p_stVertex1->xY) * fFactor;
        p_st2DVertex1->xX = GLI_BIG_GLOBALS->fXMinClipping;
    }

    if(p_st2DVertex2->xX > GLI_BIG_GLOBALS->fXMaxClipping)
    {
        fFactor = (p_st2DVertex1->xX - GLI_BIG_GLOBALS->fXMaxClipping) / (p_st2DVertex2->xX - p_st2DVertex1->xX);
        p_st2DVertex2->xY = p_st2DVertex1->xY - (p_st2DVertex2->xY - p_st2DVertex1->xY) * fFactor;
        p_stVertex2->xZ = p_stVertex1->xZ - (p_stVertex2->xY - p_stVertex1->xY) * fFactor;
        p_st2DVertex2->xX = GLI_BIG_GLOBALS->fXMaxClipping;
    }

    /* YClipping */
    if(stP1.xY < stP2.xY)
    {
        p_st2DVertex1 = &stP1;
        p_stVertex1 = &st3DP1;
        p_st2DVertex2 = &stP2;
        p_stVertex2 = &st3DP2;
    }
    else
    {
        p_st2DVertex1 = &stP2;
        p_stVertex1 = &st3DP2;
        p_st2DVertex2 = &stP1;
        p_stVertex2 = &st3DP1;
    }

    if((p_st2DVertex2->xY < GLI_BIG_GLOBALS->fYMinClipping) || (p_st2DVertex1->xY > GLI_BIG_GLOBALS->fYMaxClipping)) return;

    if(p_st2DVertex1->xY < GLI_BIG_GLOBALS->fYMinClipping)
    {
        fFactor = ( p_st2DVertex2->xY - GLI_BIG_GLOBALS->fYMinClipping )/ (p_st2DVertex2->xY - p_st2DVertex1->xY);
        p_st2DVertex1->xX = p_st2DVertex2->xX - (p_st2DVertex2->xX - p_st2DVertex1->xX) * fFactor;
        p_stVertex1->xZ = p_stVertex2->xZ - (p_stVertex2->xZ - p_stVertex1->xZ) * fFactor;
        p_st2DVertex1->xY = GLI_BIG_GLOBALS->fYMinClipping;
    }

    if(p_st2DVertex2->xY > GLI_BIG_GLOBALS->fYMaxClipping)
    {
        fFactor = (p_st2DVertex1->xY - GLI_BIG_GLOBALS->fYMaxClipping) / (p_st2DVertex2->xY - p_st2DVertex1->xY);
        p_st2DVertex2->xX = p_st2DVertex1->xX - (p_st2DVertex2->xX - p_st2DVertex1->xX) * fFactor;
        p_stVertex2->xZ = p_stVertex1->xZ - (p_stVertex2->xZ - p_stVertex1->xZ) * fFactor;
        p_st2DVertex2->xY = GLI_BIG_GLOBALS->fYMaxClipping;
    }

#else
    if(p_st2DVertex1->xX < 0) return;
    if(p_st2DVertex1->xY < 0) return;
    if(p_st2DVertex1->xX > 640.0f) return;
    if(p_st2DVertex1->xY > 480.0f) return;
    if(p_st2DVertex2->xX < 0) return;
    if(p_st2DVertex2->xY < 0) return;
    if(p_st2DVertex2->xX > 640.0f) return;
    if(p_st2DVertex2->xY > 480.0f) return;
#endif
    GLI_M_InitLine();

    GLI_BeforeDraw();

    GLI_DrawLine();

    GLI_AfterDraw();
}

