2652 lines
123 KiB
C++
2652 lines
123 KiB
C++
/*
|
||
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"
|
||
#include "util/macros.h"
|
||
#if defined(_MSC_VER)
|
||
#include <math.h> // ceil
|
||
#else
|
||
#include <cmath>
|
||
#endif
|
||
#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( push )
|
||
#pragma warning( disable : 4293 26452 )
|
||
#endif
|
||
_fxpMaxPosValueFloat -= INT32( 1 ) << iShift;
|
||
#if defined(_MSC_VER)
|
||
#pragma warning( pop )
|
||
#endif
|
||
}
|
||
|
||
// 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( push )
|
||
#pragma warning( disable : 4293 26452 )
|
||
#endif
|
||
_fxpMaxPosValueFloat -= INT32( 1 ) << iShift;
|
||
#if defined(_MSC_VER)
|
||
#pragma warning( pop )
|
||
#endif
|
||
}
|
||
|
||
// 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[PIPE_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;
|
||
}
|
||
//---------------------------------------------------------------------------------------------------------------------------------
|
||
// CHWTessellator::~CHWTessellator
|
||
//---------------------------------------------------------------------------------------------------------------------------------
|
||
CHWTessellator::~CHWTessellator()
|
||
{
|
||
delete [] m_Point;
|
||
delete [] m_Index;
|
||
}
|
||
|
||
//---------------------------------------------------------------------------------------------------------------------------------
|
||
// CHWTessellator::Init
|
||
// User calls this.
|
||
//---------------------------------------------------------------------------------------------------------------------------------
|
||
void CHWTessellator::Init(
|
||
PIPE_TESSELLATOR_PARTITIONING partitioning,
|
||
PIPE_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 PIPE_TESSELLATOR_PARTITIONING_INTEGER:
|
||
default:
|
||
break;
|
||
case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
|
||
m_parity = TESSELLATOR_PARITY_ODD;
|
||
break;
|
||
case PIPE_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 PIPE_TESSELLATOR_OUTPUT_TRIANGLE_CW:
|
||
case PIPE_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 PIPE_TESSELLATOR_OUTPUT_POINT:
|
||
DumpAllPoints();
|
||
break;
|
||
case PIPE_TESSELLATOR_OUTPUT_LINE:
|
||
DumpAllPointsAsInOrderLineList();
|
||
break;
|
||
}
|
||
return;
|
||
}
|
||
|
||
QuadGeneratePoints(processedTessFactors);
|
||
|
||
if( m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_POINT )
|
||
{
|
||
DumpAllPoints();
|
||
return;
|
||
}
|
||
if( m_outputPrimitive == PIPE_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 PIPE_TESSELLATOR_PARTITIONING_INTEGER:
|
||
case PIPE_TESSELLATOR_PARTITIONING_POW2: // don<6F>t care about pow2 distinction for validation, just treat as integer
|
||
lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
|
||
break;
|
||
|
||
case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
|
||
lowerBound = PIPE_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
|
||
break;
|
||
|
||
case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
|
||
lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_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(PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD == m_originalPartitioning)
|
||
{
|
||
#define EPSILON 0.0000152587890625f // 2^(-16), min positive fixed point fraction
|
||
#define MIN_ODD_TESSFACTOR_PLUS_HALF_EPSILON (PIPE_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 = PIPE_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 PIPE_TESSELLATOR_OUTPUT_TRIANGLE_CW:
|
||
case PIPE_TESSELLATOR_OUTPUT_TRIANGLE_CCW:
|
||
// function orients them CCW if needed
|
||
DefineClockwiseTriangle(0,1,2,/*indexStorageBaseOffset*/m_NumIndices);
|
||
m_NumIndices = 3;
|
||
break;
|
||
case PIPE_TESSELLATOR_OUTPUT_POINT:
|
||
DumpAllPoints();
|
||
break;
|
||
case PIPE_TESSELLATOR_OUTPUT_LINE:
|
||
DumpAllPointsAsInOrderLineList();
|
||
break;
|
||
}
|
||
return;
|
||
}
|
||
|
||
TriGeneratePoints(processedTessFactors);
|
||
|
||
if( m_outputPrimitive == PIPE_TESSELLATOR_OUTPUT_POINT )
|
||
{
|
||
DumpAllPoints();
|
||
return;
|
||
}
|
||
if( m_outputPrimitive == PIPE_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 PIPE_TESSELLATOR_PARTITIONING_INTEGER:
|
||
case PIPE_TESSELLATOR_PARTITIONING_POW2: // don<6F>t care about pow2 distinction for validation, just treat as integer
|
||
lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
|
||
break;
|
||
|
||
case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
|
||
lowerBound = PIPE_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
|
||
break;
|
||
|
||
case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
|
||
lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_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(PIPE_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 = PIPE_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 PIPE_TESSELLATOR_PARTITIONING_INTEGER:
|
||
case PIPE_TESSELLATOR_PARTITIONING_POW2: // don<6F>t care about pow2 distinction for validation, just treat as integer
|
||
lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
|
||
break;
|
||
|
||
case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_EVEN:
|
||
lowerBound = PIPE_TESSELLATOR_MIN_EVEN_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR;
|
||
break;
|
||
|
||
case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
|
||
lowerBound = PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR;
|
||
upperBound = PIPE_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR;
|
||
break;
|
||
}
|
||
|
||
TessFactor_V_LineDensity = tess_fmin( PIPE_TESSELLATOR_MAX_ISOLINE_DENSITY_TESSELLATION_FACTOR,
|
||
tess_fmax( PIPE_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(PIPE_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 == PIPE_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 == PIPE_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 == PIPE_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
|
||
{
|
||
TessFactorCtx.splitPointOnFloorHalfTessFactor = (RemoveMSB((fxpFloorHalfTessFactor>>FXP_FRACTION_BITS)-1)<<1) + 1;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
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
|
||
)
|
||
{
|
||
// 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};
|
||
|
||
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(
|
||
PIPE_TESSELLATOR_PARTITIONING partitioning,
|
||
PIPE_TESSELLATOR_REDUCTION insideTessFactorReduction,
|
||
PIPE_TESSELLATOR_QUAD_REDUCTION_AXIS quadInsideTessFactorReductionAxis,
|
||
PIPE_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 PIPE_TESSELLATOR_PARTITIONING_INTEGER:
|
||
default:
|
||
break;
|
||
case PIPE_TESSELLATOR_PARTITIONING_FRACTIONAL_ODD:
|
||
m_parity = TESSELLATOR_PARITY_ODD;
|
||
break;
|
||
case PIPE_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 == PIPE_TESSELLATOR_QUAD_REDUCTION_1_AXIS )
|
||
{
|
||
switch( m_insideTessFactorReduction )
|
||
{
|
||
case PIPE_TESSELLATOR_REDUCTION_MIN:
|
||
insideTessFactor[U] = tess_fmin(tess_fmin(tessFactor_Veq0,tessFactor_Veq1),tess_fmin(tessFactor_Ueq0,tessFactor_Ueq1));
|
||
break;
|
||
case PIPE_TESSELLATOR_REDUCTION_MAX:
|
||
insideTessFactor[U] = tess_fmax(tess_fmax(tessFactor_Veq0,tessFactor_Veq1),tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1));
|
||
break;
|
||
case PIPE_TESSELLATOR_REDUCTION_AVERAGE:
|
||
insideTessFactor[U] = (tessFactor_Veq0 + tessFactor_Veq1 + tessFactor_Ueq0 + tessFactor_Ueq1) / 4;
|
||
break;
|
||
default:
|
||
unreachable("impossible m_insideTessFactorReduction");
|
||
}
|
||
// 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(PIPE_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 PIPE_TESSELLATOR_REDUCTION_MIN:
|
||
insideTessFactor[U] = tess_fmin(tessFactor_Veq0,tessFactor_Veq1);
|
||
insideTessFactor[V] = tess_fmin(tessFactor_Ueq0,tessFactor_Ueq1);
|
||
break;
|
||
case PIPE_TESSELLATOR_REDUCTION_MAX:
|
||
insideTessFactor[U] = tess_fmax(tessFactor_Veq0,tessFactor_Veq1);
|
||
insideTessFactor[V] = tess_fmax(tessFactor_Ueq0,tessFactor_Ueq1);
|
||
break;
|
||
case PIPE_TESSELLATOR_REDUCTION_AVERAGE:
|
||
insideTessFactor[U] = (tessFactor_Veq0 + tessFactor_Veq1) / 2;
|
||
insideTessFactor[V] = (tessFactor_Ueq0 + tessFactor_Ueq1) / 2;
|
||
break;
|
||
default:
|
||
unreachable("impossible m_insideTessFactorReduction");
|
||
}
|
||
// 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(PIPE_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(PIPE_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 PIPE_TESSELLATOR_REDUCTION_MIN:
|
||
insideTessFactor = tess_fmin(tess_fmin(tessFactor_Ueq0,tessFactor_Veq0),tessFactor_Weq0);
|
||
break;
|
||
case PIPE_TESSELLATOR_REDUCTION_MAX:
|
||
insideTessFactor = tess_fmax(tess_fmax(tessFactor_Ueq0,tessFactor_Veq0),tessFactor_Weq0);
|
||
break;
|
||
case PIPE_TESSELLATOR_REDUCTION_AVERAGE:
|
||
insideTessFactor = (tessFactor_Ueq0 + tessFactor_Veq0 + tessFactor_Weq0) / 3;
|
||
break;
|
||
default:
|
||
unreachable("impossible m_insideTessFactorReduction");
|
||
}
|
||
|
||
// 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(PIPE_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(PIPE_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( PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR, tess_fmax( TessFactor, PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
|
||
}
|
||
else if( IntegerPartitioning() )
|
||
{
|
||
TessFactor = tess_fmin( PIPE_TESSELLATOR_MAX_TESSELLATION_FACTOR, tess_fmax( TessFactor, PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
|
||
}
|
||
else if( Odd() )
|
||
{
|
||
TessFactor = tess_fmin( PIPE_TESSELLATOR_MAX_ODD_TESSELLATION_FACTOR, tess_fmax( TessFactor, PIPE_TESSELLATOR_MIN_ODD_TESSELLATION_FACTOR) );
|
||
}
|
||
else // even
|
||
{
|
||
TessFactor = tess_fmin( PIPE_TESSELLATOR_MAX_EVEN_TESSELLATION_FACTOR, tess_fmax( TessFactor, PIPE_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);
|
||
}
|
||
}
|