///////////////////////////////////////////////////////////////////////////////
// Copyright (C) Microsoft Corporation, 1998.
//
// AttrFunc.cpp
//
// Direct3D Reference Rasterizer - Attribute Function Processing
//
///////////////////////////////////////////////////////////////////////////////
#include "pch.cpp"
#pragma hdrstop


//-----------------------------------------------------------------------------
//
// WrapDiff - returns the difference (B-A) as defined under the D3D WRAPU/V
// rules which is the shortest path between the two assuming a coincident
// position at 1. and 0.  The fA and fB input range is 0. to 1.
//
//-----------------------------------------------------------------------------
static FLOAT
WrapDiff( FLOAT fB, FLOAT fA )
{
    // compute straight distance
    FLOAT fDist1 = fB - fA;
    // compute distance 'warping' between 0. and 1.
    FLOAT fDist2 = ( fDist1 < 0 ) ? ( fDist1+1 ) : ( fDist1-1 );

    // return minimum of these
    return ( fabs( fDist1) < fabs( fDist2) ) ? ( fDist1) : ( fDist2 );
}


///////////////////////////////////////////////////////////////////////////////
//
// RRAttribFuncStatic - Attribute function data which is shared by all
// attributes and contains per-primitive and per-pixel data.  Cannot use static
// data members in RRAttribFunc class because there can be multiple instances
// of rasterizer object.
//
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
//
// SetPerTriangleData - Called once per triangle during setup to set per-triangle
// data used to compute attribute functions.
//
//-----------------------------------------------------------------------------
void
RRAttribFuncStatic::SetPerTriangleData(
    FLOAT fX0, FLOAT fY0, FLOAT fRHW0,
    FLOAT fX1, FLOAT fY1, FLOAT fRHW1,
    FLOAT fX2, FLOAT fY2, FLOAT fRHW2,
    INT32 cTextureStages,
    FLOAT* pfRHQW,
    FLOAT fDet )
{
    m_PrimType = RR_TRIANGLE;

    // compute fixed point x,y coords snapped to n.4 with nearest-even round
    INT32 iX0 = AS_INT32( (DOUBLE)fX0 + DOUBLE_4_SNAP );
    INT32 iY0 = AS_INT32( (DOUBLE)fY0 + DOUBLE_4_SNAP );
    INT32 iX1 = AS_INT32( (DOUBLE)fX1 + DOUBLE_4_SNAP );
    INT32 iY1 = AS_INT32( (DOUBLE)fY1 + DOUBLE_4_SNAP );
    INT32 iX2 = AS_INT32( (DOUBLE)fX2 + DOUBLE_4_SNAP );
    INT32 iY2 = AS_INT32( (DOUBLE)fY2 + DOUBLE_4_SNAP );
    fX0 = (FLOAT)iX0 * 1.0F/16.0F;
    fY0 = (FLOAT)iY0 * 1.0F/16.0F;
    fX1 = (FLOAT)iX1 * 1.0F/16.0F;
    fY1 = (FLOAT)iY1 * 1.0F/16.0F;
    fX2 = (FLOAT)iX2 * 1.0F/16.0F;
    fY2 = (FLOAT)iY2 * 1.0F/16.0F;

    m_fX0 = fX0;
    m_fY0 = fY0;
    m_cTextureStages = cTextureStages;

    m_fRHW0 = fRHW0;
    m_fRHW1 = fRHW1;
    m_fRHW2 = fRHW2;

    m_fDelX10 = fX1 - fX0;
    m_fDelX02 = fX0 - fX2;
    m_fDelY01 = fY0 - fY1;
    m_fDelY20 = fY2 - fY0;

    // compute inverse determinant
    m_fTriOODet = 1.f/fDet;

    // compute linear function for 1/W (for perspective correction)

    // compute linear deltas along two edges
    FLOAT fDelAttrib10 = m_fRHW1 - m_fRHW0;
    FLOAT fDelAttrib20 = m_fRHW2 - m_fRHW0;

    // compute A & B terms (dVdX and dVdY)
    m_fRHWA = m_fTriOODet * ( fDelAttrib10 * m_fDelY20 + fDelAttrib20 * m_fDelY01 );
    m_fRHWB = m_fTriOODet * ( fDelAttrib20 * m_fDelX10 + fDelAttrib10 * m_fDelX02 );

    // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv)
    m_fRHWC = m_fRHW0 - ( m_fRHWA * m_fX0 ) - ( m_fRHWB * m_fY0 );

    for(INT32 i = 0; i < m_cTextureStages; i++)
    {
        m_fRHQW0[i] = pfRHQW[0];
        m_fRHQW1[i] = pfRHQW[1];
        m_fRHQW2[i] = pfRHQW[2];
        pfRHQW += 3;

        // compute linear function for Q/W (for transformed, projected, perspective corrected texture)
        fDelAttrib10 = m_fRHQW1[i] - m_fRHQW0[i];
        fDelAttrib20 = m_fRHQW2[i] - m_fRHQW0[i];

        // compute A & B terms (dVdX and dVdY)
        m_fRHQWA[i] = m_fTriOODet * ( fDelAttrib10 * m_fDelY20 + fDelAttrib20 * m_fDelY01 );
        m_fRHQWB[i] = m_fTriOODet * ( fDelAttrib20 * m_fDelX10 + fDelAttrib10 * m_fDelX02 );

        // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv)
        m_fRHQWC[i] = m_fRHQW0[i] - ( m_fRHQWA[i] * m_fX0 ) - ( m_fRHQWB[i] * m_fY0 );
    }
}

