//----------------------------------------------------------------------------
//
// primproc.cpp
//
// Miscellaneous PrimProcessor methods.
//
// Copyright (C) Microsoft Corporation, 1997.
//
//----------------------------------------------------------------------------

#include "rgb_pch.h"
#pragma hdrstop

#include "d3dutil.h"
#include "setup.hpp"
#include "attrs_mh.h"
#include "tstp_mh.h"
#include "walk_mh.h"
#include "rsdbg.hpp"

DBG_DECLARE_FILE();

//----------------------------------------------------------------------------
//
// PrimProcessor::BeginPrimSet
//
// Marks the start of a set of primitives that have the same vertex type.
// Computes attributes used from the current state and the vertex type.
//
//----------------------------------------------------------------------------

void
PrimProcessor::BeginPrimSet(D3DPRIMITIVETYPE PrimType,
                            RAST_VERTEX_TYPE VertType)
{
    // If state hasn't changed and the primitive and vertex types match the
    // ones we're already set up for there's no work to do.
    if ((m_uPpFlags & PPF_STATE_CHANGED) == 0 &&
        VertType == m_VertType &&
        PrimType == m_PrimType)
    {
        return;
    }

    m_StpCtx.uFlags &= ~PRIMSF_ALL;

    if (m_StpCtx.pCtx->pdwRenderState[D3DRS_ZENABLE] ||
        m_StpCtx.pCtx->pdwRenderState[D3DRS_ZWRITEENABLE] ||
        m_StpCtx.pCtx->pdwRenderState[D3DRS_STENCILENABLE])
    {
        m_StpCtx.uFlags |= PRIMSF_Z_USED;
    }

    /*if (m_StpCtx.pCtx->BeadSet == D3DIBS_RAMP)
    {
        // Index is unused during copy mode texturing.
        if (m_StpCtx.pCtx->pdwRenderState
            [D3DRENDERSTATE_TEXTUREMAPBLEND] != D3DTBLEND_COPY ||
            m_StpCtx.pCtx->cActTex == 0)
        {
            m_StpCtx.uFlags |= PRIMSF_DIDX_USED;
        }
    }
    else */
    {
        // ATTENTION - Don't set these for copy mode texture?  Is
        // copy mode texture meaningful in RGB?
        m_StpCtx.uFlags |= PRIMSF_DIFF_USED;
        if (m_StpCtx.pCtx->pdwRenderState[D3DRS_SPECULARENABLE])
        {
            m_StpCtx.uFlags |= PRIMSF_SPEC_USED;
        }
    }

    if (m_StpCtx.pCtx->pdwRenderState[D3DRS_SHADEMODE] ==
        D3DSHADE_FLAT)
    {
        m_StpCtx.uFlags |= PRIMSF_FLAT_SHADED;
    }

    if (m_StpCtx.pCtx->cActTex > 0)
    {
        m_StpCtx.uFlags |= PRIMSF_TEX1_USED;

        if (m_StpCtx.pCtx->cActTex > 1)
        {
            m_StpCtx.uFlags |= PRIMSF_TEX2_USED;
        }
    }

    if ((m_StpCtx.uFlags & PRIMSF_TEX_USED) &&
        (true || m_StpCtx.pCtx->pdwRenderState[D3DRENDERSTATE_TEXTUREPERSPECTIVE]))
    {
        m_StpCtx.uFlags |= PRIMSF_PERSP_USED;
    }

    // Currently only tex1 can be mipmapped.
    if (((m_StpCtx.uFlags & PRIMSF_TEX1_USED) &&
        (PrimType == D3DPT_TRIANGLELIST ||
         PrimType == D3DPT_TRIANGLESTRIP ||
         PrimType == D3DPT_TRIANGLEFAN) &&
        (m_StpCtx.pCtx->pdwRenderState[D3DRS_FILLMODE]
         == D3DFILL_SOLID)) &&

        (((m_StpCtx.pCtx->pTexture[0]->cLOD >= 1) &&
        (m_StpCtx.pCtx->pTexture[0]->uMipFilter != D3DTFP_NONE)) ||
        // need LOD if we need to dynamically switch between different min
        // and mag filters
        (m_StpCtx.pCtx->pTexture[0]->uMinFilter !=
         m_StpCtx.pCtx->pTexture[0]->uMagFilter)))
    {
        m_StpCtx.uFlags |= PRIMSF_LOD_USED;
    }

    // select between min and mag filters for TEX2
    if (((m_StpCtx.uFlags & PRIMSF_TEX2_USED) &&
        (PrimType == D3DPT_TRIANGLELIST ||
         PrimType == D3DPT_TRIANGLESTRIP ||
         PrimType == D3DPT_TRIANGLEFAN) &&
        (m_StpCtx.pCtx->pdwRenderState[D3DRS_FILLMODE]
         == D3DFILL_SOLID)) &&

        (((m_StpCtx.pCtx->pTexture[1]->cLOD >= 1) &&
        (m_StpCtx.pCtx->pTexture[1]->uMipFilter != D3DTFP_NONE)) ||
        // need LOD if we need to dynamically switch between different min
        // and mag filters
        (m_StpCtx.pCtx->pTexture[1]->uMinFilter !=
         m_StpCtx.pCtx->pTexture[1]->uMagFilter)))
    {
        m_StpCtx.uFlags |= PRIMSF_LOD_USED;
    }

    if (m_StpCtx.pCtx->pdwRenderState[D3DRS_FOGENABLE])
    {
        // Note, if PWL_FOG is ever brought back to life, enabling
        // PRIMSF_GLOBAL_FOG_USED with no Z buffer will not trivially work
        // if (m_StpCtx.uFlags & PRIMSF_Z_USED)
        {
            switch (m_StpCtx.pCtx->pdwRenderState[D3DRS_FOGTABLEMODE])
            {
            case D3DFOG_EXP:
            case D3DFOG_EXP2:
            case D3DFOG_LINEAR:
                m_StpCtx.uFlags |= PRIMSF_GLOBAL_FOG_USED;
#ifndef PWL_FOG
                // The span routines don't support table fog directly.
                // Instead table fog is computed per vertex and used to
                // set up local fog.
                m_StpCtx.uFlags |= PRIMSF_LOCAL_FOG_USED;
#endif
                break;
            default:
                m_StpCtx.uFlags |= PRIMSF_LOCAL_FOG_USED;
                break;
            }
        }
    }

    PFN_ADDATTRS *ppfnAddAttrsTable;
    PFN_ADDSCALEDATTRS *ppfnAddScaledAttrsTable;
    PFN_FILLSPANATTRS *ppfnFillSpanAttrsTable;

    if (m_StpCtx.pCtx->BeadSet == D3DIBS_RAMP)
    {
        // Ramp does not support multitexture.
        RSASSERT((m_StpCtx.uFlags & PRIMSF_TEX2_USED) == 0);

        RSASSERT((PRIMSF_TEX1_USED | PRIMSF_DIDX_USED) == 0x14);

        // Derive a function table index from bits 2 and 4 of usage
        // information.
        // An alternative method would be to use bits 0-4 and have the
        // ramp information in the top 16 entries, but splitting the
        // ramp and RGB tables is cleaner and decouples the table sizes.
        // Decoupling is useful since the ramp possibilities are much
        // more limited so its table can be smaller.

        m_iAttrFnIdx =
            ((m_StpCtx.uFlags & PRIMSF_TEX1_USED) >> 2) |
            ((m_StpCtx.uFlags & PRIMSF_DIDX_USED) >> 3);

        ppfnAddAttrsTable = g_pfnRampAddFloatAttrsTable;
        ppfnAddScaledAttrsTable = g_pfnRampAddScaledFloatAttrsTable;
        ppfnFillSpanAttrsTable = g_pfnRampFillSpanFloatAttrsTable;
    }
    else
    {
        RSASSERT((PRIMSF_DIFF_USED | PRIMSF_SPEC_USED | PRIMSF_TEX1_USED |
                  PRIMSF_TEX2_USED) == 0xf);

        // Derive a function table index from the lower four bits of
        // usage information.  The lower bits are deliberately chosen
        // to represent the more performance-sensitive cases while
        // the upper bits generally represent cases handled by generic
        // code.
        //
        // Even restricted to only four bits the index contains unimportant
        // and unreachable cases, such as specular without diffuse or
        // tex2 without tex1.  Tables indexed must account for this.

        m_iAttrFnIdx = m_StpCtx.uFlags & (PRIMSF_DIFF_USED | PRIMSF_SPEC_USED |
                                          PRIMSF_TEX1_USED | PRIMSF_TEX2_USED);

        ppfnAddAttrsTable = g_pfnAddFloatAttrsTable;
        ppfnAddScaledAttrsTable = g_pfnAddScaledFloatAttrsTable;
        ppfnFillSpanAttrsTable = g_pfnFillSpanFloatAttrsTable;
    }

    //
    // These functions only depend on the index and so can be set here.
    // Other functions depend on per-triangle information and are set
    // later.
    //

    if ((m_StpCtx.uFlags & PRIMSF_SLOW_USED) != PRIMSF_Z_USED)
    {
        // If any slow attrs are on or Z is off use the general functions.
        m_StpCtx.pfnAddScaledAttrs = AddScaledFloatAttrs_Any_Either;
#ifndef STEP_FIXED
        m_StpCtx.pfnAddAttrs = AddFloatAttrs_Any;
        m_StpCtx.pfnFillSpanAttrs = FillSpanFloatAttrs_Any_Either;
#endif
    }
    else
    {
        m_StpCtx.pfnAddScaledAttrs =
            ppfnAddScaledAttrsTable[m_iAttrFnIdx];
#ifndef STEP_FIXED
        m_StpCtx.pfnAddAttrs = ppfnAddAttrsTable[m_iAttrFnIdx];
        m_StpCtx.pfnFillSpanAttrs = ppfnFillSpanAttrsTable[m_iAttrFnIdx];
#endif
    }

    // Attribute beads can be set here.
    PFN_SETUPTRIATTR *ppfnSlot;

    ppfnSlot = &m_StpCtx.pfnTriSetupFirstAttr;
    if (m_StpCtx.uFlags & PRIMSF_Z_USED)
    {
        if (m_StpCtx.pCtx->iZBitCount == 16)
        {
            *ppfnSlot = TriSetup_Z16;
        }
        else
        {
            *ppfnSlot = TriSetup_Z32;
        }
        ppfnSlot = &m_StpCtx.pfnTriSetupZEnd;
    }
    if (m_StpCtx.uFlags & PRIMSF_TEX1_USED)
    {
        if (m_StpCtx.uFlags & PRIMSF_PERSP_USED)
        {
            *ppfnSlot = TriSetup_Persp_Tex;
        }
        else
        {
            *ppfnSlot = TriSetup_Affine_Tex;
        }
        ppfnSlot = &m_StpCtx.pfnTriSetupTexEnd;
    }
    if (m_StpCtx.uFlags & PRIMSF_DIFF_USED)
    {
        if (m_StpCtx.uFlags & PRIMSF_FLAT_SHADED)
        {
            *ppfnSlot = TriSetup_DiffFlat;
        }
        else
        {
            *ppfnSlot = TriSetup_Diff;
        }
        ppfnSlot = &m_StpCtx.pfnTriSetupDiffEnd;
    }
    else if (m_StpCtx.uFlags & PRIMSF_DIDX_USED)
    {
        if (m_StpCtx.uFlags & PRIMSF_FLAT_SHADED)
        {
            *ppfnSlot = TriSetup_DIdxFlat;
        }
        else
        {
            *ppfnSlot = TriSetup_DIdx;
        }
        ppfnSlot = &m_StpCtx.pfnTriSetupDiffEnd;
    }
    if (m_StpCtx.uFlags & PRIMSF_SPEC_USED)
    {
        if (m_StpCtx.uFlags & PRIMSF_FLAT_SHADED)
        {
            *ppfnSlot = TriSetup_SpecFlat;
        }
        else
        {
            *ppfnSlot = TriSetup_Spec;
        }
        ppfnSlot = &m_StpCtx.pfnTriSetupSpecEnd;
    }
    if (m_StpCtx.uFlags & PRIMSF_LOCAL_FOG_USED)
    {
        *ppfnSlot = TriSetup_Fog;
        ppfnSlot = &m_StpCtx.pfnTriSetupFogEnd;
    }
    *ppfnSlot = TriSetup_End;

    // Remember the primitive and vertex type and clear the state change bit.
    m_PrimType = PrimType;
    m_VertType = VertType;
    m_uPpFlags &= ~PPF_STATE_CHANGED;
}