//----------------------------------------------------------------------------
//
// point.cpp
//
// Point processing.
//
// Copyright (C) Microsoft Corporation, 1997.
//
//----------------------------------------------------------------------------

#include "pch.cpp"
#pragma hdrstop

DBG_DECLARE_FILE();

void
PrimProcessor::FillPointSpan(LPD3DTLVERTEX pV0, PD3DI_RASTSPAN pSpan)
{
    FLOAT fZ;
    FLOAT fZScale;

    pSpan->uPix = 1;
    pSpan->uX = (UINT16)m_StpCtx.iX;
    pSpan->uY = (UINT16)m_StpCtx.iY;

    pSpan->pSurface = m_StpCtx.pCtx->pSurfaceBits +
        m_StpCtx.iX * m_StpCtx.pCtx->iSurfaceStep +
        m_StpCtx.iY * m_StpCtx.pCtx->iSurfaceStride;

    if (m_StpCtx.uFlags & PRIMSF_Z_USED)
    {
        pSpan->pZ = m_StpCtx.pCtx->pZBits +
            m_StpCtx.iX * m_StpCtx.pCtx->iZStep +
            m_StpCtx.iY * m_StpCtx.pCtx->iZStride;

        if (m_StpCtx.pCtx->iZBitCount == 16)
        {
            fZScale = Z16_SCALE;
        }
        else
        {
            fZScale = Z32_SCALE;
        }

        // fZ may be used later so set if from the vertex Z.
        fZ = pV0->dvSZ;
        pSpan->uZ = FTOI(fZ * fZScale);
    }

    if (m_StpCtx.uFlags & PRIMSF_TEX_USED)
    {
        FLOAT fW;
        FLOAT fUoW, fVoW;

        // Mipmapping doesn't have any meaning.
        RSASSERT((m_StpCtx.uFlags & PRIMSF_LOD_USED) == 0);

        if (m_StpCtx.uFlags & PRIMSF_PERSP_USED)
        {
            if (FLOAT_EQZ(pV0->dvRHW))
            {
                fW = g_fZero;
            }
            else
            {
                fW = g_fOne / pV0->dvRHW;
            }

            pSpan->iW = FTOI(fW * W_SCALE);

            fUoW = pV0->dvTU * pV0->dvRHW;
            fVoW = pV0->dvTV * pV0->dvRHW;

            pSpan->iOoW = FTOI(pV0->dvRHW * OOW_SCALE);
        }
        else
        {
            fUoW = pV0->dvTU;
            fVoW = pV0->dvTV;
        }

        pSpan->iLOD = 0;
        pSpan->iDLOD = 0;

        pSpan->iUoW1 = FTOI(fUoW * TEX_SCALE);
        pSpan->iVoW1 = FTOI(fVoW * TEX_SCALE);
    }

    if (m_StpCtx.uFlags & PRIMSF_TEX2_USED)
    {
        if (m_StpCtx.uFlags & PRIMSF_PERSP_USED)
        {
            pSpan->iUoW2 =
                FTOI(((PRAST_GENERIC_VERTEX)pV0)->dvTU2 *
                     pV0->dvRHW * TEX_SCALE);
            pSpan->iVoW2 =
                FTOI(((PRAST_GENERIC_VERTEX)pV0)->dvTV2 *
                     pV0->dvRHW * TEX_SCALE);
        }
        else
        {
            pSpan->iUoW2 =
                FTOI(((PRAST_GENERIC_VERTEX)pV0)->dvTU2 * TEX_SCALE);
            pSpan->iVoW2 =
                FTOI(((PRAST_GENERIC_VERTEX)pV0)->dvTV2 * TEX_SCALE);
        }
    }

    if (m_StpCtx.uFlags & PRIMSF_DIFF_USED)
    {
        pSpan->uB = (UINT)RGBA_GETBLUE(m_StpCtx.pFlatVtx->dcColor) <<
            COLOR_SHIFT;
        pSpan->uG = (UINT)RGBA_GETGREEN(m_StpCtx.pFlatVtx->dcColor) <<
            COLOR_SHIFT;
        pSpan->uR = (UINT)RGBA_GETRED(m_StpCtx.pFlatVtx->dcColor) <<
            COLOR_SHIFT;
        pSpan->uA = (UINT)RGBA_GETALPHA(m_StpCtx.pFlatVtx->dcColor) <<
            COLOR_SHIFT;
    }
    else if (m_StpCtx.uFlags & PRIMSF_DIDX_USED)
    {
        pSpan->iIdx = (INT32)CI_MASKALPHA(m_StpCtx.pFlatVtx->dcColor) <<
            INDEX_COLOR_FIXED_SHIFT;
        pSpan->iIdxA = (INT32)CI_GETALPHA(m_StpCtx.pFlatVtx->dcColor) <<
            INDEX_COLOR_SHIFT;
    }

    if (m_StpCtx.uFlags & PRIMSF_SPEC_USED)
    {
        pSpan->uBS = (UINT)RGBA_GETBLUE(m_StpCtx.pFlatVtx->dcSpecular) <<
            COLOR_SHIFT;
        pSpan->uGS = (UINT)RGBA_GETGREEN(m_StpCtx.pFlatVtx->dcSpecular) <<
            COLOR_SHIFT;
        pSpan->uRS = (UINT)RGBA_GETRED(m_StpCtx.pFlatVtx->dcSpecular) <<
            COLOR_SHIFT;
    }

    if (m_StpCtx.uFlags & PRIMSF_LOCAL_FOG_USED)
    {
        if (m_StpCtx.uFlags & PRIMSF_GLOBAL_FOG_USED)
        {
            // Make sure that fZ has been set.
            RSASSERT(m_StpCtx.uFlags & PRIMSF_Z_USED);

            pSpan->uFog = (UINT16)ComputeTableFog(m_StpCtx.pCtx->pdwRenderState, fZ);
        }
        else
        {
            pSpan->uFog = (INT16)
                FTOI((FLOAT)RGBA_GETALPHA(pV0->dcSpecular) *
                     FOG_255_SCALE);
        }
    }
}