//-----------------------------------------------------------------------------
//
// SetPerLineData - Called once per line during setup to set per-line
// data used to compute attribute functions.
//
//-----------------------------------------------------------------------------
void
RRAttribFuncStatic::SetPerLineData(
    FLOAT fX0, FLOAT fY0, FLOAT fRHW0,
    FLOAT fX1, FLOAT fY1, FLOAT fRHW1,
    INT32 cTextureStages,
    FLOAT* pfRHQW,
    FLOAT fMajorExtent, BOOL bXMajor )
{
    m_PrimType = RR_LINE;

    m_fLineMajorLength = fMajorExtent;
    m_bLineXMajor = bXMajor;

    m_fX0 = fX0;
    m_fY0 = fY0;
    m_cTextureStages = cTextureStages;

    m_fRHW0 = fRHW0;
    m_fRHW1 = fRHW1;

    // compute linear function for 1/W (for perspective correction)
    FLOAT fDelta = ( m_fRHW1 - m_fRHW0 ) / m_fLineMajorLength;
    m_fRHWA = ( m_bLineXMajor ) ? ( fDelta ) : ( 0. );
    m_fRHWB = ( m_bLineXMajor ) ? ( 0. ) : ( fDelta );
    m_fRHWC = fRHW0 - ( m_fRHWA * m_fX0 ) - ( m_fRHWB * m_fY0 );
    for(INT32 i = 0; i < m_cTextureStages; i++)
    {
        m_fRHQW0[i] = pfRHQW[0];
        m_fRHQW1[i] = pfRHQW[1];
        pfRHQW += 3;

        // compute linear function for Q/W (for transformed, projected, perspective corrected texture)
        FLOAT fDelta = ( m_fRHQW1[i] - m_fRHQW0[i] ) / m_fLineMajorLength;
        m_fRHQWA[i] = ( m_bLineXMajor ) ? ( fDelta ) : ( 0. );
        m_fRHQWB[i] = ( m_bLineXMajor ) ? ( 0. ) : ( fDelta );
        m_fRHQWC[i] = m_fRHQW0[i] - ( m_fRHQWA[i] * m_fX0 ) - ( m_fRHQWB[i] * m_fY0 );
    }
}

