//=================================================================
//
// Copyright (c) Microsoft Corporation 1999 - All Rights Reserved.
//
//=================================================================

//-----------------------------------------------------------------------------
// Drawspans.h: Generates and draws spans for a triangle.
//
// _FLAT_                   Define this to use flat shading
// _GOURAUD_                Define this to use Gouraud shading
// _TEXMAP_                 Define this to use texture mapping
// _ZBUFFER_                Define this to use z-buffering
// _AFFINE_                 Define this to use affine texture mapping
// _PERSPECTIVE_            Define this to use perspective texture mapping
// _FOG_                    Define this to interpolate fog intensity along the edge.
//
//-----------------------------------------------------------------------------

#if defined(_PERSPECTIVE_) && defined(_AFFINE_)
#error Can't define both _PERSPECTIVE_ and _AFFINE_ at once
#endif

#if defined(_TEXMAP_)
#undef _TEXMAP_
#endif

#if defined(_AFFINE_) || defined(_PERSPECTIVE_)
#define _TEXMAP_
#endif

    int     i, itzabState;
    const BYTE *  pbyte;

    // Patch entry vector and rgpspanfn[] for the proper trivialz and backend processors
    itzabState = g_grdRenderData.dwState >> TRIVIALZ_AND_BACKEND_SHIFT;
    assert(itzabState < SIZE_M_RGTZAB);

    // Set up the span vectors from the master table
    assert((g_grdRenderData.dwState & MASTER_STATE_MASK) < SIZE_M_RGDTD);
    pbyte = &m_rgdtd[g_grdRenderData.dwState & MASTER_STATE_MASK].bSpanProcessor[0];

#if !defined(_FOG_)
    assert(m_rgtzab[itzabState].bBackend < SIZE_RGPSPANFNTRIVIALZANDBACKEND);
    rgpspanfn[SPAN_BACKEND] = rgpspanfnTrivialZAndBackend[m_rgtzab[itzabState].bBackend];

    for (i=0; i<NUM_TABLE_SPAN_VECTORS; i++)
    {
        assert(pbyte[i] <= MAX_SPAN_FN);
        g_grdChunkData.pspanfnWorking[i] = rgpspanfn[pbyte[i]];
    }

    rgpspanfnTrivialZAndBackend[TRIVIALZ_NOOP] = g_grdChunkData.pspanfnWorking[PSPANFN_AFTER_TRIVIALZ];
    assert(m_rgtzab[itzabState].bTrivialZ < SIZE_RGPSPANFNTRIVIALZANDBACKEND);
    g_grdRenderData.pspanfnEntry = rgpspanfnTrivialZAndBackend[m_rgtzab[itzabState].bTrivialZ];
#else
    // if fog is flat to 5 bits of accuracy, use faster flat path
    g_grdRenderData.fFogIsFlat = FALSE;

    DWORD dwF0;

    if ((dwF0 = FOG(g_grdRenderData.gvVertices[0].specular) >> 3) ==
            (FOG(g_grdRenderData.gvVertices[1].specular) >> 3))
    {
        if (dwF0 == (FOG(g_grdRenderData.gvVertices[2].specular) >> 3))
        {
            g_grdRenderData.fFogIsFlat = TRUE;
        }
    }

    if (g_grdRenderData.fFogIsFlat)
    {
        rgpspanfn[SPAN_BACKEND] = SpanFog_FlatIMul;
    }
    else
    {
        if (g_grdRenderData.dwState & STATE_MONO_FOG)
        {
            rgpspanfn[SPAN_BACKEND] = SpanFog_MonoGouraudIMul;
        }
        else
        {
            rgpspanfn[SPAN_BACKEND] = SpanFog_GouraudIMul;
        }
    }

    for (i=0; i<NUM_TABLE_SPAN_VECTORS; i++)
    {
        assert(pbyte[i] <= MAX_SPAN_FN);
        g_grdChunkData.pspanfnWorking[i] = rgpspanfn[pbyte[i]];
    }

    assert(m_rgtzab[itzabState].bBackend < SIZE_RGPSPANFNTRIVIALZANDBACKEND);
    g_grdChunkData.pspanfnWorking[PSPANFN_AFTER_FOG] =
            rgpspanfnTrivialZAndBackend[m_rgtzab[itzabState].bBackend];

    rgpspanfnTrivialZAndBackend[TRIVIALZ_NOOP] = g_grdChunkData.pspanfnWorking[PSPANFN_AFTER_TRIVIALZ];
    assert(m_rgtzab[itzabState].bTrivialZ < SIZE_RGPSPANFNTRIVIALZANDBACKEND);
    g_grdRenderData.pspanfnEntry = rgpspanfnTrivialZAndBackend[m_rgtzab[itzabState].bTrivialZ];
