/*
    Copyright (c) Microsoft Corporation

    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
    associated documentation files (the "Software"), to deal in the Software without restriction,
    including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
    and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
    subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all copies or substantial
    portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
    NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include "tessellator.hpp"
#if defined(_MSC_VER)
#include <math.h> // ceil
#else
#include <cmath>
#endif
//#include <windows.h> // Just used for some commented out debug stat printing.
//#include <strsafe.h> // Ditto.
#define min(x,y) (x < y ? x : y)
#define max(x,y) (x > y ? x : y)

//=================================================================================================================================
// Some D3D Compliant Float Math (reference rasterizer implements these in RefALU class)
//=================================================================================================================================
//
//---------------------------------------------------------------------------------------------------------------------------------
// isNaN
//---------------------------------------------------------------------------------------------------------------------------------

union fiu {
    float f;
    int i;
};

static bool tess_isNaN( float a )
{
    static const int exponentMask = 0x7f800000;
    static const int mantissaMask = 0x007fffff;
    union fiu fiu;
    fiu.f = a;
    return ( ( ( fiu.i & exponentMask ) == exponentMask ) && ( fiu.i & mantissaMask ) ); // NaN
}

//---------------------------------------------------------------------------------------------------------------------------------
// flush (denorm)
//---------------------------------------------------------------------------------------------------------------------------------
static float tess_flush( float a )
{
    static const int minNormalizedFloat = 0x00800000;
    static const int signBit = 0x80000000;
    static const int signBitComplement = 0x7fffffff;
    union fiu fiu, uif;
    fiu.f = a;
    int b = fiu.i & signBitComplement; // fabs()
    if( b < minNormalizedFloat ) // UINT comparison. NaN/INF do test false here
    {
        b = signBit & (fiu.i);
        uif.i = b;
        return uif.f;
    }
    return a;
}

//---------------------------------------------------------------------------------------------------------------------------------
// IEEE754R min
//---------------------------------------------------------------------------------------------------------------------------------
static float tess_fmin( float a, float b )
{
    float _a = tess_flush( a );
    float _b = tess_flush( b );
    if( tess_isNaN( _b ) )
    {
        return a;
    }
    else if( ( _a == 0 ) && ( _b == 0 ) )
    {
        union fiu fiu;
        fiu.f = _a;
        return ( fiu.i & 0x80000000 ) ? a : b;
    }
    return _a < _b ? a : b;
}

//---------------------------------------------------------------------------------------------------------------------------------
// IEEE754R max
//---------------------------------------------------------------------------------------------------------------------------------
static float tess_fmax( float a, float b )
{
    float _a = tess_flush( a );
    float _b = tess_flush( b );

    if( tess_isNaN( _b ) )
    {
        return a;
    }
    else if( ( _a == 0 ) && ( _b == 0 ) )
    {
        union fiu fiu;
        fiu.f = _b;
        return ( fiu.i & 0x80000000 ) ? a : b;
    }
    return _a >= _b ? a : b;
}

//=================================================================================================================================
// Fixed Point Math
//=================================================================================================================================

//-----------------------------------------------------------------------------------------------------------------------------
// floatToFixedPoint
//
// Convert 32-bit float to 32-bit fixed point integer, using only
// integer arithmetic + bitwise operations.
//
// c_uIBits:  UINT8     : Width of i (aka. integer bits)
// c_uFBits:  UINT8     : Width of f (aka. fractional bits)
// c_bSigned: bool      : Whether the integer bits are a 2's complement signed value
// input:     float     : All values valid.
// output:    INT32     : At most 24 bits from LSB are meaningful, depending
//                        on the fixed point bit representation chosen (see
//                        below).  Extra bits are sign extended from the most
//                        meaningful bit.
//
//-----------------------------------------------------------------------------------------------------------------------------

typedef unsigned char UINT8;
typedef int INT32;
template< const UINT8 c_uIBits, const UINT8 c_uFBits, const bool c_bSigned >
INT32 floatToIDotF( const float& input )
{
    // ------------------------------------------------------------------------
    //                                                output fixed point format
    // 32-bit result:
    //
    //      [sign-extend]i.f
    //      |              |
    //      MSB(31)...LSB(0)
    //
    //      f               fractional part of the number, an unsigned
    //                      value with _fxpFracBitCount bits (defined below)
    //
    //      .               implied decimal
    //
    //      i               integer part of the number, a 2's complement
    //                      value with _fxpIntBitCount bits (defined below)
    //
    //      [sign-extend]   MSB of i conditionally replicated
    //
    // ------------------------------------------------------------------------
    // Define fixed point bit counts
    //

    // Commenting out C_ASSERT below to minimise #includes:
    // C_ASSERT( 2 <= c_uIBits && c_uIBits <= 32 && c_uFBits <= 32 && c_uIBits + c_uFBits <= 32 );

    // Define most negative and most positive fixed point values
    const INT32 c_iMinResult = (c_bSigned ? INT32( -1 ) << (c_uIBits + c_uFBits - 1) : 0);
    const INT32 c_iMaxResult = ~c_iMinResult;

    // ------------------------------------------------------------------------
    //                                                constant float properties
    // ------------------------------------------------------------------------
    const UINT8 _fltMantissaBitCount = 23;
    const UINT8 _fltExponentBitCount = 8;
    const INT32 _fltExponentBias     = (INT32( 1 ) << (_fltExponentBitCount - 1)) - 1;
    const INT32 _fltHiddenBit        = INT32( 1 ) << _fltMantissaBitCount;
    const INT32 _fltMantissaMask     = _fltHiddenBit - 1;
    const INT32 _fltExponentMask     = ((INT32( 1 ) << _fltExponentBitCount) - 1) << _fltMantissaBitCount;
    const INT32 _fltSignBit          = INT32( 1 ) << (_fltExponentBitCount + _fltMantissaBitCount);

    // ------------------------------------------------------------------------
    //              define min and max values as floats (clamp to these bounds)
    // ------------------------------------------------------------------------
    INT32 _fxpMaxPosValueFloat;
    INT32 _fxpMaxNegValueFloat;

    if (c_bSigned)
    {
        // The maximum positive fixed point value is 2^(i-1) - 2^(-f).
        // The following constructs the floating point bit pattern for this value,
        // as long as i >= 2.
        _fxpMaxPosValueFloat = (_fltExponentBias + c_uIBits - 1) <<_fltMantissaBitCount;
        const INT32 iShift = _fltMantissaBitCount + 2 - c_uIBits - c_uFBits;
        if (iShift >= 0)
        {
//            assert( iShift < 32 );
#if defined(_MSC_VER)
#pragma warning( suppress : 4293 )
#endif
            _fxpMaxPosValueFloat -= INT32( 1 ) << iShift;
        }

        // The maximum negative fixed point value is -2^(i-1).
        // The following constructs the floating point bit pattern for this value,
        // as long as i >= 2.
        // We need this number without the sign bit
        _fxpMaxNegValueFloat = (_fltExponentBias + c_uIBits - 1) << _fltMantissaBitCount;
    }
    else
    {
        // The maximum positive fixed point value is 2^(i) - 2^(-f).
        // The following constructs the floating point bit pattern for this value,
        // as long as i >= 2.
        _fxpMaxPosValueFloat = (_fltExponentBias + c_uIBits) <<_fltMantissaBitCount;
        const INT32 iShift = _fltMantissaBitCount + 1 - c_uIBits - c_uFBits;
        if (iShift >= 0)
        {
//            assert( iShift < 32 );
#if defined(_MSC_VER)
#pragma warning( suppress : 4293 )
#endif
            _fxpMaxPosValueFloat -= INT32( 1 ) << iShift;
        }

        // The maximum negative fixed point value is 0.
        _fxpMaxNegValueFloat = 0;
    }

    // ------------------------------------------------------------------------
    //                                                float -> fixed conversion
    // ------------------------------------------------------------------------

    // ------------------------------------------------------------------------
    //                                                      examine input float
    // ------------------------------------------------------------------------
    INT32 output              = *(INT32*)&input;
    INT32 unbiasedExponent    = ((output & _fltExponentMask) >> _fltMantissaBitCount) - _fltExponentBias;
    INT32 isNegative          = output & _fltSignBit;

    // ------------------------------------------------------------------------
    //                                                                      nan
    // ------------------------------------------------------------------------
    if (unbiasedExponent == (_fltExponentBias + 1) && (output & _fltMantissaMask))
    {
        // nan converts to 0
        output = 0;
    }
    // ------------------------------------------------------------------------
    //                                                       too large positive
    // ------------------------------------------------------------------------
    else if (!isNegative && output >= _fxpMaxPosValueFloat) // integer compare
    {
        output = c_iMaxResult;
    }
    // ------------------------------------------------------------------------
    //                                                       too large negative
    // ------------------------------------------------------------------------
                                            // integer compare
    else if (isNegative && (output & ~_fltSignBit) >= _fxpMaxNegValueFloat)
    {
        output = c_iMinResult;
    }
    // ------------------------------------------------------------------------
    //                                                                too small
    // ------------------------------------------------------------------------
    else if (unbiasedExponent < -c_uFBits - 1)
    {
        // clamp to 0
        output = 0;
    }
    // ------------------------------------------------------------------------
    //                                                             within range
    // ------------------------------------------------------------------------
    else
    {
        // copy mantissa, add hidden bit
        output = (output & _fltMantissaMask) | _fltHiddenBit;

        INT32 extraBits = _fltMantissaBitCount - c_uFBits - unbiasedExponent;
        if (extraBits >= 0)
        {
            // 2's complement if negative
            if (isNegative)
            {
                output = ~output + 1;
            }

            // From the range checks that led here, it is known that
            // unbiasedExponent < c_uIBits.  So, at most:
            // (a) unbiasedExponent == c_uIBits - 1.
            //
            // From compile validation above, it is known that
            // c_uIBits + c_uFBits <= _fltMantissaBitCount + 1).
            // So, at minimum:
            // (b) _fltMantissaBitCount == _fxtIntBitCount + c_uFBits - 1
            //
            // Substituting (a) and (b) into extraBits calculation above:
            // extraBits >= (_fxtIntBitCount + c_uFBits - 1)
            //              - c_uFBits - (c_uIBits - 1)
            // extraBits >= 0
            //
            // Thus we only have to worry about shifting right by 0 or more
            // bits to get the decimal to the right place, and never have
            // to shift left.

            INT32 LSB             = 1 << extraBits; // last bit being kept
            INT32 extraBitsMask   = LSB - 1;
            INT32 half            = LSB >> 1; // round bias

            // round to nearest-even at LSB
            if ((output & LSB) || (output & extraBitsMask) > half)
            {
                output += half;
            }

            // shift off the extra bits (sign extending)
            output >>= extraBits;
        }
        else
        {
            output <<= -extraBits;

            // 2's complement if negative
            if (isNegative)
            {
                output = ~output + 1;
            }
        }
    }
    return output;
}
//-----------------------------------------------------------------------------------------------------------------------------

#define FXP_INTEGER_BITS 15
#define FXP_FRACTION_BITS 16
#define FXP_FRACTION_MASK 0x0000ffff
#define FXP_INTEGER_MASK 0x7fff0000
#define FXP_THREE (3<<FXP_FRACTION_BITS)
#define FXP_ONE (1<<FXP_FRACTION_BITS)
#define FXP_ONE_THIRD 0x00005555
#define FXP_TWO_THIRDS 0x0000aaaa
#define FXP_ONE_HALF   0x00008000

#define FXP_MAX_INPUT_TESS_FACTOR_BEFORE_TRIPLE_AVERAGE 0x55540000 // 1/3 of max fixed point number - 1.  Numbers less than
                                                    // or equal to this allows avg. reduction on a tri patch
                                                    // including rounding.

#define FXP_MAX_INPUT_TESS_FACTOR_BEFORE_PAIR_AVERAGE 0x7FFF0000 // 1/2 of max fixed point number - 1.  Numbers less than
                                                    // or equal to this allows avg. reduction on a quad patch
                                                    // including rounding.

static const FXP s_fixedReciprocal[D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR+1] =
{
    0xffffffff, // 1/0 is the first entry (unused)
    0x10000, 0x8000, 0x5555, 0x4000,
    0x3333, 0x2aab, 0x2492, 0x2000,
    0x1c72, 0x199a, 0x1746, 0x1555,
    0x13b1, 0x1249, 0x1111, 0x1000,
    0xf0f, 0xe39, 0xd79, 0xccd,
    0xc31, 0xba3, 0xb21, 0xaab,
    0xa3d, 0x9d9, 0x97b, 0x925,
    0x8d4, 0x889, 0x842, 0x800,
    0x7c2, 0x788, 0x750, 0x71c,
    0x6eb, 0x6bd, 0x690, 0x666,
    0x63e, 0x618, 0x5f4, 0x5d1,
    0x5b0, 0x591, 0x572, 0x555,
    0x539, 0x51f, 0x505, 0x4ec,
    0x4d5, 0x4be, 0x4a8, 0x492,
    0x47e, 0x46a, 0x457, 0x444,
    0x432, 0x421, 0x410, 0x400, // 1/64 is the last entry
};

#define FLOAT_THREE 3.0f
#define FLOAT_ONE 1.0f

//---------------------------------------------------------------------------------------------------------------------------------
// floatToFixed
//---------------------------------------------------------------------------------------------------------------------------------
FXP floatToFixed(const float& input)
{
    return floatToIDotF< FXP_INTEGER_BITS, FXP_FRACTION_BITS, /*bSigned*/false >( input );
}