// Determine whether any of the given values are less than zero or greater
// than one.  Negative zero counts as less than zero so this check will
// produce some false positives but that's OK.
#define NEEDS_NORMALIZE1(fV0) \
    (ASUINT32(fV0) > INT32_FLOAT_ONE)

//----------------------------------------------------------------------------
//
// PrimProcessor::NormalizePointRHW
//
// D3DTLVERTEX.dvRHW can be anything, but our internal structures only
// allow for it being in the range [0, 1].  This function clamps
// the RHW to the proper range.
//
//----------------------------------------------------------------------------

void
PrimProcessor::NormalizePointRHW(LPD3DTLVERTEX pV0)
{
    // Save original value.
    m_dvV0RHW = pV0->dvRHW;

    // Produce a warning when a value is out of the desired range.
#if DBG
    if (FLOAT_LTZ(pV0->dvRHW))
    {
        RSDPF(("Point RHW out of range %f,%f",
               pV0->dvRHW));
    }
#endif

    if (pV0->dvRHW < NORMALIZED_RHW_MIN)
    {
        pV0->dvRHW = NORMALIZED_RHW_MIN;
    }
    else if (pV0->dvRHW > NORMALIZED_RHW_MAX)
    {
        pV0->dvRHW = NORMALIZED_RHW_MAX;
    }
}

//----------------------------------------------------------------------------
//
// PrimProcessor::Point
//
// Provides a point for processing.
//
//----------------------------------------------------------------------------

HRESULT
PrimProcessor::Point(LPD3DTLVERTEX pV0,
                     LPD3DTLVERTEX pFlatVtx)
{
    HRESULT hr;

    hr = D3D_OK;

#if DBG
    hr = ValidateVertex(pV0);
    if (hr != D3D_OK)
    {
        return hr;
    }
#endif

    // Clear per-point flags.
    m_StpCtx.uFlags &= ~(PRIMF_ALL | PTF_ALL);

    RSDPFM((RSM_FLAGS, "m_uPpFlags: 0x%08X, m_StpCtx.uFlags: 0x%08X",
            m_uPpFlags, m_StpCtx.uFlags));

    // Round coordinates to integer.
    m_StpCtx.iX = IFLOORF(pV0->dvSX + g_fHalf);
    m_StpCtx.iY = IFLOORF(pV0->dvSY + g_fHalf);

    RSDPFM((RSM_POINTS, "Point\n"));
    RSDPFM((RSM_POINTS, "    V0 (%f,%f,%f) (%d,%d)\n",
            pV0->dvSX, pV0->dvSY, pV0->dvSZ,
            m_StpCtx.iX, m_StpCtx.iY));

    // Clip test.
    if (m_StpCtx.iX < m_StpCtx.pCtx->Clip.left ||
        m_StpCtx.iX >= m_StpCtx.pCtx->Clip.right ||
        m_StpCtx.iY < m_StpCtx.pCtx->Clip.top ||
        m_StpCtx.iY >= m_StpCtx.pCtx->Clip.bottom)
    {
        return D3D_OK;
    }

    //
    // Fill out a one-pixel span for the point.
    // Since the prim deltas are irrelevant for the span,
    // the span is appended to whatever primitive happens
    // to be available in the buffer.
    //

    PD3DI_RASTSPAN pSpan;
    UINT cSpans = 1;

    hr = AppendPrim();
    if (hr != D3D_OK)
    {
        return hr;
    }

    hr = AllocSpans(&cSpans, &pSpan);
    if (hr != D3D_OK)
    {
        return hr;
    }

    m_StpCtx.pPrim->uSpans++;

    BOOL bNorm;

    // USED checks cannot be combined since TEX_USED is a multibit check.
    if ((m_StpCtx.uFlags & PRIMSF_TEX_USED) &&
        (m_StpCtx.uFlags & PRIMSF_PERSP_USED) &&
        (m_uPpFlags & PPF_NORMALIZE_RHW) &&
        NEEDS_NORMALIZE1(pV0->dvRHW))
    {
        NormalizePointRHW(pV0);
        bNorm = TRUE;
    }
    else
    {
        bNorm = FALSE;
    }

    // Remember flat color controlling vertex for setup.
    m_StpCtx.pFlatVtx = pFlatVtx;

    FillPointSpan(pV0, pSpan);

    if (bNorm)
    {
        pV0->dvRHW = m_dvV0RHW;
    }

    return D3D_OK;
}