640 lines
15 KiB
C++
640 lines
15 KiB
C++
#include <render/culling/CoordSubList.h>
|
|
#include <render/culling/CullData.h>
|
|
#include <raddebug.hpp>
|
|
#include <render/culling/Vector3i.h>
|
|
|
|
#include <memory/srrmemory.h>
|
|
|
|
//
|
|
// TODO - Replace this with a real variable setting
|
|
//
|
|
#define DEV_NUM_BINS 1024
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// External Functions
|
|
//
|
|
// Used in sorting process for centroids
|
|
// Subtracting 1 is compensation for integral truncation in conversion
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// TODO - inline it!
|
|
//
|
|
inline int CompareCellN( Cell* ipCell1, Cell* ipCell2, int iIndex )
|
|
{
|
|
//
|
|
// Is there anyway to preserve non-zero sign faster for wholly fractional differences?
|
|
// If not, you can always accept truncation loss
|
|
//
|
|
float Diff = ((Cell*)ipCell1)->Centroid(iIndex) - ((Cell*)ipCell2)->Centroid(iIndex);
|
|
|
|
if( Diff > 0.0f )
|
|
return 1;
|
|
|
|
if( Diff < 0.0f )
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
//Lossy version
|
|
// return (int)( ((Cell*)ipCell1)->Centroid(iIndex) - ((Cell*)ipCell2)->Centroid(iIndex) -1);
|
|
//
|
|
}
|
|
|
|
int CompareCellXs( const void* ipCell1, const void* ipCell2)
|
|
{
|
|
return CompareCellN( ((Cell*)ipCell1), ((Cell*)ipCell2), 0);
|
|
}
|
|
|
|
int CompareCellYs( const void* ipCell1, const void* ipCell2)
|
|
{
|
|
return CompareCellN( ((Cell*)ipCell1), ((Cell*)ipCell2), 1);
|
|
}
|
|
|
|
int CompareCellZs( const void* ipCell1, const void* ipCell2)
|
|
{
|
|
return CompareCellN( ((Cell*)ipCell1), ((Cell*)ipCell2), 2);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
// Data List
|
|
//
|
|
// int mSize;
|
|
// int mWeightedSize;
|
|
// Cell* mpCellList;
|
|
// Bounds3f mBounds;
|
|
///////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
CoordSubList::CoordSubList( Cell* ipCellList, int iSize, int iWeightedSize, Bounds3f& irBounds )
|
|
{
|
|
mpCellList = ipCellList;
|
|
mSize = iSize;
|
|
mWeightedSize = iWeightedSize;
|
|
|
|
mSpans.SetToSpan( irBounds.mMin, irBounds.mMax );
|
|
}
|
|
|
|
CoordSubList::CoordSubList( Cell* ipCellList, int iSize, Vector3i iSpans )
|
|
{
|
|
mpCellList = ipCellList;
|
|
mSize = iSize;
|
|
mSpans = iSpans;
|
|
|
|
GenWeightedSize();
|
|
|
|
}
|
|
|
|
CoordSubList::CoordSubList( Cell* ipCellList, int iSize )
|
|
{
|
|
mpCellList = ipCellList;
|
|
mSize = iSize;
|
|
|
|
GenerateMembers();
|
|
}
|
|
|
|
|
|
CoordSubList::CoordSubList()
|
|
{
|
|
mpCellList = NULL;
|
|
mSize = 0;
|
|
}
|
|
|
|
CoordSubList::~CoordSubList()
|
|
{
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Generate deterministic members
|
|
//
|
|
// WeightedSize
|
|
// Spans
|
|
//
|
|
void CoordSubList::GenerateMembers()
|
|
{
|
|
GenWeightedSize();
|
|
GenSpans();
|
|
}
|
|
|
|
void CoordSubList::GenWeightedSize()
|
|
{
|
|
int i;
|
|
mWeightedSize = 0;
|
|
|
|
for( i=0; i<mSize; i++ )
|
|
{
|
|
mWeightedSize += mpCellList[i].RenderWeight();
|
|
}
|
|
}
|
|
|
|
void CoordSubList::GenSpans()
|
|
{
|
|
int i;
|
|
/*
|
|
Bounds3f tempBounds;
|
|
|
|
tempBounds.mMin.SetFP( mpCellList[0].Centroid() );
|
|
tempBounds.mMax.SetFP( mpCellList[0].Centroid() );
|
|
|
|
for( i=1; i<mSize; i++ )
|
|
{
|
|
tempBounds.Accumulate( mpCellList[i].Centroid() );
|
|
}
|
|
*/
|
|
|
|
Bounds3i tempBounds;
|
|
|
|
tempBounds.mMin.Set( mpCellList[0].BlockIndex() );
|
|
tempBounds.mMax.Set( mpCellList[0].BlockIndex() );
|
|
|
|
for( i=1; i<mSize; i++ )
|
|
{
|
|
tempBounds.Accumulate( mpCellList[i].BlockIndex() );
|
|
}
|
|
|
|
mSpans.SetToSpan( tempBounds.mMin, tempBounds.mMax );
|
|
}
|
|
|
|
int CoordSubList::WeightedSize()
|
|
{
|
|
return mWeightedSize;
|
|
}
|
|
|
|
int CoordSubList::Size()
|
|
{
|
|
return mSize;
|
|
}
|
|
|
|
Cell& CoordSubList::Get( int i )
|
|
{
|
|
rAssert( i>-1 && i<mSize );
|
|
return mpCellList[i];
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
// This data structure should be able to split itself if any of the three axes contain
|
|
// elements occupying separate bins (marked by their block index)
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
bool CoordSubList::CanBeSplit()
|
|
{
|
|
if( mSize > 1 )
|
|
{
|
|
for( int iAxis=0; iAxis<3; iAxis++ )
|
|
{
|
|
for( int i=1; i<mSize; i++ )
|
|
{
|
|
if( mpCellList[i].BlockIndex(iAxis) != mpCellList[0].BlockIndex(iAxis) )
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
//
|
|
// SubDivide the List
|
|
//
|
|
// Divide List by a weighting value
|
|
//
|
|
void CoordSubList::SubDivideHeuristic
|
|
(
|
|
Vector3f& irGranularity,
|
|
CoordSubList** opList1,
|
|
CoordSubList** opList2,
|
|
int& iorAxis,
|
|
float& orPlanePosn
|
|
)
|
|
{
|
|
MEMTRACK_PUSH_GROUP( "CoortSubList" );
|
|
|
|
rAssert( mpCellList );
|
|
rAssert( *opList1 == NULL );
|
|
rAssert( *opList1 == NULL );
|
|
|
|
/* if( iorAxis == -1 )
|
|
{
|
|
iorAxis = ChooseAxis();
|
|
}
|
|
*/
|
|
//
|
|
// Accumulate the WeightCount as the list is sorted by median,
|
|
// where each point of Cell weight accounts for an item
|
|
//
|
|
//
|
|
// Currently this algorithm is inefficient, as it re-sorts the data
|
|
// Each iteration. But this is just a test for an offline tool.
|
|
//
|
|
/* switch( iorAxis )
|
|
{
|
|
case 0:
|
|
qsort( mpCellList, mSize, sizeof(Cell), CompareCellXs );
|
|
break;
|
|
case 1:
|
|
qsort( mpCellList, mSize, sizeof(Cell), CompareCellYs );
|
|
break;
|
|
case 2:
|
|
qsort( mpCellList, mSize, sizeof(Cell), CompareCellZs );
|
|
break;
|
|
default:
|
|
rAssert(false);
|
|
break;
|
|
}
|
|
*/
|
|
//
|
|
// Set Plane Posn
|
|
//
|
|
// int List1Size = GetWeightedMedian( orPlanePosn, iorAxis );
|
|
int List1Size = FindSplitIndex( iorAxis, 0 );
|
|
|
|
//Debug
|
|
if( List1Size >= mSize || List1Size <= 0 )
|
|
{
|
|
bool fuckedUp = true;
|
|
iorAxis = -1;
|
|
List1Size = FindSplitIndex( iorAxis, 0 );
|
|
}
|
|
|
|
|
|
SeekMinPlane( orPlanePosn, List1Size, iorAxis );
|
|
|
|
*opList1 = new(GMA_TEMP) CoordSubList( mpCellList, List1Size);
|
|
*opList2 = new(GMA_TEMP) CoordSubList( &(mpCellList[List1Size]), mSize - List1Size);
|
|
MEMTRACK_POP_GROUP("CoortSubList");
|
|
|
|
}
|
|
|
|
//
|
|
// SubDivide the List
|
|
//
|
|
void CoordSubList::SubDivideUniform( int iAxis, CoordSubList** opList1, CoordSubList** opList2 )
|
|
{
|
|
rAssert( mpCellList );
|
|
rAssert( false ); // not implemented
|
|
if( iAxis == -1 )
|
|
{
|
|
iAxis = ChooseAxis();
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// TODO -rename these GetWeightedMedian's to reflect what the second one does; distinguish
|
|
//
|
|
int CoordSubList::GetWeightedMedian( int iAxis )
|
|
{
|
|
int MedianWeightCount = mWeightedSize / 2;
|
|
int WeightCount = 0;
|
|
int i=-1;
|
|
|
|
// for( i = 0; (i < mSize) && (WeightCount < MedianWeightCount); i++ )
|
|
// {
|
|
// WeightCount += mpCellList[i].RenderWeight();
|
|
// }
|
|
do
|
|
{
|
|
i++;
|
|
WeightCount += mpCellList[i].RenderWeight();
|
|
} while( (i<mSize-1) && (WeightCount<MedianWeightCount) );
|
|
|
|
//
|
|
// If we've surpassed the median, step back,
|
|
// undercut the median to handle the 2 element cases.
|
|
//
|
|
if( WeightCount > MedianWeightCount )
|
|
{
|
|
if(i-1>=0)
|
|
{
|
|
i--;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
int CoordSubList::GetWeightedMedian( float& orPlanePosn, int iAxis )
|
|
{
|
|
int i = GetWeightedMedian( iAxis );
|
|
return GetPlanarDivision( orPlanePosn, iAxis, i );
|
|
}
|
|
|
|
int CoordSubList::GetPlanarDivision( float& orPlanePosn, int iAxis, int iIndex )
|
|
{
|
|
int DivisionIndex = mpCellList[iIndex].BlockIndex(iAxis);
|
|
orPlanePosn = mpCellList[iIndex].MaxPlane(iAxis);
|
|
|
|
for( iIndex++; (iIndex<mSize) && (mpCellList[iIndex].BlockIndex(iAxis) <= DivisionIndex); iIndex++ ){}
|
|
return iIndex;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
// Returns: The index in the SubList which marks the beginning of the second list
|
|
//
|
|
// Method:
|
|
// Find the best axis/index to split the data along.
|
|
// Best is defn'd as the one which can be split most closely to half (dichotomy heuristic)
|
|
// Then do an in-place GT/LT median sort, returning the index of the start of the GT list
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
int CoordSubList::FindSplitIndex( int& ioAxis, int iDeviationThreshold )
|
|
{
|
|
Vector3i MedianDeviation, MedianBin;
|
|
|
|
if( ioAxis == SRR_ERR_INDEX )
|
|
{
|
|
for( ioAxis=0; ioAxis<3; ioAxis++ )
|
|
{
|
|
if( mSpans[ioAxis] > 0 )
|
|
{
|
|
BinCount( MedianDeviation[ioAxis], MedianBin[ioAxis], ioAxis );
|
|
|
|
if( MedianDeviation[ioAxis] <= iDeviationThreshold )
|
|
{
|
|
return BinSort( MedianBin[ioAxis], ioAxis );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//Don't consider this axis
|
|
MedianDeviation[ioAxis] = mWeightedSize;
|
|
MedianBin[ioAxis] = mSize+1;
|
|
}
|
|
}
|
|
/*
|
|
BinCount( MedianDeviation.mY, MedianBin.mY, 1 );
|
|
if( MedianDeviation.mY < iDeviationThreshold )
|
|
{
|
|
return BinSort( MedianBin.mY, 1 );
|
|
}
|
|
|
|
BinCount( MedianDeviation.mZ, MedianBin.mZ, 2 );
|
|
if( MedianDeviation.mZ < iDeviationThreshold )
|
|
{
|
|
return BinSort( MedianBin.mZ, 2 );
|
|
}
|
|
*/
|
|
ioAxis = MedianDeviation.MinIndex();
|
|
return BinSort( MedianBin[ioAxis], ioAxis );
|
|
}
|
|
else
|
|
{
|
|
BinCount( MedianDeviation[ioAxis], MedianBin[ioAxis], ioAxis );
|
|
return BinSort( MedianBin[ioAxis], ioAxis );
|
|
}
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
void CoordSubList::BinCount( int& oMedianDeviation, int& oMedianBin, int iAxis )
|
|
{
|
|
int i, Count=0, nNonEmptyBins=0;
|
|
SubArray<int> Bins;
|
|
theCullCache().Acquire( Bins, DEV_NUM_BINS );
|
|
|
|
Bins.SetAll(Count);
|
|
|
|
//Get Total, Bin Count List
|
|
for( i=0; i<mSize; i++ )
|
|
{
|
|
Bins[ mpCellList[i].BlockIndex(iAxis) ] += mpCellList[i].RenderWeight();
|
|
Count += mpCellList[i].RenderWeight();
|
|
}
|
|
|
|
//Find Median & Deviation
|
|
Count = Count/2;
|
|
|
|
for( i=0; (i<Bins.mSize)&&(Count>0); i++ )
|
|
{
|
|
Count -= Bins[i];
|
|
}
|
|
|
|
i--;
|
|
|
|
//Find the best approximate median
|
|
if( Count < Bins[i]/-2 )
|
|
{
|
|
Count += Bins[i];
|
|
oMedianBin = i;
|
|
}
|
|
else
|
|
{
|
|
Count = rmt::Abs( Count );
|
|
oMedianBin = i+1;
|
|
}
|
|
|
|
oMedianDeviation = Count;
|
|
|
|
theCullCache().Release( Bins );
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
int CoordSubList::BinSort( int iMedianBin, int iAxis )
|
|
{
|
|
int RightIndex = mSize-1;
|
|
Cell temp;
|
|
Cell* left = &(mpCellList[0]);
|
|
Cell* right = &(mpCellList[RightIndex]);
|
|
|
|
while( left!=right )
|
|
{
|
|
if( left->BlockIndex(iAxis) < iMedianBin )
|
|
{
|
|
left++;
|
|
}
|
|
else
|
|
{
|
|
//left > MedianBin;
|
|
//left needs to be swapped with a right < Median
|
|
if( right->BlockIndex(iAxis) < iMedianBin )
|
|
{
|
|
temp = *right;
|
|
*right = *left;
|
|
*left = temp;
|
|
|
|
left++;
|
|
if( left!=right )
|
|
{
|
|
right--;
|
|
RightIndex--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
right--;
|
|
RightIndex--;
|
|
/* //In the case where they overlap, ensure right ends on GT
|
|
if( left == right )
|
|
{
|
|
if( right->BlockIndex(iAxis) < iMedianBin )
|
|
{
|
|
RightIndex++;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
}
|
|
//Debug
|
|
if( right->BlockIndex(iAxis) < iMedianBin )
|
|
{
|
|
rAssert( (right+1)->BlockIndex(iAxis) >= iMedianBin );
|
|
RightIndex++;
|
|
}
|
|
|
|
float minPlane;
|
|
SeekMinPlane( minPlane, RightIndex, iAxis );
|
|
|
|
return RightIndex;
|
|
}
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CoordSubList::SeekMinPlane( float& oPlanePosn, int i, int iAxis )
|
|
{
|
|
//Debug
|
|
float LTMaxPlane;
|
|
SeekMaxPlane( LTMaxPlane, 0, i, iAxis);
|
|
//Debug
|
|
|
|
oPlanePosn = mpCellList[i].MinPlane(iAxis);
|
|
for( i++; i<mSize; i++ )
|
|
{
|
|
rAssert( mpCellList[i].MinPlane(iAxis) < mpCellList[i].MaxPlane(iAxis) );
|
|
if( mpCellList[i].MinPlane(iAxis) < oPlanePosn )
|
|
{
|
|
oPlanePosn = mpCellList[i].MinPlane(iAxis);
|
|
}
|
|
}
|
|
|
|
//Debug
|
|
rAssert( LTMaxPlane <= oPlanePosn );
|
|
}
|
|
|
|
void CoordSubList::SeekMaxPlane( float& oPlanePosn, int iCur, int iEnd, int iAxis )
|
|
{
|
|
oPlanePosn = mpCellList[iCur].MaxPlane(iAxis);
|
|
for( iCur++; iCur<iEnd; iCur++ )
|
|
{
|
|
rAssert( mpCellList[iCur].MinPlane(iAxis) < mpCellList[iCur].MaxPlane(iAxis) );
|
|
|
|
if( mpCellList[iCur].MaxPlane(iAxis) > oPlanePosn )
|
|
{
|
|
oPlanePosn = mpCellList[iCur].MaxPlane(iAxis);
|
|
}
|
|
}
|
|
}
|
|
/*
|
|
int CoordSubList::GetPlanarDivision( float& orPlanePosn, int iAxis, int iIndex )
|
|
{
|
|
orPlanePosn = mpCellList[iIndex].MaxPlane(iAxis);
|
|
|
|
for( iIndex++; (iIndex<mSize) && (mpCellList[iIndex].MaxPlane(iAxis) <= orPlanePosn); iIndex++ ){}
|
|
return iIndex;
|
|
}
|
|
*/
|
|
/*
|
|
int CoordSubList::GetPlanarDivision( int iIndex, int iAxis, Vector3f irGranularity )
|
|
{
|
|
float UpperBound = GetCellUpperBound( mpCellList[iIndex].Centroid(iAxis), irGranularity[iAxis] );
|
|
|
|
for( ; (iIndex < mSize) && (mpCellList[iIndex].Centroid(iAxis) < UpperBound); iIndex++ ){}
|
|
|
|
return iIndex;
|
|
}
|
|
|
|
float CoordSubList::GetCellUpperBound( float iCoord, float iGranularity )
|
|
{
|
|
return (sTruncate(iCoord / iGranularity) + 1.0f) * iGranularity;
|
|
}
|
|
*/
|
|
|
|
/*int CoordSubList::GetWeightedMedian( int iAxis, int* opLowerWeightCount, tBox3D* opBounds )
|
|
{
|
|
int MedianWeightCount = mWeightedSize / 2;
|
|
int WeightCount = 0;
|
|
int i;
|
|
|
|
for( i = 0; (i < mSize) && (WeightCount < MedianWeightCount); i++ )
|
|
{
|
|
WeightCount += mpCellList[i].RenderWeight();
|
|
}
|
|
return i;
|
|
}*/
|
|
|
|
|
|
|
|
|
|
int CoordSubList::ChooseAxis()
|
|
{
|
|
// int ChosenAxis;
|
|
|
|
//
|
|
// The idea is to choose the axis that needs
|
|
// subdivision the most.
|
|
//
|
|
// Key Properties:
|
|
// Count: same in each dimension
|
|
// Span: calc'd on mBounds
|
|
// Spread: may want to ensure that the evenly spread data gets divided
|
|
//
|
|
/*float Spans[3];
|
|
Spans[0] = mBounds.mMax[0] - mBounds.mMin[0];
|
|
Spans[1] = mBounds.mMax[1] - mBounds.mMin[1];
|
|
Spans[2] = mBounds.mMax[2] - mBounds.mMin[2];
|
|
*/
|
|
return mSpans.MaxIndex();
|
|
/*
|
|
if( mSpans[0] >= mSpans[1] )
|
|
{
|
|
if( mSpans[0] >= mSpans[2] )
|
|
{
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return 2;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( mSpans[1] >= mSpans[2] )
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 2;
|
|
}
|
|
}
|
|
*/
|
|
}
|
|
|
|
/*
|
|
char CoordSubList::BestSpreadAxis()
|
|
{
|
|
int i, i_plane, weightCount, MedianWeight = mWeightedSize/2;
|
|
Vector3i LTCounts, GTCounts;
|
|
|
|
qsort( mpCellList, mSize, sizeof(Cell), CompareCellXs );
|
|
for( i=0, weightCount=0; (i<mSize)&&(weightCount<MedianWeight); i++ )
|
|
{
|
|
weightCount += mpCellList.mpData[i].mRenderWeight();
|
|
}
|
|
//
|
|
// If we've surpassed the median, step back,
|
|
// undercut the median to handle the 2 element cases.
|
|
//
|
|
if( weightCount > MedianWeight )
|
|
{
|
|
if(i-1>=0)
|
|
{
|
|
i--;
|
|
}
|
|
}
|
|
i_plane = GetPlanarDivision( junkedPlane, 0, i );
|
|
|
|
qsort( mpCellList, mSize, sizeof(Cell), CompareCellYs );
|
|
qsort( mpCellList, mSize, sizeof(Cell), CompareCellZs );
|
|
mpCellList.mpData[i].
|
|
}
|
|
*/ |