/******************************Module*Header**********************************\
*
*                           *******************
*                           * D3D SAMPLE CODE *
*                           *******************
*
* Module Name: d3dtri.c
*
*  Content:    Direct3D hw triangle rasterization code.
*
* Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved.
* Copyright (c) 1995-1999 Microsoft Corporation.  All rights reserved.
\*****************************************************************************/

#include "precomp.h"
#include "d3ddelta.h"
#include "d3dhw.h"
#include "d3dcntxt.h"
#if defined(_ALPHA_)
#include <math.h>
#endif

//-----------------------------------------------------------------------------
//
// VOID P2_Draw_FVF_Solid_Tri
//
// Hardware render a single triangle coming from three FVF vertices
//
//-----------------------------------------------------------------------------
VOID
P2_Draw_FVF_Solid_Tri(PERMEDIA_D3DCONTEXT  *pContext,
                      LPD3DTLVERTEX    lpV0, 
                      LPD3DTLVERTEX    lpV1,
                      LPD3DTLVERTEX    lpV2, 
                      LPP2FVFOFFSETS   lpFVFOff)
{
    DWORD           dwFlags = pContext->Hdr.Flags;
    DWORD           dwColorOffs,dwSpecularOffs,dwTexOffs;
    ULONG           ulRenderCmd = pContext->RenderCommand;
    D3DCOLOR        dwColor0, dwColor1, dwColor2;
    D3DCOLOR        dwSpec0, dwSpec1, dwSpec2;
    D3DVALUE        fS0, fS1, fS2, fT0, fT1, fT2, fQ0, fQ1, fQ2;
    D3DVALUE        fKs0, fKs1, fKs2;
    PERMEDIA_DEFS(pContext->ppdev);

    DBG_D3D((10,"Entering P2_Draw_FVF_Solid_Tri"));

    // Set triangle rendering mode
    RENDER_TRAPEZOID(ulRenderCmd);

    RESERVEDMAPTR(0x80);

    // Get FVF structure offsets
    __SetFVFOffsets(&dwColorOffs,&dwSpecularOffs,&dwTexOffs,lpFVFOff);

    // Get vertex color value (FVF based)
    if (dwColorOffs)
    {
        dwColor0  = FVFCOLOR(lpV0, dwColorOffs)->color;
        dwColor1  = FVFCOLOR(lpV1, dwColorOffs)->color;
        dwColor2  = FVFCOLOR(lpV2, dwColorOffs)->color;
        if (FAKE_ALPHABLEND_MODULATE & pContext->FakeBlendNum)
        {
            dwColor0  |= 0xFF000000;
            dwColor1  |= 0xFF000000;
            dwColor2  |= 0xFF000000;
        }
    }
    else
    {
        // must set default in case no D3DFVF_DIFFUSE
        dwColor0  = 0xFFFFFFFF;
        dwColor1  = 0xFFFFFFFF;
        dwColor2  = 0xFFFFFFFF;
    }
    // Get vertex specular value (FVF based) if necessary
    if ((dwFlags & (CTXT_HAS_SPECULAR_ENABLED | CTXT_HAS_FOGGING_ENABLED))
        && (dwSpecularOffs!=0))
    {
        dwSpec0   = FVFSPEC(lpV0, dwSpecularOffs)->specular;
        dwSpec1   = FVFSPEC(lpV1, dwSpecularOffs)->specular;
        dwSpec2   = FVFSPEC(lpV2, dwSpecularOffs)->specular;
    }

    if ( (dwFlags & CTXT_HAS_TEXTURE_ENABLED) && (dwTexOffs != 0) )
    {
         // Get s,t texture coordinates (FVF based)
        fS0 = FVFTEX(lpV0,dwTexOffs)->tu; 
        fT0 = FVFTEX(lpV0,dwTexOffs)->tv;
        
        fS1 = FVFTEX(lpV1,dwTexOffs)->tu; 
        fT1 = FVFTEX(lpV1,dwTexOffs)->tv;
        
        fS2 = FVFTEX(lpV2,dwTexOffs)->tu; 
        fT2 = FVFTEX(lpV2,dwTexOffs)->tv;

        // The hw requires us to keep the texture coordinates centered around 0
        // and avoid exceeding the texel wrapping limit.
        RECENTER_TEX_COORDS(pContext->MaxTextureXf, 
                            pContext->MaxTextureXi, fS0, fS1, fS2);
        RECENTER_TEX_COORDS(pContext->MaxTextureYf, 
                            pContext->MaxTextureYi, fT0, fT1, fT2);

        // Wrap texture coordinates if necessary
        WRAP(fS,dwFlags & CTXT_HAS_WRAPU_ENABLED);
        WRAP(fT,dwFlags & CTXT_HAS_WRAPV_ENABLED);

        // Scale s,t coordinate values
        fS0 *= pContext->DeltaWidthScale;
        fS1 *= pContext->DeltaWidthScale;
        fS2 *= pContext->DeltaWidthScale;
        fT0 *= pContext->DeltaHeightScale;
        fT1 *= pContext->DeltaHeightScale;
        fT2 *= pContext->DeltaHeightScale;

        // Apply perspective corrections if necessary
        if (dwFlags & CTXT_HAS_PERSPECTIVE_ENABLED)
        {
            fQ0 = lpV0->rhw; 
            fQ1 = lpV1->rhw;
            fQ2 = lpV2->rhw;

            fS0 *= fQ0;
            fT0 *= fQ0;

            fS1 *= fQ1;
            fT1 *= fQ1;

            fS2 *= fQ2;
            fT2 *= fQ2;
        }
        else
        {
            fQ0 = fQ1 = fQ2 = 1.0;
        }

        // Send lines s,t,q,ks (conditionaly),x,y,z values
        if ((dwFlags & CTXT_HAS_SPECULAR_ENABLED) && (dwSpecularOffs!=0))
        {
            fKs0   = RGB256_TO_LUMA(RGB_GETRED(dwSpec0),
                                        RGB_GETGREEN(dwSpec0),
                                        RGB_GETBLUE(dwSpec0));
            if (dwFlags & CTXT_HAS_GOURAUD_ENABLED)
            {
                fKs1 = RGB256_TO_LUMA(RGB_GETRED(dwSpec1),
                                        RGB_GETGREEN(dwSpec1),
                                        RGB_GETBLUE(dwSpec1));
                fKs2 = RGB256_TO_LUMA(RGB_GETRED(dwSpec2),
                                        RGB_GETGREEN(dwSpec2),
                                        RGB_GETBLUE(dwSpec2));
            }   
            else 
            {
                fKs2 = fKs1 = fKs0; 
            }

            SEND_VERTEX_STQ_KS_XYZ(__Permedia2TagV0FloatS, fS0, fT0, fQ0, fKs0,
                                                  lpV0->sx, lpV0->sy, lpV0->sz);
            SEND_VERTEX_STQ_KS_XYZ(__Permedia2TagV1FloatS, fS1, fT1, fQ1, fKs1,
                                                  lpV1->sx, lpV1->sy, lpV1->sz);
            SEND_VERTEX_STQ_KS_XYZ(__Permedia2TagV2FloatS, fS2, fT2, fQ2, fKs2,
                                                  lpV2->sx, lpV2->sy, lpV2->sz);
        }
        else
        {
            SEND_VERTEX_STQ_XYZ(__Permedia2TagV0FloatS, fS0, fT0, fQ0,
                                                lpV0->sx, lpV0->sy, lpV0->sz);
            SEND_VERTEX_STQ_XYZ(__Permedia2TagV1FloatS, fS1, fT1, fQ1,
                                                lpV1->sx, lpV1->sy, lpV1->sz);
            SEND_VERTEX_STQ_XYZ(__Permedia2TagV2FloatS, fS2, fT2, fQ2,
                                                lpV2->sx, lpV2->sy, lpV2->sz);
        }

    }
    else     // not textured triangle
    {
        // If specular is enabled, change the colours
        if ((dwFlags & CTXT_HAS_SPECULAR_ENABLED) && (dwSpecularOffs!=0))
        {
            CLAMP8888(dwColor0, dwColor0, dwSpec0);
            CLAMP8888(dwColor1, dwColor1, dwSpec1);
            CLAMP8888(dwColor2, dwColor2, dwSpec2);
        }

        // Send triangles x,y,z values
        SEND_VERTEX_XYZ(__Permedia2TagV0FloatS, lpV0->sx, lpV0->sy, lpV0->sz);
        SEND_VERTEX_XYZ(__Permedia2TagV1FloatS, lpV1->sx, lpV1->sy, lpV1->sz);
        SEND_VERTEX_XYZ(__Permedia2TagV2FloatS, lpV2->sx, lpV2->sy, lpV2->sz);
    }

    // If fog is set, send the appropriate values
    if ((dwFlags & CTXT_HAS_FOGGING_ENABLED) && (dwSpecularOffs!=0))
    {
        SEND_VERTEX_FOG(__Permedia2TagV0FixedF, RGB_GET_GAMBIT_FOG(dwSpec0));
        SEND_VERTEX_FOG(__Permedia2TagV1FixedF, RGB_GET_GAMBIT_FOG(dwSpec1));
        SEND_VERTEX_FOG(__Permedia2TagV2FixedF, RGB_GET_GAMBIT_FOG(dwSpec2));
    }

    // Set alpha stippling if required by context
    if (dwFlags & CTXT_HAS_ALPHASTIPPLE_ENABLED)
    {
        SET_STIPPLED_ALPHA( (RGBA_GETALPHA(lpV0->color) >> 4) );
    }

    // Send appropriate color depending on Gouraud , Mono, & Alpha
    if (dwFlags & CTXT_HAS_GOURAUD_ENABLED)
    {
        // Gouraud shading
        if (RENDER_MONO)
        {
            SEND_VERTEX_RGB_MONO(__Permedia2TagV0FixedS, dwColor0);
            SEND_VERTEX_RGB_MONO(__Permedia2TagV1FixedS, dwColor1);
            SEND_VERTEX_RGB_MONO(__Permedia2TagV2FixedS, dwColor2);
        }
        else
        {
            if (dwFlags & CTXT_HAS_ALPHABLEND_ENABLED)
            {
                if (pContext->FakeBlendNum & FAKE_ALPHABLEND_ONE_ONE)
                {
                    dwColor0 &= 0xFFFFFF;     // supress color's alpha value
                    dwColor1 &= 0xFFFFFF;
                    dwColor2 &= 0xFFFFFF;
                }
                SEND_VERTEX_RGBA(__Permedia2TagV0FixedS, dwColor0);
                SEND_VERTEX_RGBA(__Permedia2TagV1FixedS, dwColor1);
                SEND_VERTEX_RGBA(__Permedia2TagV2FixedS, dwColor2);
            }
            else
            {
                SEND_VERTEX_RGB(__Permedia2TagV0FixedS, dwColor0);
                SEND_VERTEX_RGB(__Permedia2TagV1FixedS, dwColor1);
                SEND_VERTEX_RGB(__Permedia2TagV2FixedS, dwColor2);
            }
        }
    }
    else    // Flat shading
    {
        if (RENDER_MONO)
        {
            // Get constant color from the blue channel
            DWORD BlueChannel = RGBA_GETBLUE(dwColor0);
            SEND_PERMEDIA_DATA( ConstantColor,
                            RGB_MAKE(BlueChannel, BlueChannel, BlueChannel));
        }
        else
        {
            if (pContext->FakeBlendNum & FAKE_ALPHABLEND_ONE_ONE)
            {
                dwColor0 &= 0xFFFFFF;
            }

            SEND_PERMEDIA_DATA( ConstantColor,
                                   RGBA_MAKE(RGBA_GETBLUE(dwColor0),
                                             RGBA_GETGREEN(dwColor0),
                                             RGBA_GETRED(dwColor0), 
                                             RGBA_GETALPHA(dwColor0)));
        }
    }

    SEND_PERMEDIA_DATA(DrawTriangle, ulRenderCmd); 
    COMMITDMAPTR();

    DBG_D3D((10,"Exiting P2_Draw_FVF_Solid_Tri"));

} // P2_Draw_FVF_Solid_Tri