#endif

    DRAWN_COUNT(_DRAWN_COUNTER_, g_grdRenderData.dwState);
    REPORT_IF_NO_FAST_PATH();

#if defined(_FLAT_)
    if (!(g_grdRenderData.dwState & STATE_SHADEGOURAUD))
    {
        // set flat color
        // There are two forms, one used for RGBA shading and one used for IA shading.
        if (g_grdRenderData.dwState & STATE_SHADEI)
        {
            // IA shading stores separate right-justified I and A values
            g_grdChunkData.m1FlatI = FROM_888_TO_B(g_grdRenderData.gvVertices[0].color);
            g_grdChunkData.m1FlatA = FROM_8888_TO_ALPHA(g_grdRenderData.gvVertices[0].color);
        }
        else
        {
            // RGBA shading uses a shade color/alpha format in which GB is in g_grdChunkData.m2xFlatGB,
            // and AR is in g_grdChunkData.m2xFlatAR, so they're in the correct format for the 2 RGB
            // multiplies
            g_grdChunkData.m2xFlatGB = FROM_888_TO_GBX(g_grdRenderData.gvVertices[0].color);
            g_grdChunkData.m2xFlatAR = FROM_8888_TO_ARX(g_grdRenderData.gvVertices[0].color);
        }
    }
#endif // _FLAT_

#if !defined(_TEXMAP_)
    _INIT_FN_NAME_();
#endif  // !_TEXMAP_

    if (!g_grdRenderData.fMiddleIsRight)
    {
#if defined(_PERSPECTIVE_)
        _INIT_PERSP_FN_NAME_();
        _CREATEEDGE_PERSP_FN_NAME_(g_grdRenderData.iTop, g_grdRenderData.iMiddle);
#elif defined(_AFFINE_)
        _INIT_AFFINE_FN_NAME_();
        _CREATEEDGE_AFFINE_FN_NAME_(g_grdRenderData.iTop, g_grdRenderData.iMiddle);
#else   // not textured
        _CREATEEDGE_FN_NAME_(g_grdRenderData.iTop, g_grdRenderData.iMiddle);
#endif  // _PERSPECTIVE

        _CREATEOTHEREDGE_FN_NAME_(g_grdRenderData.iTop, g_grdRenderData.iBottom);

        g_grdRenderData.cyHeight = g_grdRenderData.geEdge.cy;
    }
    else
    {
#if defined(_PERSPECTIVE_)
        _INIT_PERSP_FN_NAME_();
        _CREATEEDGE_PERSP_FN_NAME_(g_grdRenderData.iTop, g_grdRenderData.iBottom);
#elif defined(_AFFINE_)
        _INIT_AFFINE_FN_NAME_();
        _CREATEEDGE_AFFINE_FN_NAME_(g_grdRenderData.iTop, g_grdRenderData.iBottom);
#else   // not textured
        _CREATEEDGE_FN_NAME_(g_grdRenderData.iTop, g_grdRenderData.iBottom);
#endif  // _PERSPECTIVE

        _CREATEOTHEREDGE_FN_NAME_(g_grdRenderData.iTop, g_grdRenderData.iMiddle);

        g_grdRenderData.cyHeight = g_grdRenderData.geOtherEdge.cy;
    }

    g_grdRenderData.pbScreenLine  = g_grdChunkData.pbScreen + g_grdRenderData.geEdge.y * g_grdChunkData.cbScreenPitch; 