//-----------------------------------------------------------------------------
//
// SetPixel - Called once per pixel to do preparation for per-pixel attribute
// evaluations.
//
//-----------------------------------------------------------------------------
void
RRAttribFuncStatic::SetPerPixelData( INT16 iX, INT16 iY )
{
    m_iX = iX;
    m_iY = iY;

    // evalute 1/W function
    FLOAT fPixelRHW =
        ( m_fRHWA * (FLOAT)m_iX ) + ( m_fRHWB * (FLOAT)m_iY ) + m_fRHWC;
    m_fPixelW = ( 0. != fPixelRHW ) ? ( 1./fPixelRHW ) : ( 0. );
    for(INT32 i = 0; i < m_cTextureStages; i++)
    {
        FLOAT fPixelRHQW =
            ( m_fRHQWA[i] * (FLOAT)m_iX ) + ( m_fRHQWB[i] * (FLOAT)m_iY ) + m_fRHQWC[i];
        m_fPixelQW[i] = ( 0. != fPixelRHQW ) ? ( 1./fPixelRHQW ) : ( 0. );
    }
}

//-----------------------------------------------------------------------------
//
// GetPixelW,GetPixelQW,GetRhwXGradient,GetRhwYGradient,
// GetRhqwXGradient,GetRhqwYGradient - Functions to get static
// data members.
//
//-----------------------------------------------------------------------------
FLOAT RRAttribFuncStatic::GetPixelW( void ) { return m_fPixelW; }
FLOAT RRAttribFuncStatic::GetPixelQW( INT32 iStage ) { return m_fPixelQW[iStage]; }
FLOAT RRAttribFuncStatic::GetRhqwXGradient( INT32 iStage ) { return m_fRHQWA[iStage]; }
FLOAT RRAttribFuncStatic::GetRhqwYGradient( INT32 iStage ) { return m_fRHQWB[iStage]; }


///////////////////////////////////////////////////////////////////////////////
//
// RRAttribFunc - methods
//
///////////////////////////////////////////////////////////////////////////////

//-----------------------------------------------------------------------------
//
// SetConstant - Sets function to evaluate to constant value.
//
//-----------------------------------------------------------------------------
void
RRAttribFunc::SetConstant(
    FLOAT fC )
{
    m_bIsPerspective = FALSE;
    m_fA = 0.; m_fB = 0.; m_fC = fC;
}

//-----------------------------------------------------------------------------
//
// SetLinearFunc - Computes linear function for scalar attribute specified at
// triangle vertices.
//
//-----------------------------------------------------------------------------
void
RRAttribFunc::SetLinearFunc(
    FLOAT fVal0, FLOAT fVal1, FLOAT fVal2 )
{
    m_bIsPerspective = FALSE;

    switch ( m_pSD->m_PrimType )
    {
    case RR_TRIANGLE:
        {
            // compute A,B,C for triangle function

            // compute linear deltas along two edges
            FLOAT fDelAttrib10 = fVal1 - fVal0;
            FLOAT fDelAttrib20 = fVal2 - fVal0;

            // compute A & B terms (dVdX and dVdY)
            m_fA = m_pSD->m_fTriOODet *
                ( fDelAttrib10 * m_pSD->m_fDelY20 + fDelAttrib20 * m_pSD->m_fDelY01 );
            m_fB = m_pSD->m_fTriOODet *
                ( fDelAttrib20 * m_pSD->m_fDelX10 + fDelAttrib10 * m_pSD->m_fDelX02 );

            // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv)
            m_fC = fVal0 - ( m_fA * m_pSD->m_fX0 ) - ( m_fB * m_pSD->m_fY0 );
        }
        break;

    case RR_LINE:
        {
            // compute A,B,C for line function - delta is normalized difference
            // in major direction; C is computed from knowing the function value
            // at the vertices (vertex 0 is always used here)
            FLOAT fDelta = ( fVal1 - fVal0 ) / m_pSD->m_fLineMajorLength;
            m_fA = ( m_pSD->m_bLineXMajor ) ? ( fDelta ) : ( 0. );
            m_fB = ( m_pSD->m_bLineXMajor ) ? ( 0. ) : ( fDelta );

            // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv)
            m_fC = fVal0 - ( m_fA * m_pSD->m_fX0 ) - ( m_fB * m_pSD->m_fY0 );
        }
        break;

    case RR_POINT:

        // use constant function for point
        m_fA = 0.;
        m_fB = 0.;
        m_fC = fVal0;

        break;
    }

}