//---------------------------------------------------------------------------------------------------------------------------------
// fixedToFloat
//---------------------------------------------------------------------------------------------------------------------------------
float fixedToFloat(const FXP& input)
{
    // not worrying about denorm flushing the float operations (the DX spec behavior for div), since the numbers will not be that small during tessellation.
    return ((float)(input>>FXP_FRACTION_BITS) + (float)(input&FXP_FRACTION_MASK)/(1<<FXP_FRACTION_BITS));
}

//---------------------------------------------------------------------------------------------------------------------------------
// isEven
//---------------------------------------------------------------------------------------------------------------------------------
bool isEven(const float& input)
{
    return (((int)input) & 1) ? false : true;
}

//---------------------------------------------------------------------------------------------------------------------------------
// fxpCeil
//---------------------------------------------------------------------------------------------------------------------------------
FXP fxpCeil(const FXP& input)
{
    if( input & FXP_FRACTION_MASK )
    {
        return (input & FXP_INTEGER_MASK) + FXP_ONE;
    }
    return input;
}

//---------------------------------------------------------------------------------------------------------------------------------
// fxpFloor
//---------------------------------------------------------------------------------------------------------------------------------
FXP fxpFloor(const FXP& input)
{
    return (input & FXP_INTEGER_MASK);
}