#if defined(_ZBUFFER_)
    g_grdRenderData.pbZBufferLine = g_grdRenderData.pbZBuffer + g_grdRenderData.geEdge.y * g_grdRenderData.cbZBufferPitch;
    g_grdChunkData.ZIncr_Chunk = FloatToZBUFFER2(g_grdRenderData.dZdx);
#endif

#if defined(_TEXMAP_)
    assert(m_pwTexture != NULL);
#endif

    for (i=0; ; )
    {
        while (g_grdRenderData.cyHeight--)
        {
            g_grdRenderData.cPixels = g_grdRenderData.geEdge.x - g_grdRenderData.geOtherEdge.x;

            if (g_grdRenderData.cPixels < 0)
            {
                // do this efficiently
                g_grdChunkData.pPix_Chunk =
                        (WORD *)g_grdRenderData.pbScreenLine + (g_grdRenderData.geEdge.x << g_grdChunkData.cPixDouble);
                g_grdChunkData.pZ_Chunk = (WORD *)g_grdRenderData.pbZBufferLine + g_grdRenderData.geEdge.x;
                g_grdChunkData.dwZ_Chunk = FloatToZBUFFER2(g_grdRenderData.geEdge.Z);

#if defined(_AFFINE_)

	            g_grdChunkData.fix16u_Chunk = FloatToFixed16_16(g_grdRenderData.geEdge.u);
	            g_grdChunkData.fix16v_Chunk = FloatToFixed16_16(g_grdRenderData.geEdge.v);
                g_grdChunkData.fix16u_Chunk += g_grdRenderData.f16dudxModifier;
                g_grdChunkData.fix16v_Chunk += g_grdRenderData.f16dvdxModifier;

#elif defined(_PERSPECTIVE_)

                float       oow;
                float       uow;
                float       vow;
                FIX16_16    fix16uNext, fix16vNext;
                float       w;

                oow = g_grdRenderData.geEdge.oow;
                uow = g_grdRenderData.geEdge.uow;
                vow = g_grdRenderData.geEdge.vow;

                w = (float)1.0 / oow;

                fix16uNext = FloatToFixed16_16(uow * w);
                fix16vNext = FloatToFixed16_16(vow * w);

                fix16uNext += g_grdRenderData.f16dudxModifier;
                fix16vNext += g_grdRenderData.f16dvdxModifier;

                if (g_grdRenderData.cPixels < -MAX_SPAN_BLOCK_LEN)
                {
                    // the next chunk isn't the last, just blast ahead a full chunk
                    oow += g_grdRenderData.doowdx16;
                    uow += g_grdRenderData.duowdx16;
                    vow += g_grdRenderData.dvowdx16;
                }
                else
                {
                    // the next chunk is the last, calc the required
                    // length (but land right on the last pixel, because
                    // there may not even be valid u and v for pixels
                    // off the triangle
                    oow -= g_grdRenderData.doowdx * (g_grdRenderData.cPixels + 1);
                    uow -= g_grdRenderData.duowdx * (g_grdRenderData.cPixels + 1);
                    vow -= g_grdRenderData.dvowdx * (g_grdRenderData.cPixels + 1);
                }

#if USE_C
                w = (float)1.0 / oow;
#else
                // overlap FDIV with other work
                _asm
                {
                    fld  dword ptr [g_grdRenderData.Const1]
                    fdiv dword ptr [oow]
                }
#endif  // USE_C

#endif  // _PERSPECTIVE_

                FIX16_16 fix16Blue;
                FIX16_16 fix16Green;
                FIX16_16 fix16Red;
                FIX16_16 fix16Alpha;

                fix16Blue = g_grdRenderData.geEdge.f16Blue;
                fix16Green = g_grdRenderData.geEdge.f16Green;
                fix16Red = g_grdRenderData.geEdge.f16Red;
                fix16Alpha = g_grdRenderData.geEdge.f16Alpha;

#if defined(_FOG_)
                // rather than take the time to do the test to see if fog is flat, we'll
                // just do the fixed-point fog calcs regardless
                FIX16_16 fix16Fog = g_grdRenderData.geEdge.f16Fog;
#endif

                goto ChunkLoopEntry;

                do
                {
                    // code to advance to the next chunk, placed here so we can
                    // fall through at bottom of loop
                    g_grdChunkData.dwZ_Chunk += (g_grdChunkData.ZIncr_Chunk << 4);

#if defined(_AFFINE_)
                    g_grdChunkData.fix16u_Chunk += g_grdRenderData.fix16du_Chunk_X16;
                    g_grdChunkData.fix16v_Chunk += g_grdRenderData.fix16dv_Chunk_X16;
#endif  // _AFFINE_

                    fix16Blue += g_grdRenderData.f16dBdx << 4;
                    fix16Green += g_grdRenderData.f16dGdx << 4;
                    fix16Red += g_grdRenderData.f16dRdx << 4;
                    fix16Alpha += g_grdRenderData.f16dAdx << 4;

                    assert(((fix16Blue >> 19) >= 0) && ((fix16Blue >> 19) <= 0x1F));
                    assert((g_grdRenderData.dwState & STATE_SHADEI) | (((fix16Green >> 19) >= 0) && ((fix16Green >> 19) <= 0x1F)));
                    assert((g_grdRenderData.dwState & STATE_SHADEI) | (((fix16Red >> 19) >= 0) && ((fix16Red >> 19) <= 0x1F)));
                    assert(!(g_grdRenderData.dwState & STATE_SHADEALPHA) | (((fix16Alpha >> 19) >= 0) && ((fix16Alpha >> 19) <= 0x1F)));

#if defined(_FOG_)
                    // rather than take the time to do the test to see if fog is flat, we'll
                    // just do the fixed-point fog calcs regardless
                    fix16Fog += g_grdRenderData.f16dFdx << 4;
                    assert(g_grdRenderData.fFogIsFlat | (((fix16Fog >> 19) >= 0) && ((fix16Fog >> 19) <= 0x1F)));
#endif

ChunkLoopEntry:
#if defined(_TEXMAP_)
                    DWORD dwBlue, dwGreen, dwRed;

                    dwBlue = fix16Blue >> 14;
                    g_grdChunkData.gi_Chunk = BUILD_GI(dwBlue);

                    if (!(g_grdRenderData.dwState & STATE_SHADEI))
                    {
                        dwGreen = fix16Green >> 14;
                        dwRed = fix16Red >> 14;

                        g_grdChunkData.grb_Chunk = BUILD_GRB(dwRed, dwBlue);
                        g_grdChunkData.ggb_Chunk = BUILD_GGB(dwGreen, dwBlue);
                        g_grdChunkData.grgb_Chunk = BUILD_GRGB(dwRed, dwGreen, dwBlue);

                        if (!(g_grdRenderData.dwState & STATE_SHADEALPHA))
                        {
                            g_grdChunkData.gag_Chunk = BUILD_GG(dwGreen);
                            g_grdChunkData.gar_Chunk = BUILD_GR(dwRed);
                        }
                    }

                    if (g_grdRenderData.dwState & STATE_SHADEALPHA)
                    {
                        DWORD dwAlpha = fix16Alpha >> 14;

                        g_grdChunkData.gag_Chunk = BUILD_GAG(dwAlpha, dwGreen);
                        g_grdChunkData.ga_Chunk = BUILD_GA(fix16Alpha);
                        g_grdChunkData.gar_Chunk = BUILD_GAR(dwAlpha, dwRed);
                    }
#else   // !_TEXMAP_
                    DWORD dwBlue = fix16Blue >> 14;
                    DWORD dwGreen = fix16Green >> 14;
                    DWORD dwRed = fix16Red >> 14;
                    g_grdChunkData.grb_Chunk = BUILD_GRB(dwRed, dwBlue);
                    g_grdChunkData.grgb_Chunk = BUILD_GRGB(dwRed, dwGreen, dwBlue);

                    if (g_grdRenderData.dwState & STATE_SHADEALPHA)
                    {
                        DWORD dwAlpha = fix16Alpha >> 14;
                        g_grdChunkData.gag_Chunk = BUILD_GAG(dwAlpha, dwGreen);
                    }
                    else
                    {
                        g_grdChunkData.gag_Chunk = BUILD_GG(dwGreen);
                    }
#endif  // _TEXMAP_

#if defined(_FOG_)
                    // rather than take the time to do the test to see if fog is flat, we'll
                    // just do the fixed-point fog calcs regardless
                    g_grdChunkData.giFog_Chunk = BUILD_GI(fix16Fog >> 14);
#endif

                    g_grdChunkData.cPixels_Chunk = g_grdRenderData.cPixels;

#if defined(_PERSPECTIVE_)

                    g_grdChunkData.fix16u_Chunk = fix16uNext;
                    g_grdChunkData.fix16v_Chunk = fix16vNext;

#if !USE_C
                    // pick up results from overlapped FDIV
                    _asm
                    {
                        fstp dword ptr [w]
                    }
#endif  // USE_C

	                fix16uNext = FloatToFixed16_16(uow * w);
	                fix16vNext = FloatToFixed16_16(vow * w);

                    fix16uNext += g_grdRenderData.f16dudxModifier;
                    fix16vNext += g_grdRenderData.f16dvdxModifier;

                    if (g_grdRenderData.cPixels < -MAX_SPAN_BLOCK_LEN)
                    {
                        g_grdChunkData.cPixels_Chunk = -MAX_SPAN_BLOCK_LEN;

                        // use a shift to divide for max chunks
                        g_grdChunkData.fix16du_Chunk =
                                (fix16uNext - g_grdChunkData.fix16u_Chunk) >> MAX_SPAN_BLOCK_SHIFT;
                        g_grdChunkData.fix16dv_Chunk =
                                (fix16vNext - g_grdChunkData.fix16v_Chunk) >> MAX_SPAN_BLOCK_SHIFT;

                        // calculate w for next time (potentially FDIV can overlap)
                        int cNextChunk = g_grdRenderData.cPixels - g_grdChunkData.cPixels_Chunk;

                        if (cNextChunk < -MAX_SPAN_BLOCK_LEN)
                        {
                            // the next chunk isn't the last, just blast ahead a full chunk
                            oow += g_grdRenderData.doowdx16;
                            uow += g_grdRenderData.duowdx16;
                            vow += g_grdRenderData.dvowdx16;
                        }
                        else
                        {
                            // the next chunk is the last, calc the required
                            // length (but land right on the last pixel, because
                            // there may not even be valid u and v for pixels
                            // off the triangle
                            oow -= g_grdRenderData.doowdx * (cNextChunk + 1);
                            uow -= g_grdRenderData.duowdx * (cNextChunk + 1);
                            vow -= g_grdRenderData.dvowdx * (cNextChunk + 1);
                        }

#if USE_C
                        w = (float)1.0 / oow;
#else
                        // overlap FDIV with other work
                        _asm
                        {
                            fld  dword ptr [g_grdRenderData.Const1]
                            fdiv dword ptr [oow]
                        }
#endif  // USE_C
                    }
                    else
                    {
                        // we have to divide the variable-length last chunk
                        // -g_grdRenderData.cPixels - 1 because for the last chunk, the
                        // end of the span is on the last pixel, not one pixel
                        // after it, as is the case for full chunks
                        if (g_grdRenderData.cPixels >= -2)
                        {
                            if (g_grdRenderData.cPixels == -2)
                            {
                                g_grdChunkData.fix16du_Chunk = (fix16uNext - g_grdChunkData.fix16u_Chunk);
                                g_grdChunkData.fix16dv_Chunk = (fix16vNext - g_grdChunkData.fix16v_Chunk);
                            }
                        }
                        else
                        {
#if USE_C
                            g_grdChunkData.fix16du_Chunk =
                                    (g_grdChunkData.fix16u_Chunk - fix16uNext) / (g_grdRenderData.cPixels + 1);
                            g_grdChunkData.fix16dv_Chunk =
                                    (g_grdChunkData.fix16v_Chunk - fix16vNext) / (g_grdRenderData.cPixels + 1);
#else
                            assert(g_grdRenderData.cPixels >= -16);

                            _asm {
                                mov ecx,[g_grdRenderData.cPixels]
                                mov eax,[fix16uNext]
                                sub eax,[g_grdChunkData.fix16u_Chunk]
                                jns  X1
                                neg eax
                                mul [Div_2_15_Table+16*4+ecx*4]
                                neg edx
                                jmp X2
X1:
                                mul [Div_2_15_Table+16*4+ecx*4]
X2:
                                mov [g_grdChunkData.fix16du_Chunk],edx

                                mov eax,[fix16vNext]
                                sub eax,[g_grdChunkData.fix16v_Chunk]
                                jns  X3
                                neg eax
                                mul [Div_2_15_Table+16*4+ecx*4]
                                neg edx
                                jmp X4
X3:
                                mul [Div_2_15_Table+16*4+ecx*4]
X4:
                                mov [g_grdChunkData.fix16dv_Chunk],edx
                            }
#endif  // USE_C
                        }
                    }

#else

                    if (g_grdRenderData.cPixels < -MAX_SPAN_BLOCK_LEN)
                        g_grdChunkData.cPixels_Chunk = -MAX_SPAN_BLOCK_LEN;

#endif  // _PERSPECTIVE_

                    // point to rightmost pixel of span
                    g_grdChunkData.pPix_Chunk -= g_grdChunkData.cPixels_Chunk << g_grdChunkData.cPixDouble;
                    g_grdChunkData.pZ_Chunk -= g_grdChunkData.cPixels_Chunk;

                    DRAW_SPAN();

                    g_grdRenderData.cPixels -= g_grdChunkData.cPixels_Chunk;
                } while (g_grdRenderData.cPixels < 0);
            }

            // only happens when height==0 and i==1
            if (g_grdRenderData.cyHeight < i)
                goto Done;

#if defined(_PERSPECTIVE_)
            _STEPEDGE_PERSP_FN_NAME_();
#elif defined(_AFFINE_)
            _STEPEDGE_AFFINE_FN_NAME_();
#else
            _STEPEDGE_FN_NAME_();
#endif  // _PERSPECTIVE_

            #include "..\inc\stepotheredge.h"

            g_grdRenderData.pbScreenLine  += g_grdChunkData.cbScreenPitch;
            g_grdRenderData.pbZBufferLine += g_grdRenderData.cbZBufferPitch;
        }

        // if the top two edges were the same length, there's no bottom and we're done
        if (g_grdRenderData.geEdge.cy == g_grdRenderData.geOtherEdge.cy)
            break;

        i++;

        if (!g_grdRenderData.fMiddleIsRight)
        {
#if defined(_PERSPECTIVE_)
            _CREATEEDGE_PERSP_FN_NAME_(g_grdRenderData.iMiddle, g_grdRenderData.iBottom);
#elif defined(_AFFINE_)
            _CREATEEDGE_AFFINE_FN_NAME_(g_grdRenderData.iMiddle, g_grdRenderData.iBottom);
#else   // not textured
            _CREATEEDGE_FN_NAME_(g_grdRenderData.iMiddle, g_grdRenderData.iBottom);
#endif  // _PERSPECTIVE_

            g_grdRenderData.cyHeight = g_grdRenderData.geEdge.cy;
        }
        else
        {
            _CREATEOTHEREDGE_FN_NAME_(g_grdRenderData.iMiddle, g_grdRenderData.iBottom);

            g_grdRenderData.cyHeight = g_grdRenderData.geOtherEdge.cy;
        }
    }
Done:;

#if defined(_TEXMAP_)
#undef _TEXMAP_
#endif