//-----------------------------------------------------------------------------
//
// SetPerspFunc - Computes perspective corrected function for scalar attribute
// specified at triangle vertices.
//
//-----------------------------------------------------------------------------
void
RRAttribFunc::SetPerspFunc(
    FLOAT fVal0, FLOAT fVal1, FLOAT fVal2 )
{
    switch ( m_pSD->m_PrimType )
    {
    case RR_TRIANGLE:
        {
            // triangle function

            // compute adjusted values for vertices 1,2 based on wrap flag
            FLOAT fVal1P = (fVal1);
            FLOAT fVal2P = (fVal2);

            // compute perspective corrected linear deltas along two edges
            FLOAT fDelAttrib10 = ( fVal1P * m_pSD->m_fRHW1 ) - ( fVal0 * m_pSD->m_fRHW0 );
            FLOAT fDelAttrib20 = ( fVal2P * m_pSD->m_fRHW2 ) - ( fVal0 * m_pSD->m_fRHW0 );

            // compute A & B terms (dVdX and dVdY)
            m_fA = m_pSD->m_fTriOODet *
                ( fDelAttrib10 * m_pSD->m_fDelY20 + fDelAttrib20 * m_pSD->m_fDelY01 );
            m_fB = m_pSD->m_fTriOODet *
                ( fDelAttrib20 * m_pSD->m_fDelX10 + fDelAttrib10 * m_pSD->m_fDelX02 );

            // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv)
            m_fC = ( fVal0* m_pSD->m_fRHW0)
                - ( m_fA * m_pSD->m_fX0 ) - ( m_fB * m_pSD->m_fY0 );

            m_bIsPerspective = TRUE;
        }
        break;

    case RR_LINE:
        {
            // line function

            FLOAT fVal1P = (fVal1);
            FLOAT fDelta =
                ( fVal1P*m_pSD->m_fRHW1 - fVal0*m_pSD->m_fRHW0) / m_pSD->m_fLineMajorLength;
            m_fA = ( m_pSD->m_bLineXMajor ) ? ( fDelta ) : ( 0. );
            m_fB = ( m_pSD->m_bLineXMajor ) ? ( 0. ) : ( fDelta );
            // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv)
            m_fC = ( fVal0* m_pSD->m_fRHW0)
                - ( m_fA * m_pSD->m_fX0 ) - ( m_fB * m_pSD->m_fY0 );

            m_bIsPerspective = TRUE;
        }
        break;

    case RR_POINT:

        // use constant function for point
        m_fA = 0.;
        m_fB = 0.;
        m_fC = fVal0;

        // don't correct constant functions
        m_bIsPerspective = FALSE;

        break;
    }
}

//-----------------------------------------------------------------------------
//
// Eval - Evaluates function at pixel position set in RRAttribFunc::SetPerPixelData.
// Functions know if they are perspective corrected or not, and if so then do
// the multiply through by the 1/(1/w) term to normalize.
//
//-----------------------------------------------------------------------------
FLOAT
RRAttribFunc::Eval( void )
{
    FLOAT fRet =
        ( m_fA * (FLOAT)m_pSD->m_iX ) + ( m_fB * (FLOAT)m_pSD->m_iY ) + m_fC;
    if ( m_bIsPerspective ) { fRet *= m_pSD->m_fPixelW; }
    return fRet;
}