//=================================================================================================================================
// CHWTessellator
//=================================================================================================================================

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::CHWTessellator
//---------------------------------------------------------------------------------------------------------------------------------
CHWTessellator::CHWTessellator()
{
    m_Point = 0;
    m_Index = 0;
    m_NumPoints = 0;
    m_NumIndices = 0;
    m_bUsingPatchedIndices = false;
    m_bUsingPatchedIndices2 = false;
#ifdef ALLOW_XBOX_360_COMPARISON
	m_bXBox360Mode = false;
#endif
}
//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::~CHWTessellator
//---------------------------------------------------------------------------------------------------------------------------------
CHWTessellator::~CHWTessellator()
{
    delete [] m_Point;
    delete [] m_Index;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::Init
// User calls this.
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::Init(
    D3D11_TESSELLATOR_PARTITIONING       partitioning,
    D3D11_TESSELLATOR_OUTPUT_PRIMITIVE   outputPrimitive)
{
    if( 0 == m_Point )
    {
        m_Point = new DOMAIN_POINT[MAX_POINT_COUNT];
    }
    if( 0 == m_Index )
    {
        m_Index = new int[MAX_INDEX_COUNT];
    }
    m_partitioning = partitioning;
    m_originalPartitioning = partitioning;
    switch( partitioning )
    {
    case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
    default:
        break;
    case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
        m_parity = TESSELLATOR_PARITY_ODD;
        break;
    case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
        m_parity = TESSELLATOR_PARITY_EVEN;
        break;
    }
    m_originalParity = m_parity;
    m_outputPrimitive = outputPrimitive;
    m_NumPoints = 0;
    m_NumIndices = 0;
}
//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::TessellateQuadDomain
// User calls this
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::TessellateQuadDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
                                         float insideTessFactor_U, float insideTessFactor_V )
{
    PROCESSED_TESS_FACTORS_QUAD processedTessFactors;
    QuadProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Ueq1,tessFactor_Veq1,insideTessFactor_U,insideTessFactor_V,processedTessFactors);

    if( processedTessFactors.bPatchCulled )
    {
        m_NumPoints = 0;
        m_NumIndices = 0;
        return;
    }
    else if( processedTessFactors.bJustDoMinimumTessFactor )
    {
        DefinePoint(/*U*/0,/*V*/0,/*pointStorageOffset*/0);
        DefinePoint(/*U*/FXP_ONE,/*V*/0,/*pointStorageOffset*/1);
        DefinePoint(/*U*/FXP_ONE,/*V*/FXP_ONE,/*pointStorageOffset*/2);
        DefinePoint(/*U*/0,/*V*/FXP_ONE,/*pointStorageOffset*/3);
        m_NumPoints = 4;

        switch(m_outputPrimitive)
        {
        case D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CW:
        case D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CCW:
            // function orients them CCW if needed
            DefineClockwiseTriangle(0,1,3,/*indexStorageOffset*/0);
            DefineClockwiseTriangle(1,2,3,/*indexStorageOffset*/3);
            m_NumIndices = 6;
            break;
        case D3D11_TESSELLATOR_OUTPUT_POINT:
            DumpAllPoints();
            break;
        case D3D11_TESSELLATOR_OUTPUT_LINE:
            DumpAllPointsAsInOrderLineList();
            break;
        }
        return;
    }

    QuadGeneratePoints(processedTessFactors);

    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_POINT )
    {
        DumpAllPoints();
        return;
    }
    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_LINE )
    {
        DumpAllPointsAsInOrderLineList();
        return;
    }

    QuadGenerateConnectivity(processedTessFactors); // can be done in parallel to QuadGeneratePoints()
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::QuadProcessTessFactors
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::QuadProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
                      float insideTessFactor_U, float insideTessFactor_V, PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
{
    // Is the patch culled?
    if( !(tessFactor_Ueq0 > 0) || // NaN will pass
        !(tessFactor_Veq0 > 0) ||
        !(tessFactor_Ueq1 > 0) ||
        !(tessFactor_Veq1 > 0) )
    {
        processedTessFactors.bPatchCulled = true;
        return;
    }
    else
    {
        processedTessFactors.bPatchCulled = false;
    }

    // Clamp edge TessFactors
    float lowerBound = 0.0, upperBound = 0.0;
    switch(m_originalPartitioning)
    {
        case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
        case D3D11_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
            break;

        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
            lowerBound = D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
            break;

        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
            break;
    }

    tessFactor_Ueq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Ueq0 ) );
    tessFactor_Veq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Veq0 ) );
    tessFactor_Ueq1 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Ueq1 ) );
    tessFactor_Veq1 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Veq1 ) );

    if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
    {
        tessFactor_Ueq0 = ceil(tessFactor_Ueq0);
        tessFactor_Veq0 = ceil(tessFactor_Veq0);
        tessFactor_Ueq1 = ceil(tessFactor_Ueq1);
        tessFactor_Veq1 = ceil(tessFactor_Veq1);
    }

    // Clamp inside TessFactors
    if(D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD == m_originalPartitioning)
    {
#define EPSILON 0.0000152587890625f // 2^(-16), min positive fixed point fraction
#define MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON (D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON/2)
        // If any TessFactor will end up > 1 after floatToFixed conversion later,
        // then force the inside TessFactors to be > 1 so there is a picture frame.
        if( (tessFactor_Ueq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
            (tessFactor_Veq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
            (tessFactor_Ueq1 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
            (tessFactor_Veq1 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
            (insideTessFactor_U > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
            (insideTessFactor_V > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) )
        {
            // Force picture frame
            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON;
        }
    }

    insideTessFactor_U = tess_fmin( upperBound, tess_fmax( lowerBound, insideTessFactor_U ) );
    insideTessFactor_V = tess_fmin( upperBound, tess_fmax( lowerBound, insideTessFactor_V ) );
    // Note the above clamps map NaN to lowerBound


    if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
    {
        insideTessFactor_U = ceil(insideTessFactor_U);
        insideTessFactor_V = ceil(insideTessFactor_V);
    }

    // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
    m_NumPoints = 0;
    m_NumIndices = 0;

    // Process tessFactors
    float outsideTessFactor[QUAD_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Ueq1, tessFactor_Veq1};
    float insideTessFactor[QUAD_AXES] = {insideTessFactor_U,insideTessFactor_V};
    int edge, axis;
    if( HWIntegerPartitioning() )
    {
        for( edge = 0; edge < QUAD_EDGES; edge++ )
        {
            int edgeEven = isEven(outsideTessFactor[edge]);
            processedTessFactors.outsideTessFactorParity[edge] = edgeEven ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
        }
        for( axis = 0; axis < QUAD_AXES; axis++ )
        {
            processedTessFactors.insideTessFactorParity[axis] =
                (isEven(insideTessFactor[axis]) || (FLOAT_ONE == insideTessFactor[axis]) )
                ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
        }
    }
    else
    {
        for( edge = 0; edge < QUAD_EDGES; edge++ )
        {
            processedTessFactors.outsideTessFactorParity[edge] = m_originalParity;
        }
        processedTessFactors.insideTessFactorParity[U] = processedTessFactors.insideTessFactorParity[V] = m_originalParity;
    }

    // Save fixed point TessFactors
    for( edge = 0; edge < QUAD_EDGES; edge++ )
    {
        processedTessFactors.outsideTessFactor[edge] = floatToFixed(outsideTessFactor[edge]);
    }
    for( axis = 0; axis < QUAD_AXES; axis++ )
    {
        processedTessFactors.insideTessFactor[axis] = floatToFixed(insideTessFactor[axis]);
    }

    if( HWIntegerPartitioning() || Odd() )
    {
        // Special case if all TessFactors are 1
        if( (FXP_ONE == processedTessFactors.insideTessFactor[U]) &&
            (FXP_ONE == processedTessFactors.insideTessFactor[V]) &&
            (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq0]) &&
            (FXP_ONE == processedTessFactors.outsideTessFactor[Veq0]) &&
            (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq1]) &&
            (FXP_ONE == processedTessFactors.outsideTessFactor[Veq1]) )
        {
            processedTessFactors.bJustDoMinimumTessFactor = true;
            return;
        }
    }
    processedTessFactors.bJustDoMinimumTessFactor = false;

    // Compute TessFactor-specific metadata
    for(int edge = 0; edge < QUAD_EDGES; edge++ )
    {
        SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
        ComputeTessFactorContext(processedTessFactors.outsideTessFactor[edge], processedTessFactors.outsideTessFactorCtx[edge]);
    }

    for(int axis = 0; axis < QUAD_AXES; axis++)
    {
        SetTessellationParity(processedTessFactors.insideTessFactorParity[axis]);
        ComputeTessFactorContext(processedTessFactors.insideTessFactor[axis], processedTessFactors.insideTessFactorCtx[axis]);
    }

    // Compute some initial data.

    // outside edge offsets and storage
    for(int edge = 0; edge < QUAD_EDGES; edge++ )
    {
        SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
        processedTessFactors.numPointsForOutsideEdge[edge] = NumPointsForTessFactor(processedTessFactors.outsideTessFactor[edge]);
        m_NumPoints += processedTessFactors.numPointsForOutsideEdge[edge];
    }
    m_NumPoints -= 4;

    // inside edge offsets
    for(int axis = 0; axis < QUAD_AXES; axis++)
    {
        SetTessellationParity(processedTessFactors.insideTessFactorParity[axis]);
        processedTessFactors.numPointsForInsideTessFactor[axis] = NumPointsForTessFactor(processedTessFactors.insideTessFactor[axis]);
        int pointCountMin = ( TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[axis] ) ? 4 : 3;
        // max() allows degenerate transition regions when inside TessFactor == 1
        processedTessFactors.numPointsForInsideTessFactor[axis] = max(pointCountMin,processedTessFactors.numPointsForInsideTessFactor[axis]);
    }

    processedTessFactors.insideEdgePointBaseOffset = m_NumPoints;

    // inside storage, including interior edges above
    int numInteriorPoints = (processedTessFactors.numPointsForInsideTessFactor[U] - 2)*(processedTessFactors.numPointsForInsideTessFactor[V]-2);
    m_NumPoints += numInteriorPoints;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::QuadGeneratePoints
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::QuadGeneratePoints( const PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
{
    // Generate exterior ring edge points, clockwise from top-left
    int pointOffset = 0;
    int edge;
    for(edge = 0; edge < QUAD_EDGES; edge++ )
    {
        int parity = edge&0x1;
        int startPoint = 0;
        int endPoint = processedTessFactors.numPointsForOutsideEdge[edge] - 1;
        for(int p = startPoint; p < endPoint; p++,pointOffset++) // don't include end, since next edge starts with it.
        {
            FXP fxpParam;
            int q = ((edge==1)||(edge==2)) ? p : endPoint - p; // reverse order
            SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
            PlacePointIn1D(processedTessFactors.outsideTessFactorCtx[edge],q,fxpParam);
            if( parity )
            {
                DefinePoint(/*U*/fxpParam,
                            /*V*/(edge == 3) ? FXP_ONE : 0,
                            /*pointStorageOffset*/pointOffset);
            }
            else
            {
                DefinePoint(/*U*/(edge == 2) ? FXP_ONE : 0,
                            /*V*/fxpParam,
                            /*pointStorageOffset*/pointOffset);
            }
        }
    }

    // Generate interior ring points, clockwise from (U==0,V==1) (bottom-left) spiralling toward center
    static const int startRing = 1;
    int minNumPointsForTessFactor = min(processedTessFactors.numPointsForInsideTessFactor[U],processedTessFactors.numPointsForInsideTessFactor[V]);
    int numRings = (minNumPointsForTessFactor >> 1);  // note for even tess we aren't counting center point here.
    for(int ring = startRing; ring < numRings; ring++)
    {
        int startPoint = ring;
        int endPoint[QUAD_AXES] = {processedTessFactors.numPointsForInsideTessFactor[U] - 1 - startPoint,
                                   processedTessFactors.numPointsForInsideTessFactor[V] - 1 - startPoint};

        for(edge = 0; edge < QUAD_EDGES; edge++ )
        {
            int parity[QUAD_AXES] = {edge&0x1,((edge+1)&0x1)};
            int perpendicularAxisPoint = (edge < 2) ? startPoint : endPoint[parity[0]];
            FXP fxpPerpParam;
            SetTessellationParity(processedTessFactors.insideTessFactorParity[parity[0]]);
            PlacePointIn1D(processedTessFactors.insideTessFactorCtx[parity[0]],perpendicularAxisPoint,fxpPerpParam);
            SetTessellationParity(processedTessFactors.insideTessFactorParity[parity[1]]);
            for(int p = startPoint; p < endPoint[parity[1]]; p++, pointOffset++) // don't include end: next edge starts with it.
            {
                FXP fxpParam;
                int q = ((edge == 1)||(edge==2)) ? p : endPoint[parity[1]] - (p - startPoint);
                PlacePointIn1D(processedTessFactors.insideTessFactorCtx[parity[1]],q,fxpParam);
                if( parity[1] )
                {
                    DefinePoint(/*U*/fxpPerpParam,
                                /*V*/fxpParam,
                                /*pointStorageOffset*/pointOffset);
                }
                else
                {
                    DefinePoint(/*U*/fxpParam,
                                /*V*/fxpPerpParam,
                                /*pointStorageOffset*/pointOffset);
                }
            }
        }
    }
    // For even tessellation, the inner "ring" is degenerate - a row of points
    if( (processedTessFactors.numPointsForInsideTessFactor[U] > processedTessFactors.numPointsForInsideTessFactor[V]) &&
        (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) )
    {
        int startPoint = numRings;
        int endPoint = processedTessFactors.numPointsForInsideTessFactor[U] - 1 - startPoint;
        SetTessellationParity(processedTessFactors.insideTessFactorParity[U]);
        for( int p = startPoint; p <= endPoint; p++, pointOffset++ )
        {
            FXP fxpParam;
            PlacePointIn1D(processedTessFactors.insideTessFactorCtx[U],p,fxpParam);
            DefinePoint(/*U*/fxpParam,
                        /*V*/FXP_ONE_HALF, // middle
                        /*pointStorageOffset*/pointOffset);
        }
    }
    else if( (processedTessFactors.numPointsForInsideTessFactor[V] >= processedTessFactors.numPointsForInsideTessFactor[U]) &&
             (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U]) )
    {
        int startPoint = numRings;
        int endPoint;
        FXP fxpParam;
        endPoint = processedTessFactors.numPointsForInsideTessFactor[V] - 1 - startPoint;
        SetTessellationParity(processedTessFactors.insideTessFactorParity[V]);
        for( int p = endPoint; p >= startPoint; p--, pointOffset++ )
        {
            PlacePointIn1D(processedTessFactors.insideTessFactorCtx[V],p,fxpParam);
            DefinePoint(/*U*/FXP_ONE_HALF, // middle
                        /*V*/fxpParam,
                        /*pointStorageOffset*/pointOffset);
        }
    }
}
//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::QuadGenerateConnectivity
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::QuadGenerateConnectivity( const PROCESSED_TESS_FACTORS_QUAD& processedTessFactors )
{
    // Generate primitives for all the concentric rings, one side at a time for each ring
    static const int startRing = 1;
    int numPointRowsToCenter[QUAD_AXES] = {((processedTessFactors.numPointsForInsideTessFactor[U]+1) >> 1),
                                            ((processedTessFactors.numPointsForInsideTessFactor[V]+1) >> 1)}; // +1 is so even tess includes the center point
    int numRings = min(numPointRowsToCenter[U],numPointRowsToCenter[V]);
    int degeneratePointRing[QUAD_AXES] = { // Even partitioning causes degenerate row of points,
                                           // which results in exceptions to the point ordering conventions
                                           // when travelling around the rings counterclockwise.
        (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) ? numPointRowsToCenter[V] - 1 : -1,
        (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U]) ? numPointRowsToCenter[U] - 1 : -1 };

    const TESS_FACTOR_CONTEXT* outsideTessFactorCtx[QUAD_EDGES] = {&processedTessFactors.outsideTessFactorCtx[Ueq0],
                                                    &processedTessFactors.outsideTessFactorCtx[Veq0],
                                                    &processedTessFactors.outsideTessFactorCtx[Ueq1],
                                                    &processedTessFactors.outsideTessFactorCtx[Veq1]};
    TESSELLATOR_PARITY outsideTessFactorParity[QUAD_EDGES] = {processedTessFactors.outsideTessFactorParity[Ueq0],
                                                        processedTessFactors.outsideTessFactorParity[Veq0],
                                                        processedTessFactors.outsideTessFactorParity[Ueq1],
                                                        processedTessFactors.outsideTessFactorParity[Veq1]};
    int numPointsForOutsideEdge[QUAD_EDGES] = {processedTessFactors.numPointsForOutsideEdge[Ueq0],
                                              processedTessFactors.numPointsForOutsideEdge[Veq0],
                                              processedTessFactors.numPointsForOutsideEdge[Ueq1],
                                              processedTessFactors.numPointsForOutsideEdge[Veq1]};

    int insideEdgePointBaseOffset = processedTessFactors.insideEdgePointBaseOffset;
    int outsideEdgePointBaseOffset = 0;
    int edge;
    for(int ring = startRing; ring < numRings; ring++)
    {
        int numPointsForInsideEdge[QUAD_AXES] = {processedTessFactors.numPointsForInsideTessFactor[U] - 2*ring,
                                                 processedTessFactors.numPointsForInsideTessFactor[V] - 2*ring};

        int edge0InsidePointBaseOffset = insideEdgePointBaseOffset;
        int edge0OutsidePointBaseOffset = outsideEdgePointBaseOffset;

        for(edge = 0; edge < QUAD_EDGES; edge++ )
        {
            int parity = (edge+1)&0x1;

            int numTriangles = numPointsForInsideEdge[parity] + numPointsForOutsideEdge[edge] - 2;
            int insideBaseOffset;
            int outsideBaseOffset;
            if( edge == 3 ) // We need to patch the indexing so Stitch() can think it sees
                            // 2 sequentially increasing rows of points, even though we have wrapped around
                            // to the end of the inner and outer ring's points, so the last point is really
                            // the first point for the ring.
                            // We make it so that when Stitch() calls AddIndex(), that function
                            // will do any necessary index adjustment.
            {
                if( ring == degeneratePointRing[parity] )
                {
                    m_IndexPatchContext2.baseIndexToInvert = insideEdgePointBaseOffset + 1;
                    m_IndexPatchContext2.cornerCaseBadValue = outsideEdgePointBaseOffset + numPointsForOutsideEdge[edge] - 1;
                    m_IndexPatchContext2.cornerCaseReplacementValue = edge0OutsidePointBaseOffset;
                    m_IndexPatchContext2.indexInversionEndPoint = (m_IndexPatchContext2.baseIndexToInvert << 1) - 1;
                    insideBaseOffset = m_IndexPatchContext2.baseIndexToInvert;
                    outsideBaseOffset = outsideEdgePointBaseOffset;
                    SetUsingPatchedIndices2(true);
                }
                else
                {
                    m_IndexPatchContext.insidePointIndexDeltaToRealValue    = insideEdgePointBaseOffset;
                    m_IndexPatchContext.insidePointIndexBadValue            = numPointsForInsideEdge[parity] - 1;
                    m_IndexPatchContext.insidePointIndexReplacementValue    = edge0InsidePointBaseOffset;
                    m_IndexPatchContext.outsidePointIndexPatchBase          = m_IndexPatchContext.insidePointIndexBadValue+1; // past inside patched index range
                    m_IndexPatchContext.outsidePointIndexDeltaToRealValue   = outsideEdgePointBaseOffset
                                                                                - m_IndexPatchContext.outsidePointIndexPatchBase;
                    m_IndexPatchContext.outsidePointIndexBadValue           = m_IndexPatchContext.outsidePointIndexPatchBase
                                                                                + numPointsForOutsideEdge[edge] - 1;
                    m_IndexPatchContext.outsidePointIndexReplacementValue   = edge0OutsidePointBaseOffset;

                    insideBaseOffset = 0;
                    outsideBaseOffset = m_IndexPatchContext.outsidePointIndexPatchBase;
                    SetUsingPatchedIndices(true);
                }
            }
            else if( (edge == 2) && (ring == degeneratePointRing[parity]) )
            {
                m_IndexPatchContext2.baseIndexToInvert = insideEdgePointBaseOffset;
                m_IndexPatchContext2.cornerCaseBadValue = -1; // unused
                m_IndexPatchContext2.cornerCaseReplacementValue = -1; // unused
                m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert << 1;
                insideBaseOffset = m_IndexPatchContext2.baseIndexToInvert;
                outsideBaseOffset = outsideEdgePointBaseOffset;
                SetUsingPatchedIndices2(true);
            }
            else
            {
                insideBaseOffset = insideEdgePointBaseOffset;
                outsideBaseOffset = outsideEdgePointBaseOffset;
            }
            if( ring == startRing )
            {
                StitchTransition(/*baseIndexOffset: */m_NumIndices,
                               insideBaseOffset,processedTessFactors.insideTessFactorCtx[parity].numHalfTessFactorPoints,processedTessFactors.insideTessFactorParity[parity],
                               outsideBaseOffset,outsideTessFactorCtx[edge]->numHalfTessFactorPoints,outsideTessFactorParity[edge]);
            }
            else
            {
                StitchRegular(/*bTrapezoid*/true, DIAGONALS_MIRRORED,
                              /*baseIndexOffset: */m_NumIndices,
                              numPointsForInsideEdge[parity],
                              insideBaseOffset,outsideBaseOffset);
            }
            SetUsingPatchedIndices(false);
            SetUsingPatchedIndices2(false);
            m_NumIndices += numTriangles*3;
            outsideEdgePointBaseOffset += numPointsForOutsideEdge[edge] - 1;
            if( (edge == 2) && (ring == degeneratePointRing[parity]) )
            {
                insideEdgePointBaseOffset -= numPointsForInsideEdge[parity] - 1;
            }
            else
            {
                insideEdgePointBaseOffset += numPointsForInsideEdge[parity] - 1;
            }
            numPointsForOutsideEdge[edge] = numPointsForInsideEdge[parity];
        }
        if( startRing == ring )
        {
            for(edge = 0; edge < QUAD_EDGES; edge++ )
            {
                outsideTessFactorCtx[edge] = &processedTessFactors.insideTessFactorCtx[edge&1];
                outsideTessFactorParity[edge] = processedTessFactors.insideTessFactorParity[edge&1];
            }
        }
    }

    // Triangulate center - a row of quads if odd
    // This triangulation may be producing diagonals that are asymmetric about
    // the center of the patch in this region.
    if( (processedTessFactors.numPointsForInsideTessFactor[U] > processedTessFactors.numPointsForInsideTessFactor[V]) &&
        (TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[V] ) )
    {
        SetUsingPatchedIndices2(true);
        int stripNumQuads = (((processedTessFactors.numPointsForInsideTessFactor[U]>>1) - (processedTessFactors.numPointsForInsideTessFactor[V]>>1))<<1)+
                            ((TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[U] ) ? 2 : 1);
        m_IndexPatchContext2.baseIndexToInvert = outsideEdgePointBaseOffset + stripNumQuads + 2;
        m_IndexPatchContext2.cornerCaseBadValue = m_IndexPatchContext2.baseIndexToInvert;
        m_IndexPatchContext2.cornerCaseReplacementValue = outsideEdgePointBaseOffset;
        m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert +
                                                      m_IndexPatchContext2.baseIndexToInvert + stripNumQuads;
        StitchRegular(/*bTrapezoid*/false,DIAGONALS_INSIDE_TO_OUTSIDE,
                       /*baseIndexOffset: */m_NumIndices, /*numInsideEdgePoints:*/stripNumQuads+1,
                       /*insideEdgePointBaseOffset*/m_IndexPatchContext2.baseIndexToInvert,
                       outsideEdgePointBaseOffset+1);
        SetUsingPatchedIndices2(false);
        m_NumIndices += stripNumQuads*6;
    }
    else if((processedTessFactors.numPointsForInsideTessFactor[V] >= processedTessFactors.numPointsForInsideTessFactor[U]) &&
            (TESSELLATOR_PARITY_ODD == processedTessFactors.insideTessFactorParity[U]) )
    {
        SetUsingPatchedIndices2(true);
        int stripNumQuads = (((processedTessFactors.numPointsForInsideTessFactor[V]>>1) - (processedTessFactors.numPointsForInsideTessFactor[U]>>1))<<1)+
                            ((TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V] ) ? 2 : 1);
        m_IndexPatchContext2.baseIndexToInvert = outsideEdgePointBaseOffset + stripNumQuads + 1;
        m_IndexPatchContext2.cornerCaseBadValue = -1; // unused
        m_IndexPatchContext2.indexInversionEndPoint = m_IndexPatchContext2.baseIndexToInvert +
                                                      m_IndexPatchContext2.baseIndexToInvert + stripNumQuads;
		DIAGONALS diag = (TESSELLATOR_PARITY_EVEN == processedTessFactors.insideTessFactorParity[V]) ?
							DIAGONALS_INSIDE_TO_OUTSIDE : DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE;
        StitchRegular(/*bTrapezoid*/false,diag,
                       /*baseIndexOffset: */m_NumIndices, /*numInsideEdgePoints:*/stripNumQuads+1,
                       /*insideEdgePointBaseOffset*/m_IndexPatchContext2.baseIndexToInvert,
                       outsideEdgePointBaseOffset);
        SetUsingPatchedIndices2(false);
        m_NumIndices += stripNumQuads*6;
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::TessellateTriDomain
// User calls this
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::TessellateTriDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
                                        float insideTessFactor )
{
    PROCESSED_TESS_FACTORS_TRI processedTessFactors;
    TriProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Weq0,insideTessFactor,processedTessFactors);

    if( processedTessFactors.bPatchCulled )
    {
        m_NumPoints = 0;
        m_NumIndices = 0;
        return;
    }
    else if( processedTessFactors.bJustDoMinimumTessFactor )
    {
        DefinePoint(/*U*/0,/*V*/FXP_ONE,/*pointStorageOffset*/0); //V=1 (beginning of Ueq0 edge VW)
        DefinePoint(/*U*/0,/*V*/0,/*pointStorageOffset*/1); //W=1 (beginning of Veq0 edge WU)
        DefinePoint(/*U*/FXP_ONE,/*V*/0,/*pointStorageOffset*/2); //U=1 (beginning of Weq0 edge UV)
        m_NumPoints = 3;

        switch(m_outputPrimitive)
        {
        case D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CW:
        case D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CCW:
            // function orients them CCW if needed
            DefineClockwiseTriangle(0,1,2,/*indexStorageBaseOffset*/m_NumIndices);
            m_NumIndices = 3;
            break;
        case D3D11_TESSELLATOR_OUTPUT_POINT:
            DumpAllPoints();
            break;
        case D3D11_TESSELLATOR_OUTPUT_LINE:
            DumpAllPointsAsInOrderLineList();
            break;
        }
        return;
    }

    TriGeneratePoints(processedTessFactors);

    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_POINT )
    {
        DumpAllPoints();
        return;
    }
    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_LINE )
    {
        DumpAllPointsAsInOrderLineList();
        return;
    }

    TriGenerateConnectivity(processedTessFactors); // can be done in parallel to TriGeneratePoints()
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::TriProcessTessFactors
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::TriProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
                                            float insideTessFactor, PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
{
    // Is the patch culled?
    if( !(tessFactor_Ueq0 > 0) || // NaN will pass
        !(tessFactor_Veq0 > 0) ||
        !(tessFactor_Weq0 > 0) )
    {
        processedTessFactors.bPatchCulled = true;
        return;
    }
    else
    {
        processedTessFactors.bPatchCulled = false;
    }

    // Clamp edge TessFactors
    float lowerBound = 0.0, upperBound = 0.0;
    switch(m_originalPartitioning)
    {
        case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
        case D3D11_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
            break;

        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
            lowerBound = D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
            break;

        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
            break;
    }

    tessFactor_Ueq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Ueq0 ) );
    tessFactor_Veq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Veq0 ) );
    tessFactor_Weq0 = tess_fmin( upperBound, tess_fmax( lowerBound, tessFactor_Weq0 ) );

    if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
    {
        tessFactor_Ueq0 = ceil(tessFactor_Ueq0);
        tessFactor_Veq0 = ceil(tessFactor_Veq0);
        tessFactor_Weq0 = ceil(tessFactor_Weq0);
    }

    // Clamp inside TessFactors
    if(D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD == m_originalPartitioning)
    {
        if( (tessFactor_Ueq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
            (tessFactor_Veq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON) ||
            (tessFactor_Weq0 > MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON))
            // Don't need the same check for insideTessFactor for tri patches,
            // since there is only one insideTessFactor, as opposed to quad
            // patches which have 2 insideTessFactors.
        {
            // Force picture frame
            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR + EPSILON;
        }
    }

    insideTessFactor = tess_fmin( upperBound, tess_fmax( lowerBound, insideTessFactor ) );
    // Note the above clamps map NaN to lowerBound

    if( HWIntegerPartitioning()) // pow2 or integer, round to next int (hw doesn't care about pow2 distinction)
    {
        insideTessFactor = ceil(insideTessFactor);
    }

    // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
    m_NumPoints = 0;
    m_NumIndices = 0;

    // Process tessFactors
    float outsideTessFactor[TRI_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Weq0};
    int edge;
    if( HWIntegerPartitioning() )
    {
        for( edge = 0; edge < TRI_EDGES; edge++ )
        {
            int edgeEven = isEven(outsideTessFactor[edge]);
            processedTessFactors.outsideTessFactorParity[edge] = edgeEven ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
        }
        processedTessFactors.insideTessFactorParity = (isEven(insideTessFactor) || (FLOAT_ONE == insideTessFactor))
                                        ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
    }
    else
    {
        for( edge = 0; edge < TRI_EDGES; edge++ )
        {
            processedTessFactors.outsideTessFactorParity[edge] = m_originalParity;
        }
        processedTessFactors.insideTessFactorParity = m_originalParity;
    }

    // Save fixed point TessFactors
    for( edge = 0; edge < TRI_EDGES; edge++ )
    {
        processedTessFactors.outsideTessFactor[edge] = floatToFixed(outsideTessFactor[edge]);
    }
    processedTessFactors.insideTessFactor = floatToFixed(insideTessFactor);

    if( HWIntegerPartitioning() || Odd() )
    {
        // Special case if all TessFactors are 1
        if( (FXP_ONE == processedTessFactors.insideTessFactor) &&
            (FXP_ONE == processedTessFactors.outsideTessFactor[Ueq0]) &&
            (FXP_ONE == processedTessFactors.outsideTessFactor[Veq0]) &&
            (FXP_ONE == processedTessFactors.outsideTessFactor[Weq0]) )
        {
            processedTessFactors.bJustDoMinimumTessFactor = true;
            return;
        }
    }
    processedTessFactors.bJustDoMinimumTessFactor = false;

    // Compute per-TessFactor metadata
    for(edge = 0; edge < TRI_EDGES; edge++ )
    {
        SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
        ComputeTessFactorContext(processedTessFactors.outsideTessFactor[edge], processedTessFactors.outsideTessFactorCtx[edge]);
    }
    SetTessellationParity(processedTessFactors.insideTessFactorParity);
    ComputeTessFactorContext(processedTessFactors.insideTessFactor, processedTessFactors.insideTessFactorCtx);

    // Compute some initial data.

    // outside edge offsets and storage
    for(edge = 0; edge < TRI_EDGES; edge++ )
    {
        SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
        processedTessFactors.numPointsForOutsideEdge[edge] = NumPointsForTessFactor(processedTessFactors.outsideTessFactor[edge]);
        m_NumPoints += processedTessFactors.numPointsForOutsideEdge[edge];
    }
    m_NumPoints -= 3;

    // inside edge offsets
    SetTessellationParity(processedTessFactors.insideTessFactorParity);
    processedTessFactors.numPointsForInsideTessFactor = NumPointsForTessFactor(processedTessFactors.insideTessFactor);
    {
        int pointCountMin = Odd() ? 4 : 3;
        // max() allows degenerate transition regions when inside TessFactor == 1
        processedTessFactors.numPointsForInsideTessFactor = max(pointCountMin,processedTessFactors.numPointsForInsideTessFactor);
    }

    processedTessFactors.insideEdgePointBaseOffset = m_NumPoints;

    // inside storage, including interior edges above
    {
        int numInteriorRings = (processedTessFactors.numPointsForInsideTessFactor >> 1) - 1;
        int numInteriorPoints;
        if( Odd() )
        {
            numInteriorPoints = TRI_EDGES*(numInteriorRings*(numInteriorRings+1) - numInteriorRings);
        }
        else
        {
            numInteriorPoints = TRI_EDGES*(numInteriorRings*(numInteriorRings+1)) + 1;
        }
        m_NumPoints += numInteriorPoints;
    }

}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::TriGeneratePoints
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::TriGeneratePoints( const PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
{
    // Generate exterior ring edge points, clockwise starting from point V (VW, the U==0 edge)
    int pointOffset = 0;
    int edge;
    for(edge = 0; edge < TRI_EDGES; edge++ )
    {
        int parity = edge&0x1;
        int startPoint = 0;
        int endPoint = processedTessFactors.numPointsForOutsideEdge[edge] - 1;
        for(int p = startPoint; p < endPoint; p++, pointOffset++) // don't include end, since next edge starts with it.
        {
            FXP fxpParam;
            int q = (parity) ? p : endPoint - p; // whether to reverse point order given we are defining V or U (W implicit):
                                                 // edge0, VW, has V decreasing, so reverse 1D points below
                                                 // edge1, WU, has U increasing, so don't reverse 1D points  below
                                                 // edge2, UV, has U decreasing, so reverse 1D points below
            SetTessellationParity(processedTessFactors.outsideTessFactorParity[edge]);
            PlacePointIn1D(processedTessFactors.outsideTessFactorCtx[edge],q,fxpParam);
            if( edge == 0 )
            {
                DefinePoint(/*U*/0,
                            /*V*/fxpParam,
                            /*pointStorageOffset*/pointOffset);
            }
            else
            {
                DefinePoint(/*U*/fxpParam,
                            /*V*/(edge == 2) ? FXP_ONE - fxpParam : 0,
                            /*pointStorageOffset*/pointOffset);
            }
        }
    }

    // Generate interior ring points, clockwise spiralling in
    SetTessellationParity(processedTessFactors.insideTessFactorParity);
    static const int startRing = 1;
    int numRings = (processedTessFactors.numPointsForInsideTessFactor >> 1);
    for(int ring = startRing; ring < numRings; ring++)
    {
        int startPoint = ring;
        int endPoint = processedTessFactors.numPointsForInsideTessFactor - 1 - startPoint;

        for(edge = 0; edge < TRI_EDGES; edge++ )
        {
            int parity = edge&0x1;
            int perpendicularAxisPoint = startPoint;
            FXP fxpPerpParam;
            PlacePointIn1D(processedTessFactors.insideTessFactorCtx,perpendicularAxisPoint,fxpPerpParam);
            fxpPerpParam *= FXP_TWO_THIRDS; // Map location to the right size in barycentric space.
                                         // I (amarp) can draw a picture to explain.
                                         // We know this fixed point math won't over/underflow
            fxpPerpParam = (fxpPerpParam+FXP_ONE_HALF/*round*/)>>FXP_FRACTION_BITS; // get back to n.16
            for(int p = startPoint; p < endPoint; p++, pointOffset++) // don't include end: next edge starts with it.
            {
                FXP fxpParam;
                int q = (parity) ? p : endPoint - (p - startPoint); // whether to reverse point given we are defining V or U (W implicit):
                                                         // edge0, VW, has V decreasing, so reverse 1D points below
                                                         // edge1, WU, has U increasing, so don't reverse 1D points  below
                                                         // edge2, UV, has U decreasing, so reverse 1D points below
                PlacePointIn1D(processedTessFactors.insideTessFactorCtx,q,fxpParam);
                // edge0 VW, has perpendicular parameter U constant
                // edge1 WU, has perpendicular parameter V constant
                // edge2 UV, has perpendicular parameter W constant
                const unsigned int deriv = 2; // reciprocal is the rate of change of edge-parallel parameters as they are pushed into the triangle
                switch(edge)
                {
                case 0:
                    DefinePoint(/*U*/fxpPerpParam,
                                /*V*/fxpParam - (fxpPerpParam+1/*round*/)/deriv, // we know this fixed point math won't over/underflow
                                /*pointStorageOffset*/pointOffset);
                    break;
                case 1:
                    DefinePoint(/*U*/fxpParam - (fxpPerpParam+1/*round*/)/deriv,// we know this fixed point math won't over/underflow
                                /*V*/fxpPerpParam,
                                /*pointStorageOffset*/pointOffset);
                    break;
                case 2:
                    DefinePoint(/*U*/fxpParam - (fxpPerpParam+1/*round*/)/deriv,// we know this fixed point math won't over/underflow
                                /*V*/FXP_ONE - (fxpParam - (fxpPerpParam+1/*round*/)/deriv) - fxpPerpParam,// we know this fixed point math won't over/underflow
                                /*pointStorageOffset*/pointOffset);
                    break;
                }
            }
        }
    }
    if( !Odd() )
    {
        // Last point is the point at the center.
        DefinePoint(/*U*/FXP_ONE_THIRD,
                    /*V*/FXP_ONE_THIRD,
                    /*pointStorageOffset*/pointOffset);
    }
}
//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::TriGenerateConnectivity
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::TriGenerateConnectivity( const PROCESSED_TESS_FACTORS_TRI& processedTessFactors )
{
    // Generate primitives for all the concentric rings, one side at a time for each ring
    static const int startRing = 1;
    int numRings = ((processedTessFactors.numPointsForInsideTessFactor+1) >> 1); // +1 is so even tess includes the center point, which we want to now
    const TESS_FACTOR_CONTEXT* outsideTessFactorCtx[TRI_EDGES] = {&processedTessFactors.outsideTessFactorCtx[Ueq0],
                                            &processedTessFactors.outsideTessFactorCtx[Veq0],
                                            &processedTessFactors.outsideTessFactorCtx[Weq0]};
    TESSELLATOR_PARITY outsideTessFactorParity[TRI_EDGES] = {processedTessFactors.outsideTessFactorParity[Ueq0],
                                            processedTessFactors.outsideTessFactorParity[Veq0],
                                            processedTessFactors.outsideTessFactorParity[Weq0]};
    int numPointsForOutsideEdge[TRI_EDGES] = {processedTessFactors.numPointsForOutsideEdge[Ueq0],
                                              processedTessFactors.numPointsForOutsideEdge[Veq0],
                                              processedTessFactors.numPointsForOutsideEdge[Weq0]};

    int insideEdgePointBaseOffset = processedTessFactors.insideEdgePointBaseOffset;
    int outsideEdgePointBaseOffset = 0;
    int edge;
    for(int ring = startRing; ring < numRings; ring++)
    {
        int numPointsForInsideEdge = processedTessFactors.numPointsForInsideTessFactor - 2*ring;
        int edge0InsidePointBaseOffset = insideEdgePointBaseOffset;
        int edge0OutsidePointBaseOffset = outsideEdgePointBaseOffset;
        for(edge = 0; edge < TRI_EDGES; edge++ )
        {
            int numTriangles = numPointsForInsideEdge + numPointsForOutsideEdge[edge] - 2;

            int insideBaseOffset;
            int outsideBaseOffset;
            if( edge == 2 )
            {
                m_IndexPatchContext.insidePointIndexDeltaToRealValue    = insideEdgePointBaseOffset;
                m_IndexPatchContext.insidePointIndexBadValue            = numPointsForInsideEdge - 1;
                m_IndexPatchContext.insidePointIndexReplacementValue    = edge0InsidePointBaseOffset;
                m_IndexPatchContext.outsidePointIndexPatchBase          = m_IndexPatchContext.insidePointIndexBadValue+1; // past inside patched index range
                m_IndexPatchContext.outsidePointIndexDeltaToRealValue   = outsideEdgePointBaseOffset
                                                                            - m_IndexPatchContext.outsidePointIndexPatchBase;
                m_IndexPatchContext.outsidePointIndexBadValue           = m_IndexPatchContext.outsidePointIndexPatchBase
                                                                            + numPointsForOutsideEdge[edge] - 1;
                m_IndexPatchContext.outsidePointIndexReplacementValue   = edge0OutsidePointBaseOffset;
                SetUsingPatchedIndices(true);
                insideBaseOffset = 0;
                outsideBaseOffset = m_IndexPatchContext.outsidePointIndexPatchBase;
            }
            else
            {
                insideBaseOffset = insideEdgePointBaseOffset;
                outsideBaseOffset = outsideEdgePointBaseOffset;
            }
            if( ring == startRing )
            {
                StitchTransition(/*baseIndexOffset: */m_NumIndices,
                               insideBaseOffset,processedTessFactors.insideTessFactorCtx.numHalfTessFactorPoints,processedTessFactors.insideTessFactorParity,
                               outsideBaseOffset,outsideTessFactorCtx[edge]->numHalfTessFactorPoints,outsideTessFactorParity[edge]);
            }
            else
            {
                StitchRegular(/*bTrapezoid*/true, DIAGONALS_MIRRORED,
                              /*baseIndexOffset: */m_NumIndices,
                              numPointsForInsideEdge,
                              insideBaseOffset,outsideBaseOffset);
            }
            if( 2 == edge )
            {
                SetUsingPatchedIndices(false);
            }
            m_NumIndices += numTriangles*3;
            outsideEdgePointBaseOffset += numPointsForOutsideEdge[edge] - 1;
            insideEdgePointBaseOffset += numPointsForInsideEdge - 1;
            numPointsForOutsideEdge[edge] = numPointsForInsideEdge;
        }
        if( startRing == ring )
        {
            for(edge = 0; edge < TRI_EDGES; edge++ )
            {
                outsideTessFactorCtx[edge] = &processedTessFactors.insideTessFactorCtx;
                outsideTessFactorParity[edge] = processedTessFactors.insideTessFactorParity;
            }
        }
    }
    if( Odd() )
    {
        // Triangulate center (a single triangle)
        DefineClockwiseTriangle(outsideEdgePointBaseOffset, outsideEdgePointBaseOffset+1, outsideEdgePointBaseOffset+2,
                       m_NumIndices);
        m_NumIndices += 3;
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::TessellateIsoLineDomain
// User calls this.
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::TessellateIsoLineDomain( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail )
{
    PROCESSED_TESS_FACTORS_ISOLINE processedTessFactors;
    IsoLineProcessTessFactors(TessFactor_V_LineDensity,TessFactor_U_LineDetail,processedTessFactors);
    if( processedTessFactors.bPatchCulled )
    {
        m_NumPoints = 0;
        m_NumIndices = 0;
        return;
    }
    IsoLineGeneratePoints(processedTessFactors);
    IsoLineGenerateConnectivity(processedTessFactors); // can be done in parallel to IsoLineGeneratePoints
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::IsoLineProcessTessFactors
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::IsoLineProcessTessFactors( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail,
                                                PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
{
    // Is the patch culled?
    if( !(TessFactor_V_LineDensity > 0) || // NaN will pass
        !(TessFactor_U_LineDetail > 0) )
    {
        processedTessFactors.bPatchCulled = true;
        return;
    }
    else
    {
        processedTessFactors.bPatchCulled = false;
    }

    // Clamp edge TessFactors
    float lowerBound = 0.0, upperBound = 0.0;
    switch(m_originalPartitioning)
    {
        case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
        case D3D11_TESSELLATOR_PARTITIONING_POW2: // don�t care about pow2 distinction for validation, just treat as integer
            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
            break;

        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
            lowerBound = D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
            break;

        case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
            lowerBound = D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
            upperBound = D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
            break;
    }

    TessFactor_V_LineDensity = tess_fmin( D3D11_TESSELLATOR_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR,
                                    tess_fmax( D3D11_TESSELLATOR_MIN_ISOLINE_DENSITY_TESSELLATION_FACTOR, TessFactor_V_LineDensity ) );
    TessFactor_U_LineDetail = tess_fmin( upperBound, tess_fmax( lowerBound, TessFactor_U_LineDetail ) );

    // Reset our vertex and index buffers.  We have enough storage for the max tessFactor.
    m_NumPoints = 0;
    m_NumIndices = 0;

    // Process tessFactors
    if( HWIntegerPartitioning() )
    {
        TessFactor_U_LineDetail = ceil(TessFactor_U_LineDetail);
        processedTessFactors.lineDetailParity = isEven(TessFactor_U_LineDetail) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
    }
    else
    {
        processedTessFactors.lineDetailParity = m_originalParity;
    }

    FXP fxpTessFactor_U_LineDetail = floatToFixed(TessFactor_U_LineDetail);

    SetTessellationParity(processedTessFactors.lineDetailParity);

    ComputeTessFactorContext(fxpTessFactor_U_LineDetail, processedTessFactors.lineDetailTessFactorCtx);
    processedTessFactors.numPointsPerLine = NumPointsForTessFactor(fxpTessFactor_U_LineDetail);

    OverridePartitioning(D3D11_TESSELLATOR_PARTITIONING_INTEGER);

    TessFactor_V_LineDensity = ceil(TessFactor_V_LineDensity);
    processedTessFactors.lineDensityParity = isEven(TessFactor_V_LineDensity) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
    SetTessellationParity(processedTessFactors.lineDensityParity);
    FXP fxpTessFactor_V_LineDensity = floatToFixed(TessFactor_V_LineDensity);
    ComputeTessFactorContext(fxpTessFactor_V_LineDensity, processedTessFactors.lineDensityTessFactorCtx);

    processedTessFactors.numLines = NumPointsForTessFactor(fxpTessFactor_V_LineDensity) - 1; // don't draw last line at V == 1.

    RestorePartitioning();

    // Compute some initial data.

    // outside edge offsets
    m_NumPoints = processedTessFactors.numPointsPerLine * processedTessFactors.numLines;
    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_POINT )
    {
        m_NumIndices = m_NumPoints;
    }
    else // line
    {
        m_NumIndices = processedTessFactors.numLines*(processedTessFactors.numPointsPerLine-1)*2;
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::IsoLineGeneratePoints
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::IsoLineGeneratePoints( const PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
{
    int line, pointOffset;
    for(line = 0, pointOffset = 0; line < processedTessFactors.numLines; line++)
    {
        for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
        {
            FXP fxpU,fxpV;
            SetTessellationParity(processedTessFactors.lineDensityParity);
            PlacePointIn1D(processedTessFactors.lineDensityTessFactorCtx,line,fxpV);

            SetTessellationParity(processedTessFactors.lineDetailParity);
            PlacePointIn1D(processedTessFactors.lineDetailTessFactorCtx,point,fxpU);

            DefinePoint(fxpU,fxpV,pointOffset++);
        }
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::IsoLineGenerateConnectivity
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::IsoLineGenerateConnectivity( const PROCESSED_TESS_FACTORS_ISOLINE& processedTessFactors )
{
    int line, pointOffset, indexOffset;
    if( m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_POINT )
    {
        for(line = 0, pointOffset = 0, indexOffset = 0; line < processedTessFactors.numLines; line++)
        {
            for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
            {
                DefineIndex(pointOffset++,indexOffset++);
            }
        }
    }
    else // line
    {
        for(line = 0, pointOffset = 0, indexOffset = 0; line < processedTessFactors.numLines; line++)
        {
            for(int point = 0; point < processedTessFactors.numPointsPerLine; point++)
            {
                if( point > 0 )
                {
                    DefineIndex(pointOffset-1,indexOffset++);
                    DefineIndex(pointOffset,indexOffset++);
                }
                pointOffset++;
            }
        }
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::GetPointCount
// User calls this.
//---------------------------------------------------------------------------------------------------------------------------------
int CHWTessellator::GetPointCount()
{
    return m_NumPoints;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::GetIndexCount()
// User calls this.
//---------------------------------------------------------------------------------------------------------------------------------
int CHWTessellator::GetIndexCount()
{
    return m_NumIndices;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::GetPoints()
// User calls this.
//---------------------------------------------------------------------------------------------------------------------------------
DOMAIN_POINT* CHWTessellator::GetPoints()
{
    return m_Point;
}
//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::GetIndices()
// User calls this.
//---------------------------------------------------------------------------------------------------------------------------------
int* CHWTessellator::GetIndices()
{
    return m_Index;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::DefinePoint()
//---------------------------------------------------------------------------------------------------------------------------------
int CHWTessellator::DefinePoint(FXP fxpU, FXP fxpV, int pointStorageOffset)
{
//    WCHAR foo[80];
//    StringCchPrintf(foo,80,L"off:%d, uv=(%f,%f)\n",pointStorageOffset,fixedToFloat(fxpU),fixedToFloat(fxpV));
//    OutputDebugString(foo);
    m_Point[pointStorageOffset].u = fixedToFloat(fxpU);
    m_Point[pointStorageOffset].v = fixedToFloat(fxpV);
    return pointStorageOffset;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::DefineIndex()
//--------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::DefineIndex(int index, int indexStorageOffset)
{
    index = PatchIndexValue(index);
//    WCHAR foo[80];
//    StringCchPrintf(foo,80,L"off:%d, idx=%d, uv=(%f,%f)\n",indexStorageOffset,index,m_Point[index].u,m_Point[index].v);
//    OutputDebugString(foo);
    m_Index[indexStorageOffset] = index;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::DefineClockwiseTriangle()
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::DefineClockwiseTriangle(int index0, int index1, int index2, int indexStorageBaseOffset)
{
    // inputs a clockwise triangle, stores a CW or CCW triangle depending on the state
    DefineIndex(index0,indexStorageBaseOffset);
    bool bWantClockwise = (m_outputPrimitive == D3D11_TESSELLATOR_OUTPUT_TRIANGLE_CW) ? true : false;
    if( bWantClockwise )
    {
        DefineIndex(index1,indexStorageBaseOffset+1);
        DefineIndex(index2,indexStorageBaseOffset+2);
    }
    else
    {
        DefineIndex(index2,indexStorageBaseOffset+1);
        DefineIndex(index1,indexStorageBaseOffset+2);
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::DumpAllPoints()
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::DumpAllPoints()
{
    for( int p = 0; p < m_NumPoints; p++ )
    {
        DefineIndex(p,m_NumIndices++);
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::DumpAllPointsAsInOrderLineList()
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::DumpAllPointsAsInOrderLineList()
{
    for( int p = 1; p < m_NumPoints; p++ )
    {
        DefineIndex(p-1,m_NumIndices++);
        DefineIndex(p,m_NumIndices++);
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// RemoveMSB
//---------------------------------------------------------------------------------------------------------------------------------
int RemoveMSB(int val)
{
    int check;
    if( val <= 0x0000ffff ) { check = ( val <= 0x000000ff ) ? 0x00000080 : 0x00008000; }
    else                    { check = ( val <= 0x00ffffff ) ? 0x00800000 : 0x80000000; }
    for( int i = 0; i < 8; i++, check >>= 1 ) { if( val & check ) return (val & ~check); }
    return 0;
}
//---------------------------------------------------------------------------------------------------------------------------------
// GetMSB
//---------------------------------------------------------------------------------------------------------------------------------
int GetMSB(int val)
{
    int check;
    if( val <= 0x0000ffff ) { check = ( val <= 0x000000ff ) ? 0x00000080 : 0x00008000; }
    else                    { check = ( val <= 0x00ffffff ) ? 0x00800000 : 0x80000000; }
    for( int i = 0; i < 8; i++, check >>= 1 ) { if( val & check ) return check; }
    return 0;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::CleanseParameter()
//---------------------------------------------------------------------------------------------------------------------------------
/* NOTHING TO DO FOR FIXED POINT ARITHMETIC!
void CHWTessellator::CleanseParameter(float& parameter)
{
    // Clean up [0..1] parameter to guarantee that (1 - (1 - parameter)) == parameter.
    parameter = 1.0f - parameter;
    parameter = 1.0f - parameter;

}
*/
//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::NumPointsForTessFactor()
//---------------------------------------------------------------------------------------------------------------------------------
int CHWTessellator::NumPointsForTessFactor( FXP fxpTessFactor )
{
    int numPoints;
    if( Odd() )
    {
        numPoints = (fxpCeil(FXP_ONE_HALF + (fxpTessFactor+1/*round*/)/2)*2)>>FXP_FRACTION_BITS;
    }
    else
    {
        numPoints = ((fxpCeil((fxpTessFactor+1/*round*/)/2)*2)>>FXP_FRACTION_BITS)+1;
    }
    return numPoints;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::ComputeTessFactorContext()
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::ComputeTessFactorContext( FXP fxpTessFactor, TESS_FACTOR_CONTEXT& TessFactorCtx )
{
    FXP fxpHalfTessFactor = (fxpTessFactor+1/*round*/)/2;
    if( Odd() || (fxpHalfTessFactor == FXP_ONE_HALF)) // fxpHalfTessFactor == 1/2 if TessFactor is 1, but we're pretending we are even.
    {
        fxpHalfTessFactor += FXP_ONE_HALF;
    }
    FXP fxpFloorHalfTessFactor = fxpFloor(fxpHalfTessFactor);
    FXP fxpCeilHalfTessFactor = fxpCeil(fxpHalfTessFactor);
    TessFactorCtx.fxpHalfTessFactorFraction = fxpHalfTessFactor - fxpFloorHalfTessFactor;
    //CleanseParameter(TessFactorCtx.fxpHalfTessFactorFraction);
    TessFactorCtx.numHalfTessFactorPoints = (fxpCeilHalfTessFactor>>FXP_FRACTION_BITS); // for EVEN, we don't include the point always fixed at the midpoint of the TessFactor
    if( fxpCeilHalfTessFactor == fxpFloorHalfTessFactor )
    {
        TessFactorCtx.splitPointOnFloorHalfTessFactor =  /*pick value to cause this to be ignored*/ TessFactorCtx.numHalfTessFactorPoints+1;
    }
    else if( Odd() )
    {
        if( fxpFloorHalfTessFactor == FXP_ONE )
        {
            TessFactorCtx.splitPointOnFloorHalfTessFactor = 0;
        }
        else
        {
#ifdef ALLOW_XBOX_360_COMPARISON
            if( m_bXBox360Mode )
                TessFactorCtx.splitPointOnFloorHalfTessFactor = TessFactorCtx.numHalfTessFactorPoints-2;
            else
#endif
				TessFactorCtx.splitPointOnFloorHalfTessFactor = (RemoveMSB((fxpFloorHalfTessFactor>>FXP_FRACTION_BITS)-1)<<1) + 1;
        }
    }
    else
    {
#ifdef ALLOW_XBOX_360_COMPARISON
        if( m_bXBox360Mode )
            TessFactorCtx.splitPointOnFloorHalfTessFactor = TessFactorCtx.numHalfTessFactorPoints-1;
        else
#endif
			TessFactorCtx.splitPointOnFloorHalfTessFactor = (RemoveMSB(fxpFloorHalfTessFactor>>FXP_FRACTION_BITS)<<1) + 1;
    }
    int numFloorSegments = (fxpFloorHalfTessFactor * 2)>>FXP_FRACTION_BITS;
    int numCeilSegments = (fxpCeilHalfTessFactor * 2)>>FXP_FRACTION_BITS;
    if( Odd() )
    {
        numFloorSegments -= 1;
        numCeilSegments -= 1;
    }
    TessFactorCtx.fxpInvNumSegmentsOnFloorTessFactor = s_fixedReciprocal[numFloorSegments];
    TessFactorCtx.fxpInvNumSegmentsOnCeilTessFactor = s_fixedReciprocal[numCeilSegments];
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::PlacePointIn1D()
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::PlacePointIn1D( const TESS_FACTOR_CONTEXT& TessFactorCtx, int point, FXP& fxpLocation )
{
    bool bFlip;
    if( point >= TessFactorCtx.numHalfTessFactorPoints )
    {
        point = (TessFactorCtx.numHalfTessFactorPoints << 1) - point;
        if( Odd() )
        {
            point -= 1;
        }
        bFlip = true;
    }
    else
    {
        bFlip = false;
    }
    if( point == TessFactorCtx.numHalfTessFactorPoints )
    {
        fxpLocation = FXP_ONE_HALF; // special casing middle since 16 bit fixed math below can't reproduce 0.5 exactly
        return;
    }
    unsigned int indexOnCeilHalfTessFactor = point;
    unsigned int indexOnFloorHalfTessFactor = indexOnCeilHalfTessFactor;
    if( point > TessFactorCtx.splitPointOnFloorHalfTessFactor )
    {
        indexOnFloorHalfTessFactor -= 1;
    }
    // For the fixed point multiplies below, we know the results are <= 16 bits because
    // the locations on the halfTessFactor are <= half the number of segments for the total TessFactor.
    // So a number divided by a number that is at least twice as big will give
    // a result no bigger than 0.5 (which in fixed point is 16 bits in our case)
    FXP fxpLocationOnFloorHalfTessFactor = indexOnFloorHalfTessFactor * TessFactorCtx.fxpInvNumSegmentsOnFloorTessFactor;
    FXP fxpLocationOnCeilHalfTessFactor = indexOnCeilHalfTessFactor * TessFactorCtx.fxpInvNumSegmentsOnCeilTessFactor;

    // Since we know the numbers calculated above are <= fixed point 0.5, and the equation
    // below is just lerping between two values <= fixed point 0.5 (0x00008000), then we know
    // that the final result before shifting by 16 bits is no larger than 0x80000000.  Once we
    // shift that down by 16, we get the result of lerping 2 numbers <= 0.5, which is obviously
    // at most 0.5 (0x00008000)
    fxpLocation = fxpLocationOnFloorHalfTessFactor * (FXP_ONE - TessFactorCtx.fxpHalfTessFactorFraction) +
                  fxpLocationOnCeilHalfTessFactor * (TessFactorCtx.fxpHalfTessFactorFraction);
    fxpLocation = (fxpLocation + FXP_ONE_HALF/*round*/) >> FXP_FRACTION_BITS; // get back to n.16
    /* Commenting out floating point version.  Note the parameter cleansing it does is not needed in fixed point.
    if( bFlip )
        location = 1.0f - location; // complement produces cleansed result.
    else
        CleanseParameter(location);
    */
    if( bFlip )
    {
        fxpLocation = FXP_ONE - fxpLocation;
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::StitchRegular
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::StitchRegular(bool bTrapezoid,DIAGONALS diagonals,
                                 int baseIndexOffset, int numInsideEdgePoints,
                                 int insideEdgePointBaseOffset, int outsideEdgePointBaseOffset)
{
    int insidePoint = insideEdgePointBaseOffset;
    int outsidePoint = outsideEdgePointBaseOffset;
    if( bTrapezoid )
    {
        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
        baseIndexOffset += 3; outsidePoint++;
    }
    int p;
    switch( diagonals )
    {
    case DIAGONALS_INSIDE_TO_OUTSIDE:
        // Diagonals pointing from inside edge forward towards outside edge
        for( p = 0; p < numInsideEdgePoints-1; p++ )
        {
            DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;

            DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            insidePoint++; outsidePoint++;
        }
        break;
    case DIAGONALS_INSIDE_TO_OUTSIDE_EXCEPT_MIDDLE: // Assumes ODD tessellation
        // Diagonals pointing from outside edge forward towards inside edge

        // First half
        for( p = 0; p < numInsideEdgePoints/2-1; p++ )
        {
            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
            baseIndexOffset += 3;
            DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            insidePoint++; outsidePoint++;
        }

        // Middle
        DefineClockwiseTriangle(outsidePoint,insidePoint+1,insidePoint,baseIndexOffset);
        baseIndexOffset += 3;
        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
        baseIndexOffset += 3;
        insidePoint++; outsidePoint++; p+=2;

        // Second half
        for( ; p < numInsideEdgePoints; p++ )
        {
            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
            baseIndexOffset += 3;
            DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            insidePoint++; outsidePoint++;
        }
        break;
    case DIAGONALS_MIRRORED:
        // First half, diagonals pointing from outside of outside edge to inside of inside edge
        for( p = 0; p < numInsideEdgePoints/2; p++ )
        {
            DefineClockwiseTriangle(outsidePoint,insidePoint+1,insidePoint,baseIndexOffset);
            baseIndexOffset += 3;
            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            insidePoint++; outsidePoint++;
        }
        // Second half, diagonals pointing from inside of inside edge to outside of outside edge
        for( ; p < numInsideEdgePoints-1; p++ )
        {
            DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            DefineClockwiseTriangle(insidePoint,outsidePoint+1,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            insidePoint++; outsidePoint++;
        }
        break;
    }
    if( bTrapezoid )
    {
        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
        baseIndexOffset += 3;
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::StitchTransition()
//---------------------------------------------------------------------------------------------------------------------------------
void CHWTessellator::StitchTransition(int baseIndexOffset,
                                    int insideEdgePointBaseOffset, int insideNumHalfTessFactorPoints,
                                    TESSELLATOR_PARITY insideEdgeTessFactorParity,
                                    int outsideEdgePointBaseOffset, int outsideNumHalfTessFactorPoints,
                                    TESSELLATOR_PARITY outsideTessFactorParity
)
{

#ifdef ALLOW_XBOX_360_COMPARISON
    // Tables to assist in the stitching of 2 rows of points having arbitrary TessFactors.
    // The stitching order is governed by Ruler Function vertex split ordering (see external documentation).
    //
    // The contents of the finalPointPositionTable are where vertex i [0..32] ends up on the half-edge
    // at the max tessellation amount given ruler-function split order.
    // Recall the other half of an edge is mirrored, so we only need to deal with one half.
    // This table is used to decide when to advance a point on the interior or exterior.
    // It supports odd TessFactor up to 65 and even TessFactor up to 64.
    static const int _finalPointPositionTable[33] =
            { 0, 32, 16, 8, 17, 4, 18, 9, 19, 2, 20, 10, 21, 5, 22, 11, 23,
              1, 24, 12, 25, 6, 26, 13, 27, 3, 28, 14, 29, 7, 30, 15, 31 };
    // The loopStart and loopEnd tables below just provide optimal loop bounds for the
    // stitching algorithm further below, for any given halfTssFactor.
    // There is probably a better way to encode this...

    // loopStart[halfTessFactor] encodes the FIRST entry other that [0] in finalPointPositionTable[] above which is
    // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
    static const int _loopStart[33] =
            {1,1,17,9,9,5,5,5,5,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
    // loopStart[halfTessFactor] encodes the LAST entry in finalPointPositionTable[] above which is
    // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
    static const int _loopEnd[33] =
            {0,0,17,17,25,25,25,25,29,29,29,29,29,29,29,29,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,32};
    const int* finalPointPositionTable;
    const int* loopStart;
    const int* loopEnd;
    if( m_bXBox360Mode )
    {
        // The XBox360 vertex introduction order is always from the center of the edge.
        // So the final positions of points on the half-edge are this trivial table.
        static const int XBOXfinalPointPositionTable[33] =
                { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17,
                  18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 };
        // loopStart and loopEnd (meaning described above) also become trivial for XBox360 splitting.
        static const int XBOXloopStart[33] =
                {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1};
        static const int XBOXloopEnd[33] =
                {0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31};

        finalPointPositionTable = XBOXfinalPointPositionTable;
        loopStart = XBOXloopStart;
        loopEnd = XBOXloopEnd;
    }
    else
    {
        finalPointPositionTable = _finalPointPositionTable;
        loopStart = _loopStart;
        loopEnd =_loopEnd;
    }
#else
    // Tables to assist in the stitching of 2 rows of points having arbitrary TessFactors.
    // The stitching order is governed by Ruler Function vertex split ordering (see external documentation).
    //
    // The contents of the finalPointPositionTable are where vertex i [0..33] ends up on the half-edge
    // at the max tessellation amount given ruler-function split order.
    // Recall the other half of an edge is mirrored, so we only need to deal with one half.
    // This table is used to decide when to advance a point on the interior or exterior.
    // It supports odd TessFactor up to 65 and even TessFactor up to 64.
    static const int finalPointPositionTable[33] =
            { 0, 32, 16, 8, 17, 4, 18, 9, 19, 2, 20, 10, 21, 5, 22, 11, 23,
              1, 24, 12, 25, 6, 26, 13, 27, 3, 28, 14, 29, 7, 30, 15, 31 };

    // The loopStart and loopEnd tables below just provide optimal loop bounds for the
    // stitching algorithm further below, for any given halfTssFactor.
    // There is probably a better way to encode this...

    // loopStart[halfTessFactor] encodes the FIRST entry in finalPointPositionTable[] above which is
    // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
    static const int loopStart[33] =
            {1,1,17,9,9,5,5,5,5,3,3,3,3,3,3,3,3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2};
    // loopStart[halfTessFactor] encodes the LAST entry in finalPointPositionTable[] above which is
    // less than halfTessFactor.  Exceptions are entry 0 and 1, which are set up to skip the loop.
    static const int loopEnd[33] =
            {0,0,17,17,25,25,25,25,29,29,29,29,29,29,29,29,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,31,32};
#endif
    if( TESSELLATOR_PARITY_ODD == insideEdgeTessFactorParity )
    {
        insideNumHalfTessFactorPoints -= 1;
    }
    if( TESSELLATOR_PARITY_ODD == outsideTessFactorParity )
    {
        outsideNumHalfTessFactorPoints -= 1;
    }
    // Walk first half
    int outsidePoint = outsideEdgePointBaseOffset;
    int insidePoint = insideEdgePointBaseOffset;

    // iStart,iEnd are a small optimization so the loop below doesn't have to go from 0 up to 31
    int iStart = min(loopStart[insideNumHalfTessFactorPoints],loopStart[outsideNumHalfTessFactorPoints]);
    int iEnd = max(loopEnd[insideNumHalfTessFactorPoints],loopEnd[outsideNumHalfTessFactorPoints]);

    if( finalPointPositionTable[0] < outsideNumHalfTessFactorPoints ) // since we dont' start the loop at 0 below, we need a special case.
    {
        // Advance outside
        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
        baseIndexOffset += 3; outsidePoint++;
    }

    for(int i = iStart; i <= iEnd; i++)
    {
        if( /*(i>0) && <-- not needed since iStart is never 0*/(finalPointPositionTable[i] < insideNumHalfTessFactorPoints))
        {
            // Advance inside
            DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3; insidePoint++;
        }
        if((finalPointPositionTable[i] < outsideNumHalfTessFactorPoints))
        {
            // Advance outside
            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
            baseIndexOffset += 3; outsidePoint++;
        }
    }

    if( (insideEdgeTessFactorParity != outsideTessFactorParity) || (insideEdgeTessFactorParity == TESSELLATOR_PARITY_ODD))
    {
        if( insideEdgeTessFactorParity == outsideTessFactorParity )
        {
            // Quad in the middle
            DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            DefineClockwiseTriangle(insidePoint+1,outsidePoint,outsidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            insidePoint++;
            outsidePoint++;
        }
        else if( TESSELLATOR_PARITY_EVEN == insideEdgeTessFactorParity )
        {
            // Triangle pointing inside
            DefineClockwiseTriangle(insidePoint,outsidePoint,outsidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            outsidePoint++;
        }
        else
        {
            // Triangle pointing outside
            DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3;
            insidePoint++;
        }
    }

    // Walk second half.
    for(int i = iEnd; i >= iStart; i--)
    {
        if((finalPointPositionTable[i] < outsideNumHalfTessFactorPoints))
        {
            // Advance outside
            DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
            baseIndexOffset += 3; outsidePoint++;
        }
        if( /*(i>0) && <-- not needed since iStart is never 0*/ (finalPointPositionTable[i] < insideNumHalfTessFactorPoints))
        {
            // Advance inside
            DefineClockwiseTriangle(insidePoint,outsidePoint,insidePoint+1,baseIndexOffset);
            baseIndexOffset += 3; insidePoint++;
        }
    }
    // Below case is not needed if we didn't optimize loop above and made it run from 31 down to 0.
    if((finalPointPositionTable[0] < outsideNumHalfTessFactorPoints))
    {
        DefineClockwiseTriangle(outsidePoint,outsidePoint+1,insidePoint,baseIndexOffset);
        baseIndexOffset += 3; outsidePoint++;
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHWTessellator::PatchIndexValue()
//--------------------------------------------------------------------------------------------------------------------------------
int CHWTessellator::PatchIndexValue(int index)
{
    if( m_bUsingPatchedIndices )
    {
        if( index >= m_IndexPatchContext.outsidePointIndexPatchBase ) // assumed remapped outide indices are > remapped inside vertices
        {
            if( index == m_IndexPatchContext.outsidePointIndexBadValue )
                index = m_IndexPatchContext.outsidePointIndexReplacementValue;
            else
                index += m_IndexPatchContext.outsidePointIndexDeltaToRealValue;
        }
        else
        {
            if( index == m_IndexPatchContext.insidePointIndexBadValue )
                index = m_IndexPatchContext.insidePointIndexReplacementValue;
            else
                index += m_IndexPatchContext.insidePointIndexDeltaToRealValue;
        }
    }
    else if( m_bUsingPatchedIndices2 )
    {
        if( index >= m_IndexPatchContext2.baseIndexToInvert )
        {
            if( index == m_IndexPatchContext2.cornerCaseBadValue )
            {
                index = m_IndexPatchContext2.cornerCaseReplacementValue;
            }
            else
            {
                index = m_IndexPatchContext2.indexInversionEndPoint - index;
            }
        }
        else if( index == m_IndexPatchContext2.cornerCaseBadValue )
        {
            index = m_IndexPatchContext2.cornerCaseReplacementValue;
        }
    }
    return index;
}


//=================================================================================================================================
// CHLSLTessellator
//=================================================================================================================================

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::CHLSLTessellator
//---------------------------------------------------------------------------------------------------------------------------------
CHLSLTessellator::CHLSLTessellator()
{
    m_LastComputedTessFactors[0] = m_LastComputedTessFactors[1] = m_LastComputedTessFactors[2] =
    m_LastComputedTessFactors[3] = m_LastComputedTessFactors[4] = m_LastComputedTessFactors[5] = 0;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::Init
// User calls this.
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::Init(
    D3D11_TESSELLATOR_PARTITIONING       partitioning,
    D3D11_TESSELLATOR_REDUCTION          insideTessFactorReduction,
    D3D11_TESSELLATOR_QUAD_REDUCTION_AXIS quadInsideTessFactorReductionAxis,
    D3D11_TESSELLATOR_OUTPUT_PRIMITIVE   outputPrimitive)
{
    CHWTessellator::Init(partitioning,outputPrimitive);
    m_LastComputedTessFactors[0] = m_LastComputedTessFactors[1] = m_LastComputedTessFactors[2] =
    m_LastComputedTessFactors[3] = m_LastComputedTessFactors[4] = m_LastComputedTessFactors[5] = 0;
    m_partitioning = partitioning;
    m_originalPartitioning = partitioning;
    switch( partitioning )
    {
    case D3D11_TESSELLATOR_PARTITIONING_INTEGER:
    default:
        break;
    case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
        m_parity = TESSELLATOR_PARITY_ODD;
        break;
    case D3D11_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
        m_parity = TESSELLATOR_PARITY_EVEN;
        break;
    }
    m_originalParity = m_parity;
    m_outputPrimitive = outputPrimitive;
    m_insideTessFactorReduction = insideTessFactorReduction;
    m_quadInsideTessFactorReductionAxis = quadInsideTessFactorReductionAxis;
}
//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::TessellateQuadDomain
// User calls this
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::TessellateQuadDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
                                         float insideTessFactorScaleU, float insideTessFactorScaleV )
{
    QuadHLSLProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Ueq1,tessFactor_Veq1,insideTessFactorScaleU,insideTessFactorScaleV);

    CHWTessellator::TessellateQuadDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1],m_LastComputedTessFactors[2],m_LastComputedTessFactors[3],
                                         m_LastComputedTessFactors[4],m_LastComputedTessFactors[5]);
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::QuadHLSLProcessTessFactors
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::QuadHLSLProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Ueq1, float tessFactor_Veq1,
                                               float insideTessFactorScaleU, float insideTessFactorScaleV )
{
    if( !(tessFactor_Ueq0 > 0) ||// NaN will pass
        !(tessFactor_Veq0 > 0) ||
        !(tessFactor_Ueq1 > 0) ||
        !(tessFactor_Veq1 > 0) )
    {
        m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
        m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
        m_LastUnRoundedComputedTessFactors[2] = tessFactor_Ueq1;
        m_LastUnRoundedComputedTessFactors[3] = tessFactor_Veq1;
        m_LastUnRoundedComputedTessFactors[4] = 0;
        m_LastUnRoundedComputedTessFactors[5] = 0;
        m_LastComputedTessFactors[0] =
        m_LastComputedTessFactors[1] =
        m_LastComputedTessFactors[2] =
        m_LastComputedTessFactors[3] =
        m_LastComputedTessFactors[4] =
        m_LastComputedTessFactors[5] = 0;
        return;
    }

    CleanupFloatTessFactor(tessFactor_Ueq0);// clamp to [1.0f..INF], NaN->1.0f
    CleanupFloatTessFactor(tessFactor_Veq0);
    CleanupFloatTessFactor(tessFactor_Ueq1);
    CleanupFloatTessFactor(tessFactor_Veq1);

    // Save off tessFactors so they can be returned to app
    m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
    m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
    m_LastUnRoundedComputedTessFactors[2] = tessFactor_Ueq1;
    m_LastUnRoundedComputedTessFactors[3] = tessFactor_Veq1;

    // Process outside tessFactors
    float outsideTessFactor[QUAD_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Ueq1, tessFactor_Veq1};
    int edge, axis;
    TESSELLATOR_PARITY insideTessFactorParity[QUAD_AXES];
    if( Pow2Partitioning() || IntegerPartitioning() )
    {
        for( edge = 0; edge < QUAD_EDGES; edge++ )
        {
            RoundUpTessFactor(outsideTessFactor[edge]);
            ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
        }
    }
    else
    {
        SetTessellationParity(m_originalParity); // ClampTessFactor needs it
        for( edge = 0; edge < QUAD_EDGES; edge++ )
        {
            ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
        }
    }

    // Compute inside TessFactors
    float insideTessFactor[QUAD_AXES];
    if( m_quadInsideTessFactorReductionAxis == D3D11_TESSELLATOR_QUAD_REDUCTION_1_AXIS )
    {
        switch( m_insideTessFactorReduction )
        {
        case D3D11_TESSELLATOR_REDUCTION_MIN:
            insideTessFactor[U] = tess_fmin(tess_fmin(tessFactor_Veq0,tessFactor_Veq1),tess_fmin(tessFactor_Ueq0,tessFactor_Ueq1));
            break;
        case D3D11_TESSELLATOR_REDUCTION_MAX:
            insideTessFactor[U] = tess_fmax(tess_fmax(tessFactor_Veq0,tessFactor_Veq1),tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1));
            break;
        case D3D11_TESSELLATOR_REDUCTION_AVERAGE:
            insideTessFactor[U] = (tessFactor_Veq0 + tessFactor_Veq1 + tessFactor_Ueq0 + tessFactor_Ueq1) / 4;
            break;
        }
        // Scale inside tessFactor based on user scale factor.

        ClampFloatTessFactorScale(insideTessFactorScaleU); // clamp scale value to [0..1], NaN->0
        insideTessFactor[U] = insideTessFactor[U]*insideTessFactorScaleU;

        // Compute inside parity
        if( Pow2Partitioning() || IntegerPartitioning() )
        {
            ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
            m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
            RoundUpTessFactor(insideTessFactor[U]);
            insideTessFactorParity[U] =
            insideTessFactorParity[V] =
                (isEven(insideTessFactor[U]) || (FLOAT_ONE == insideTessFactor[U]) )
                ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
        }
        else
        {
            ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
            m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
            // no parity changes for fractional tessellation - just use what the user requested
            insideTessFactorParity[U] = insideTessFactorParity[V] = m_originalParity;
        }

        // To prevent snapping on edges, the "picture frame" comes
        // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
        if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[U]) &&
            (insideTessFactor[U] < FLOAT_THREE) )
        {
            if(D3D11_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
            {
                insideTessFactor[U] = tess_fmin(FLOAT_THREE,tess_fmax(tess_fmax(tessFactor_Veq0,tessFactor_Veq1),tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1)));
            }
            else
            {
                insideTessFactor[U] = tess_fmin(FLOAT_THREE,(tessFactor_Veq0 + tessFactor_Veq1 + tessFactor_Ueq0 + tessFactor_Ueq1) / 4);
            }
            ClampTessFactor(insideTessFactor[U]); // clamp reduction result that is based on unbounded user input
            m_LastUnRoundedComputedTessFactors[4] = m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
            if( IntegerPartitioning())
            {
                RoundUpTessFactor(insideTessFactor[U]);
                insideTessFactorParity[U] =
                insideTessFactorParity[V] = isEven(insideTessFactor[U]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
            }
        }
        insideTessFactor[V] = insideTessFactor[U];
    }
    else
    {
        switch( m_insideTessFactorReduction )
        {
        case D3D11_TESSELLATOR_REDUCTION_MIN:
            insideTessFactor[U] = tess_fmin(tessFactor_Veq0,tessFactor_Veq1);
            insideTessFactor[V] = tess_fmin(tessFactor_Ueq0,tessFactor_Ueq1);
            break;
        case D3D11_TESSELLATOR_REDUCTION_MAX:
            insideTessFactor[U] = tess_fmax(tessFactor_Veq0,tessFactor_Veq1);
            insideTessFactor[V] = tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1);
            break;
        case D3D11_TESSELLATOR_REDUCTION_AVERAGE:
            insideTessFactor[U] = (tessFactor_Veq0 + tessFactor_Veq1) / 2;
            insideTessFactor[V] = (tessFactor_Ueq0 + tessFactor_Ueq1) / 2;
            break;
        }
        // Scale inside tessFactors based on user scale factor.

        ClampFloatTessFactorScale(insideTessFactorScaleU); // clamp scale value to [0..1], NaN->0
        ClampFloatTessFactorScale(insideTessFactorScaleV);
        insideTessFactor[U] = insideTessFactor[U]*insideTessFactorScaleU;
        insideTessFactor[V] = insideTessFactor[V]*insideTessFactorScaleV;

        // Compute inside parity
        if( Pow2Partitioning() || IntegerPartitioning() )
        {
            for( axis = 0; axis < QUAD_AXES; axis++ )
            {
                ClampTessFactor(insideTessFactor[axis]); // clamp reduction + scale result that is based on unbounded user input
                m_LastUnRoundedComputedTessFactors[4+axis] = insideTessFactor[axis]; // Save off TessFactors so they can be returned to app
                RoundUpTessFactor(insideTessFactor[axis]);
                insideTessFactorParity[axis] =
                    (isEven(insideTessFactor[axis]) || (FLOAT_ONE == insideTessFactor[axis]) )
                    ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
            }
        }
        else
        {
            ClampTessFactor(insideTessFactor[U]); // clamp reduction + scale result that is based on unbounded user input
            ClampTessFactor(insideTessFactor[V]); // clamp reduction + scale result that is based on unbounded user input
            m_LastUnRoundedComputedTessFactors[4] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
            m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[V]; // Save off TessFactors so they can be returned to app
             // no parity changes for fractional tessellation - just use what the user requested
            insideTessFactorParity[U] = insideTessFactorParity[V] = m_originalParity;
        }

        // To prevent snapping on edges, the "picture frame" comes
        // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
        if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[U]) &&
            (insideTessFactor[U] < FLOAT_THREE) )
        {
            if(D3D11_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
            {
                insideTessFactor[U] = tess_fmin(FLOAT_THREE,tess_fmax(tessFactor_Veq0,tessFactor_Veq1));
            }
            else
            {
                insideTessFactor[U] = tess_fmin(FLOAT_THREE,(tessFactor_Veq0 + tessFactor_Veq1) / 2);
            }
            ClampTessFactor(insideTessFactor[U]); // clamp reduction result that is based on unbounded user input
            m_LastUnRoundedComputedTessFactors[4] = insideTessFactor[U]; // Save off TessFactors so they can be returned to app
            if( IntegerPartitioning())
            {
                RoundUpTessFactor(insideTessFactor[U]);
                insideTessFactorParity[U] = isEven(insideTessFactor[U]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
            }
        }

        if( (TESSELLATOR_PARITY_ODD == insideTessFactorParity[V]) &&
            (insideTessFactor[V] < FLOAT_THREE) )
        {
            if(D3D11_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
            {
                insideTessFactor[V] = tess_fmin(FLOAT_THREE,tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1));
            }
            else
            {
                insideTessFactor[V] = tess_fmin(FLOAT_THREE,(tessFactor_Ueq0 + tessFactor_Ueq1) / 2);
            }
            ClampTessFactor(insideTessFactor[V]);// clamp reduction result that is based on unbounded user input
            m_LastUnRoundedComputedTessFactors[5] = insideTessFactor[V]; // Save off TessFactors so they can be returned to app
            if( IntegerPartitioning())
            {
                RoundUpTessFactor(insideTessFactor[V]);
                insideTessFactorParity[V] = isEven(insideTessFactor[V]) ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
            }
        }

        for( axis = 0; axis < QUAD_AXES; axis++ )
        {
            if( TESSELLATOR_PARITY_ODD == insideTessFactorParity[axis] )
            {
                // Ensure the first ring ("picture frame") interpolates in on all sides
                // as much as the side with the minimum TessFactor.  Prevents snapping to edge.
                if( (insideTessFactor[axis] < FLOAT_THREE) && (insideTessFactor[axis] < insideTessFactor[(axis+1)&0x1]))
                {
                    insideTessFactor[axis] = tess_fmin(insideTessFactor[(axis+1)&0x1],FLOAT_THREE);
                    m_LastUnRoundedComputedTessFactors[4+axis] = insideTessFactor[axis]; // Save off TessFactors so they can be returned to app
                }
            }
        }
    }

    // Save off TessFactors so they can be returned to app
    m_LastComputedTessFactors[0] = outsideTessFactor[Ueq0];
    m_LastComputedTessFactors[1] = outsideTessFactor[Veq0];
    m_LastComputedTessFactors[2] = outsideTessFactor[Ueq1];
    m_LastComputedTessFactors[3] = outsideTessFactor[Veq1];
    m_LastComputedTessFactors[4] = insideTessFactor[U];
    m_LastComputedTessFactors[5] = insideTessFactor[V];
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::TessellateTriDomain
// User calls this
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::TessellateTriDomain( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
                                        float insideTessFactorScale )
{
    TriHLSLProcessTessFactors(tessFactor_Ueq0,tessFactor_Veq0,tessFactor_Weq0,insideTessFactorScale);

    CHWTessellator::TessellateTriDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1],m_LastComputedTessFactors[2],m_LastComputedTessFactors[3]);
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::TriHLSLProcessTessFactors
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::TriHLSLProcessTessFactors( float tessFactor_Ueq0, float tessFactor_Veq0, float tessFactor_Weq0,
                                  float insideTessFactorScale )
{
    if( !(tessFactor_Ueq0 > 0) || // NaN will pass
        !(tessFactor_Veq0 > 0) ||
        !(tessFactor_Weq0 > 0) )
    {
        m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
        m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
        m_LastUnRoundedComputedTessFactors[2] = tessFactor_Weq0;
        m_LastUnRoundedComputedTessFactors[3] =
        m_LastComputedTessFactors[0] =
        m_LastComputedTessFactors[1] =
        m_LastComputedTessFactors[2] =
        m_LastComputedTessFactors[3] = 0;
        return;
    }

    CleanupFloatTessFactor(tessFactor_Ueq0); // clamp to [1.0f..INF], NaN->1.0f
    CleanupFloatTessFactor(tessFactor_Veq0);
    CleanupFloatTessFactor(tessFactor_Weq0);

    // Save off TessFactors so they can be returned to app
    m_LastUnRoundedComputedTessFactors[0] = tessFactor_Ueq0;
    m_LastUnRoundedComputedTessFactors[1] = tessFactor_Veq0;
    m_LastUnRoundedComputedTessFactors[2] = tessFactor_Weq0;

    // Process outside TessFactors
    float outsideTessFactor[TRI_EDGES] = {tessFactor_Ueq0, tessFactor_Veq0, tessFactor_Weq0};
    int edge;
    if( Pow2Partitioning() || IntegerPartitioning() )
    {
        for( edge = 0; edge < TRI_EDGES; edge++ )
        {
            RoundUpTessFactor(outsideTessFactor[edge]); // for pow2 this rounds to pow2
            ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
        }
    }
    else
    {
        for( edge = 0; edge < TRI_EDGES; edge++ )
        {
            ClampTessFactor(outsideTessFactor[edge]); // clamp unbounded user input based on tessellation mode
        }
    }

    // Compute inside TessFactor
    float insideTessFactor;
    switch( m_insideTessFactorReduction )
    {
    case D3D11_TESSELLATOR_REDUCTION_MIN:
        insideTessFactor = tess_fmin(tess_fmin(tessFactor_Ueq0,tessFactor_Veq0),tessFactor_Weq0);
        break;
    case D3D11_TESSELLATOR_REDUCTION_MAX:
        insideTessFactor = tess_fmax(tess_fmax(tessFactor_Ueq0,tessFactor_Veq0),tessFactor_Weq0);
        break;
    case D3D11_TESSELLATOR_REDUCTION_AVERAGE:
        insideTessFactor = (tessFactor_Ueq0 + tessFactor_Veq0 + tessFactor_Weq0) / 3;
        break;
    }

    // Scale inside TessFactor based on user scale factor.
    ClampFloatTessFactorScale(insideTessFactorScale); // clamp scale value to [0..1], NaN->0
    insideTessFactor = insideTessFactor*tess_fmin(FLOAT_ONE,insideTessFactorScale);

    ClampTessFactor(insideTessFactor); // clamp reduction + scale result that is based on unbounded user input
    m_LastUnRoundedComputedTessFactors[3] = insideTessFactor;// Save off TessFactors so they can be returned to app
    TESSELLATOR_PARITY parity;
    if( Pow2Partitioning() || IntegerPartitioning() )
    {
        RoundUpTessFactor(insideTessFactor);
        parity = (isEven(insideTessFactor) || (FLOAT_ONE == insideTessFactor))
                                        ? TESSELLATOR_PARITY_EVEN : TESSELLATOR_PARITY_ODD;
    }
    else
    {
        parity = m_originalParity;
    }

    if( (TESSELLATOR_PARITY_ODD == parity) &&
        (insideTessFactor < FLOAT_THREE))
    {
        // To prevent snapping on edges, the "picture frame" comes
        // in using avg or max (and ignore inside TessFactor scaling) until it is at least 3.
        if(D3D11_TESSELLATOR_REDUCTION_MAX == m_insideTessFactorReduction)
        {
            insideTessFactor = tess_fmin(FLOAT_THREE,tess_fmax(tessFactor_Ueq0,tess_fmax(tessFactor_Veq0,tessFactor_Weq0)));
        }
        else
        {
            insideTessFactor = tess_fmin(FLOAT_THREE,(tessFactor_Ueq0 + tessFactor_Veq0 + tessFactor_Weq0) / 3);
        }
        ClampTessFactor(insideTessFactor); // clamp reduction result that is based on unbounded user input
        m_LastUnRoundedComputedTessFactors[3] = insideTessFactor;// Save off TessFactors so they can be returned to app
        if( IntegerPartitioning())
        {
            RoundUpTessFactor(insideTessFactor);
        }
    }

    // Save off TessFactors so they can be returned to app
    m_LastComputedTessFactors[0] = outsideTessFactor[Ueq0];
    m_LastComputedTessFactors[1] = outsideTessFactor[Veq0];
    m_LastComputedTessFactors[2] = outsideTessFactor[Weq0];
    m_LastComputedTessFactors[3] = insideTessFactor;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::TessellateIsoLineDomain
// User calls this.
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::TessellateIsoLineDomain( float TessFactor_U_LineDetail, float TessFactor_V_LineDensity )
{
    IsoLineHLSLProcessTessFactors(TessFactor_V_LineDensity,TessFactor_U_LineDetail);
    CHWTessellator::TessellateIsoLineDomain(m_LastComputedTessFactors[0],m_LastComputedTessFactors[1]);
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::IsoLineHLSLProcessTessFactors
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::IsoLineHLSLProcessTessFactors( float TessFactor_V_LineDensity, float TessFactor_U_LineDetail )
{
    if( !(TessFactor_V_LineDensity > 0) || // NaN will pass
        !(TessFactor_U_LineDetail > 0) )
    {
        m_LastUnRoundedComputedTessFactors[0] = TessFactor_V_LineDensity;
        m_LastUnRoundedComputedTessFactors[1] = TessFactor_U_LineDetail;
        m_LastComputedTessFactors[0] =
        m_LastComputedTessFactors[1] = 0;
        return;
    }

    CleanupFloatTessFactor(TessFactor_V_LineDensity); // clamp to [1.0f..INF], NaN->1.0f
    CleanupFloatTessFactor(TessFactor_U_LineDetail); // clamp to [1.0f..INF], NaN->1.0f

    ClampTessFactor(TessFactor_U_LineDetail); // clamp unbounded user input based on tessellation mode

    m_LastUnRoundedComputedTessFactors[1] = TessFactor_U_LineDetail;    // Save off TessFactors so they can be returned to app

    if(Pow2Partitioning()||IntegerPartitioning())
    {
        RoundUpTessFactor(TessFactor_U_LineDetail);
    }

    OverridePartitioning(D3D11_TESSELLATOR_PARTITIONING_INTEGER);

    ClampTessFactor(TessFactor_V_LineDensity); // Clamp unbounded user input to integer
    m_LastUnRoundedComputedTessFactors[0] = TessFactor_V_LineDensity;    // Save off TessFactors so they can be returned to app

    RoundUpTessFactor(TessFactor_V_LineDensity);

    RestorePartitioning();

    // Save off TessFactors so they can be returned to app
    m_LastComputedTessFactors[0] = TessFactor_V_LineDensity;
    m_LastComputedTessFactors[1] = TessFactor_U_LineDetail;
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::ClampTessFactor()
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::ClampTessFactor(float& TessFactor)
{
    if( Pow2Partitioning() )
    {
        TessFactor = tess_fmin( D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR, tess_fmax( TessFactor, D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
    }
    else if( IntegerPartitioning() )
    {
        TessFactor = tess_fmin( D3D11_TESSELLATOR_MAX_TESSELLATION_FACTOR, tess_fmax( TessFactor, D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
    }
    else if( Odd() )
    {
        TessFactor = tess_fmin( D3D11_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR, tess_fmax( TessFactor, D3D11_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
    }
    else // even
    {
        TessFactor = tess_fmin( D3D11_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR, tess_fmax( TessFactor, D3D11_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR) );
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::CleanupFloatTessFactor()
//---------------------------------------------------------------------------------------------------------------------------------
static const int exponentMask = 0x7f800000;
static const int mantissaMask = 0x007fffff;
void CHLSLTessellator::CleanupFloatTessFactor(float& input)
{
    // If input is < 1.0f or NaN, clamp to 1.0f.
    // In other words, clamp input to [1.0f...+INF]
    int bits = *(int*)&input;
    if( ( ( ( bits & exponentMask ) == exponentMask ) && ( bits & mantissaMask ) ) ||// nan?
        (input < 1.0f) )
    {
        input = 1;
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::ClampFloatTessFactorScale()
//---------------------------------------------------------------------------------------------------------------------------------
void CHLSLTessellator::ClampFloatTessFactorScale(float& input)
{
    // If input is < 0.0f or NaN, clamp to 0.0f.  > 1 clamps to 1.
    // In other words, clamp input to [0.0f...1.0f]
    int bits = *(int*)&input;
    if( ( ( ( bits & exponentMask ) == exponentMask ) && ( bits & mantissaMask ) ) ||// nan?
        (input < 0.0f) )
    {
        input = 0;
    }
    else if( input > 1 )
    {
        input = 1;
    }
}

//---------------------------------------------------------------------------------------------------------------------------------
// CHLSLTessellator::RoundUpTessFactor()
//---------------------------------------------------------------------------------------------------------------------------------
static const int exponentLSB = 0x00800000;
void CHLSLTessellator::RoundUpTessFactor(float& TessFactor)
{
    // Assume TessFactor is in [1.0f..+INF]
    if( Pow2Partitioning() )
    {
        int bits = *(int*)&TessFactor;
        if( bits & mantissaMask )
        {
            *(int*)&TessFactor = (bits & exponentMask) + exponentLSB;
        }
    }
    else if( IntegerPartitioning() )
    {
        TessFactor = ceil(TessFactor);
    }
}