//-----------------------------------------------------------------------------
//
// SetPerspFunc - Computes perspective corrected function for scalar attribute
// specified at triangle vertices.
//
//-----------------------------------------------------------------------------
void
RRAttribFunc::SetPerspFunc(
    FLOAT fVal0, FLOAT fVal1, FLOAT fVal2,
    BOOL bWrap, BOOL bIsShadowMap )
{
    switch ( m_pSD->m_PrimType )
    {
    case RR_TRIANGLE:
        {
            // triangle function
            FLOAT fRHW0 = m_pSD->m_fRHW0;
            FLOAT fRHW1 = m_pSD->m_fRHW1;
            FLOAT fRHW2 = m_pSD->m_fRHW2;
            if (bIsShadowMap)
            {
                fRHW0 = 1.0f;
                fRHW1 = 1.0f;
                fRHW2 = 1.0f;
            }

            // compute adjusted values for vertices 1,2 based on wrap flag
            FLOAT fVal1P = bWrap ? ( fVal0 + WrapDiff(fVal1,fVal0) ) : (fVal1);
            FLOAT fVal2P = bWrap ? ( fVal0 + WrapDiff(fVal2,fVal0) ) : (fVal2);

            // compute perspective corrected linear deltas along two edges
            FLOAT fDelAttrib10 = ( fVal1P * fRHW1 ) - ( fVal0 * fRHW0 );
            FLOAT fDelAttrib20 = ( fVal2P * fRHW2 ) - ( fVal0 * fRHW0 );

            // compute A & B terms (dVdX and dVdY)
            m_fA = m_pSD->m_fTriOODet *
                ( fDelAttrib10 * m_pSD->m_fDelY20 + fDelAttrib20 * m_pSD->m_fDelY01 );
            m_fB = m_pSD->m_fTriOODet *
                ( fDelAttrib20 * m_pSD->m_fDelX10 + fDelAttrib10 * m_pSD->m_fDelX02 );

            // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv)
            m_fC = ( fVal0 * fRHW0 )
                - ( m_fA * m_pSD->m_fX0 ) - ( m_fB * m_pSD->m_fY0 );

            m_bIsPerspective = TRUE;
        }
        break;

    case RR_LINE:
        {
            // line function

            FLOAT fRHW0 = m_pSD->m_fRHW0;
            FLOAT fRHW1 = m_pSD->m_fRHW1;
            if (bIsShadowMap)
            {
                fRHW0 = 1.0f;
                fRHW1 = 1.0f;
            }

            FLOAT fVal1P = bWrap ? ( fVal0 + WrapDiff(fVal1,fVal0) ) : (fVal1);
            FLOAT fDelta =
                ( fVal1P*fRHW1 - fVal0*fRHW0) / m_pSD->m_fLineMajorLength;
            m_fA = ( m_pSD->m_bLineXMajor ) ? ( fDelta ) : ( 0. );
            m_fB = ( m_pSD->m_bLineXMajor ) ? ( 0. ) : ( fDelta );
            // compute C term (Fv = A*Xv + B*Yv + C => C = Fv - A*Xv - B*Yv)
            m_fC = ( fVal0* fRHW0)
                - ( m_fA * m_pSD->m_fX0 ) - ( m_fB * m_pSD->m_fY0 );

            m_bIsPerspective = TRUE;
        }
        break;

    case RR_POINT:

        // use constant function for point
        m_fA = 0.;
        m_fB = 0.;
        m_fC = fVal0;

        // don't correct constant functions
        m_bIsPerspective = FALSE;

        break;
    }
}

//-----------------------------------------------------------------------------
//
// Eval - Evaluates function at pixel position set in RRAttribFunc::SetPerPixelData.
// Functions know if they are perspective corrected or not, and if so then do
// the multiply through by the 1/(q/w) term to normalize.
//
//-----------------------------------------------------------------------------
FLOAT
RRAttribFunc::Eval( INT32 iStage )
{
    FLOAT fRet =
        ( m_fA * (FLOAT)m_pSD->m_iX ) + ( m_fB * (FLOAT)m_pSD->m_iY ) + m_fC;
    // m_bIsPerspective will always be set since persp function is always
    // used for texture coords
    if ( m_bIsPerspective ) { fRet *= m_pSD->m_fPixelQW[iStage]; }
    return fRet;
}


///////////////////////////////////////////////////////////////////////////////
// end