#include <sys/timeb.h>
#include "gpuCacheSpatialSubdivision.h"
#include <maya/MMatrix.h>
#include <stdlib.h>
#include <tbb/parallel_for.h>
#include <tbb/parallel_reduce.h>
#include <tbb/blocked_range.h>
#include <maya/MGlobal.h>
#include <set>
namespace GPUCache {
    gpuCacheIsectAccelParams
        gpuCacheIsectAccelParams::uniformGridParams(
        int divX,
        int divY,
        int divZ
        )
    {
        return gpuCacheIsectAccelParams( gpuCacheIsectAccelParams::kUniformGrid,
            divX, divY, divZ );
    }
    gpuCacheIsectAccelParams
        gpuCacheIsectAccelParams::autoUniformGridParams()
    {
        return gpuCacheIsectAccelParams( gpuCacheIsectAccelParams::kAutoUniformGrid,
            -1, -1, -1 );
    }
    gpuCacheIsectAccelParams::gpuCacheIsectAccelParams()
        : fAlgorithm( gpuCacheIsectAccelParams::kUniformGrid ),
        fDivX(10),
        fDivY(10),
        fDivZ(10)
    {
    }
    gpuCacheIsectAccelParams::gpuCacheIsectAccelParams( 
        int alg, 
        int divX, 
        int divY, 
        int divZ 
        )
        : fAlgorithm(alg),
        fDivX(divX),
        fDivY(divY),
        fDivZ(divZ)
    {
    }
    int gpuCacheIsectAccelParams::operator==( const gpuCacheIsectAccelParams& rhs )
        
        
        
        
        
        
        
        
        
        
    {
        if( (fAlgorithm == rhs.fAlgorithm) &&
            (fDivX == rhs.fDivX) &&
            (fDivY == rhs.fDivY) &&
            (fDivZ == rhs.fDivZ) )
        {
            return 1;
        }
        else
        {
            return 0;
        }
    }
    int gpuCacheIsectAccelParams::operator!=( const gpuCacheIsectAccelParams& rhs )
        
        
        
        
        
    {
        return !((*this)==rhs);
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    class gpuCacheVoxelGrid : public SpatialGrid { 
    public: 
        typedef SpatialGrid ParentClass; 
        const unsigned int numTriangles;
        const index_t* srcTriangleVertIndices;
        const float* srcPositions;
        gridPoint3<int>* indexArrayRange;
        gpuCacheVoxelGrid( 
const MBoundingBox &bound, 
const gridPoint3<int> &numVoxels, 
unsigned int thisNumTriangles, 
const index_t* thisSrcTriangleVertIndices, 
const float* thisSrcPositions):
            SpatialGrid( bound, numVoxels ),
            numTriangles(thisNumTriangles),
            srcTriangleVertIndices(thisSrcTriangleVertIndices),
            srcPositions(thisSrcPositions)
        {
            addTrianglesToGrid();
        }
        virtual ~gpuCacheVoxelGrid(); 
        void operator()( const tbb::blocked_range<unsigned int> &br ) const;
        void                getTris( 
MIntArray &triArray, 
const gridPoint3<int> &grid); 
 
        virtual float       getMemoryFootprint(); 
    private: 
        void addTrianglesToGrid();
    }; 
    gpuCacheVoxelGrid::~gpuCacheVoxelGrid()
    {
    }
    struct TbbBuildVoxelGrid {
        const gpuCacheVoxelGrid *gpuGrid;
        void operator()( const tbb::blocked_range<unsigned int>& br ) const {
            for (unsigned int j = br.begin(); j != br.end(); j++) {
                index_t idx0=gpuGrid->srcTriangleVertIndices[3*j]*3;
                index_t idx1=gpuGrid->srcTriangleVertIndices[3*j+1]*3;
                index_t idx2=gpuGrid->srcTriangleVertIndices[3*j+2]*3;
                MPoint vertex1(gpuGrid->srcPositions[idx0],gpuGrid->srcPositions[idx0+1],gpuGrid->srcPositions[idx0+2]);
 
                MPoint vertex2(gpuGrid->srcPositions[idx1],gpuGrid->srcPositions[idx1+1],gpuGrid->srcPositions[idx1+2]);
 
                MPoint vertex3(gpuGrid->srcPositions[idx2],gpuGrid->srcPositions[idx2+1],gpuGrid->srcPositions[idx2+2]);
 
                
                
                gpuGrid->getVoxelRange( bbox2, gpuGrid->indexArrayRange[j*2], gpuGrid->indexArrayRange[j*2+1] );
            }
        }
        TbbBuildVoxelGrid( const gpuCacheVoxelGrid *thisGpuGrid ) : gpuGrid(thisGpuGrid) {}
        TbbBuildVoxelGrid( const TbbBuildVoxelGrid &thisTbbVoxelGrid ) : gpuGrid(thisTbbVoxelGrid.gpuGrid) {}
        ~TbbBuildVoxelGrid() {}
    };
    void gpuCacheVoxelGrid::addTrianglesToGrid() {
        indexArrayRange = new gridPoint3<int>[2*numTriangles];
        TbbBuildVoxelGrid tbbBVG(this);
        tbb::parallel_for(tbb::blocked_range<unsigned int>(0, numTriangles, 100), tbbBVG, tbb::auto_partitioner());
        for (unsigned int j = 0; j < numTriangles; j++) {
            const gridPoint3<int>& minIndices = indexArrayRange[j*2];
            const gridPoint3<int>& maxIndices = indexArrayRange[j*2+1];
            
            for( int x = minIndices[0]; x <= maxIndices[0]; x++ ) { 
                for( int y = minIndices[1]; y <= maxIndices[1]; y++ ) { 
                    for( int z = minIndices[2]; z <= maxIndices[2]; z++ ) { 
                        MUintArray *indices = getVoxelContents( gridPoint3<int>(x,y,z) );
 
                    }
                }
            }
        }
        delete [] indexArrayRange;
    }
void gpuCacheVoxelGrid::getTris( 
MIntArray &triArray,
 
    const gridPoint3<int> &gridLocation)
    
    
    
    
{
    MUintArray *values = getVoxelContents( gridLocation );
 
    unsigned int numTriangles = values->
length(); 
 
    
    if(triArray.
length() < numTriangles){
 
    }
    unsigned int nAdded = 0;
    for( unsigned int i = 0; i < numTriangles; i++ ) { 
        unsigned int index = (*values)[i]; 
        triArray[nAdded] = index;
        nAdded++;
    }
    
    if(triArray.
length() > nAdded){ 
 
    }   
}
float 
    gpuCacheVoxelGrid::getMemoryFootprint() 
    
    
    
    
    
{
    float totalClassSize = ParentClass::getMemoryFootprint();
    return totalClassSize;
}
class SimpleTimer
{
public:
    SimpleTimer() {};
    void startTimer()
    {
        fStartTime = GetMilliCount();
    }
    double elapsedTime()
    {
        return GetMilliSpan();
    }
private:
    int GetMilliCount()
    {
        timeb tb;
        ftime( &tb );
        int nCount = tb.millitm + (tb.time & 0xfffff) * 1000;
        return nCount;
    }
    int GetMilliSpan()
    {
        int nSpan = GetMilliCount() - fStartTime;
        if ( nSpan < 0 )
            nSpan += 0x100000 * 1000;
        return nSpan;
    }
    int fStartTime;
}; 
int gpuCacheSpatialSubdivision::fsTotalNumActiveSpatialSubdivisions = 0;
int gpuCacheSpatialSubdivision::fsTotalNumCreatedSpatialSubdivisions = 0;
float gpuCacheSpatialSubdivision::fsTotalMemoryFootprint = 0.0;
float gpuCacheSpatialSubdivision::fsPeakMemoryFootprint = 0.0;
float gpuCacheSpatialSubdivision::fsTotalBuildTime = 0.0;
gridPoint3<int> 
    computeBoundsFromTriangleDensity( 
    unsigned int numTriangles, const index_t* srcTriangleVertIndices, const float* srcPositions,
    int trianglesPerVoxel,
    const gridPoint3<int>& minVoxels,
    const gridPoint3<int>& maxVoxels
    )
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
{
    gridPoint3<int> res;
    
    
    
    float trianglesAlongAxis = powf( float(trianglesPerVoxel), 0.33f );
    
    
    
    float totalSize[3] = { 0.0, 0.0, 0.0 };
    for (unsigned int j = 0; j < numTriangles; j++) {
        index_t idx0=srcTriangleVertIndices[3*j]*3;
        index_t idx1=srcTriangleVertIndices[3*j+1]*3;
        index_t idx2=srcTriangleVertIndices[3*j+2]*3;
        MPoint vertex1(srcPositions[idx0],srcPositions[idx0+1],srcPositions[idx0+2]);
 
        MPoint vertex2(srcPositions[idx1],srcPositions[idx1+1],srcPositions[idx1+2]);
 
        MPoint vertex3(srcPositions[idx2],srcPositions[idx2+1],srcPositions[idx2+2]);
 
        
        
        totalSize[0] += triBound.
width();   
        totalSize[1] += triBound.
height();
        totalSize[2] += triBound.
depth();
    }
    float boundSize[3] = {
    };
    
    
    for( int i = 0; i < 3; i++ )
    {
        
        
        float avgSize = totalSize[i] / numTriangles;
        
        
        float voxelSize = avgSize * trianglesAlongAxis;
        
        
        
        float numVoxels = boundSize[i] / voxelSize;
        
        
        int iNumVoxels;
        if( numVoxels < minVoxels[i] )
        {
            iNumVoxels = minVoxels[i];
        }
        else if( numVoxels > maxVoxels[i] )
        {
            iNumVoxels = maxVoxels[i];
        }
        else
        {
            iNumVoxels = (int)ceil(numVoxels);
        }
        res[i] = iNumVoxels;
    }
    return res;
}
gpuCacheSpatialSubdivision::gpuCacheSpatialSubdivision( 
    const unsigned int numTriangles, 
    const index_t* srcTriangleVertIndices, 
    const float* srcPositions,  
    const gpuCacheIsectAccelParams& accelParams
    )
    : fAccelParams(accelParams)
    
    
    
    
    
    
    
    
    
    
    
    
{
    
    
    SimpleTimer myTimer;
    myTimer.startTimer();
    if( (accelParams.fAlgorithm == gpuCacheIsectAccelParams::kUniformGrid) ||
        (accelParams.fAlgorithm == gpuCacheIsectAccelParams::kAutoUniformGrid) )
    {
        gridPoint3<int> numSub;
        
        
        
        
        if( fAccelParams.fAlgorithm == gpuCacheIsectAccelParams::kAutoUniformGrid )
        {
            
            
            
            
            
            numSub = computeBoundsFromTriangleDensity( numTriangles, srcTriangleVertIndices, srcPositions,
                bounds, 
                12, 
                gridPoint3<int>(1,1,1), 
                gridPoint3<int>(100,100,100) );
        }
        else
        {
            numSub = gridPoint3<int>( fAccelParams.fDivX, 
                fAccelParams.fDivY, 
                fAccelParams.fDivZ );
        }
        
        
        fVoxelGrid = new gpuCacheVoxelGrid( bounds, numSub, numTriangles, srcTriangleVertIndices, srcPositions);
    }
    
    
    
    
    fMemoryFootprint = fVoxelGrid->getMemoryFootprint();
    fBuildTime = (float)myTimer.elapsedTime();
    fsTotalMemoryFootprint += fMemoryFootprint;
    if( fsTotalMemoryFootprint > fsPeakMemoryFootprint )
    {
        fsPeakMemoryFootprint = fsTotalMemoryFootprint;
    }
    fsTotalBuildTime += fBuildTime;
    fsTotalNumActiveSpatialSubdivisions++;
    fsTotalNumCreatedSpatialSubdivisions++;
}
gpuCacheSpatialSubdivision::~gpuCacheSpatialSubdivision()
    
    
    
    
    
    
    
{
    deleteVoxelGrid();
}
void gpuCacheSpatialSubdivision::deleteVoxelGrid()
    
    
    
    
    
{
    if( fVoxelGrid != NULL )
    {
        
        
        fsTotalNumActiveSpatialSubdivisions--;
        fsTotalMemoryFootprint -= fMemoryFootprint; 
        
        
        delete fVoxelGrid;
        fVoxelGrid = NULL;
    }           
}
struct TbbFindClosestEdgePoint {
    double minDist;
    const index_t* srcTriangleVertIndices;
    const float* srcPositions;
    void reset() {
        minDist = std::numeric_limits<double>::max();
    }
    TbbFindClosestEdgePoint( 
const index_t * thisSrcTriangleVertIndices, 
const float *thisSrcPositions, 
const MPoint &thisRayPoint, 
const MVector &thisRayDirection, 
const MIntArray &thisTriArray) : 
                srcTriangleVertIndices(thisSrcTriangleVertIndices), srcPositions(thisSrcPositions), rayPoint(thisRayPoint), rayDirection(thisRayDirection), triArray(thisTriArray) {
        reset();
    }
    TbbFindClosestEdgePoint(const TbbFindClosestEdgePoint& fCEP, tbb::split) : 
                srcTriangleVertIndices(fCEP.srcTriangleVertIndices), srcPositions(fCEP.srcPositions), rayPoint(fCEP.rayPoint), rayDirection(fCEP.rayDirection), triArray(fCEP.triArray) {
        reset();
    }
    void operator()(tbb::blocked_range<size_t> r) {
        int end=r.end();
        for( size_t j=r.begin(); j!=end; ++j ) {
                int triIndex = triArray[j];
                index_t idx0=srcTriangleVertIndices[3*triIndex]*3;
                index_t idx1=srcTriangleVertIndices[3*triIndex+1]*3;
                index_t idx2=srcTriangleVertIndices[3*triIndex+2]*3;
                MPoint vertex1(srcPositions[idx0],srcPositions[idx0+1],srcPositions[idx0+2]);
 
                MPoint vertex2(srcPositions[idx1],srcPositions[idx1+1],srcPositions[idx1+2]);
 
                MPoint vertex3(srcPositions[idx2],srcPositions[idx2+1],srcPositions[idx2+2]);
 
                double dist = gpuCacheIsectUtil::getEdgeSnapPointOnTriangle(rayPoint,rayDirection,vertex1,vertex2,vertex3,clsPoint);
                if(dist<minDist){
                    minDist = dist;
                    closestPoint = clsPoint;
                }   
            }
    }
    void join( TbbFindClosestEdgePoint &other ) {
        if(other.minDist < minDist ) {
            minDist = other.minDist;
            closestPoint = other.closestPoint;
        }
    }
};
double gpuCacheSpatialSubdivision::getEdgeSnapPoint(const unsigned int numTriangles, 
                                                   const index_t*   srcTriangleVertIndices, 
                                                   const float* srcPositions,   
{
    TbbFindClosestEdgePoint fCP( srcTriangleVertIndices, srcPositions, rayPoint, rayDirection, triArray );
    tbb::parallel_reduce(tbb::blocked_range<size_t>(0, triArray.
length()), fCP );
    closestPoint = fCP.closestPoint;
        return fCP.minDist;
}
double gpuCacheSpatialSubdivision::getEdgeSnapPoint(const unsigned int numTriangles, 
                                                   const index_t*   srcTriangleVertIndices, 
                                                   const float* srcPositions,   
{
    std::set< gridPoint3<int> > potentialVoxels;
    gridPoint3<int> numVoxelsByAxis = fVoxelGrid->getNumVoxels();
    int numVoxels = numVoxelsByAxis[0] * numVoxelsByAxis[1] * numVoxelsByAxis[2];
    MPoint voxSizes(bbox.
width()/numVoxelsByAxis[0],bbox.
height()/numVoxelsByAxis[1],bbox.
depth()/numVoxelsByAxis[2]);
 
    MPoint expandAmount = 0.1*voxSizes;
 
    double minDist = std::numeric_limits<double>::max();
    bool *checkedBox = new bool[numVoxels];
    double *allDists = new double[numVoxels];
    gridPoint3<int> closestGridPoint;
    for (int i=0;i<numVoxelsByAxis[0];i++) {
        for (int j=0;j<numVoxelsByAxis[1];j++) {
            for (int k=0;k<numVoxelsByAxis[2];k++) {
                gridPoint3<int> gridLocation = gridPoint3<int>(i,j,k);
                MUintArray *values = fVoxelGrid->getVoxelContents( gridLocation );
 
                int linearIndex = k * (numVoxelsByAxis[0] * numVoxelsByAxis[1]) + j * numVoxelsByAxis[0] + i;
                checkedBox[linearIndex] = false;
                if(values->length()>0){
                    MPoint c1 = bbox.
min() + 
MPoint(i*voxSizes[0],j*voxSizes[1],k*voxSizes[2]);
 
                    allDists[linearIndex] = gpuCacheIsectUtil::getEdgeSnapPointOnBox(rayPoint,rayDirection,voxBox,queryPoint);
                    if(allDists[linearIndex] < minDist){
                        minDist = allDists[linearIndex];
                        closestGridPoint = gridLocation;
                    }
                }
                else {
                    allDists[linearIndex] = std::numeric_limits<double>::max();
                }
            }
        }
    }
    for (int i=0;i<numVoxelsByAxis[0];i++) {
        for (int j=0;j<numVoxelsByAxis[1];j++) {
            for (int k=0;k<numVoxelsByAxis[2];k++) {
                int linearIndex = k * (numVoxelsByAxis[0] * numVoxelsByAxis[1]) + j * numVoxelsByAxis[0] + i;
                if(allDists[linearIndex]<=minDist){
                    potentialVoxels.insert(gridPoint3<int>(i,j,k));
                    checkedBox[linearIndex]=true;
                }
            }
        }
    }
    minDist = std::numeric_limits<double>::max();
    while(potentialVoxels.size()>0){
        std::set< gridPoint3<int> >::iterator voxelIt = potentialVoxels.begin();
        gridPoint3<int> gridLoc = *voxelIt;
        potentialVoxels.erase(voxelIt);
        int linearIndex = gridLoc[2] * (numVoxelsByAxis[0] * numVoxelsByAxis[1]) + gridLoc[1] * numVoxelsByAxis[0] + gridLoc[0];
        if(allDists[linearIndex]>minDist) continue;
        fVoxelGrid->getTris( triArray, gridLoc ); 
        double dist = getEdgeSnapPoint(numTriangles,srcTriangleVertIndices,srcPositions,rayPoint, rayDirection,triArray,clsPoint);
        if(dist<minDist){
            minDist = dist;
            closestPoint = clsPoint;
            for (int i=0;i<numVoxelsByAxis[0];i++){
                for (int j=0;j<numVoxelsByAxis[1];j++){
                    for (int k=0;k<numVoxelsByAxis[2];k++) {
                        int linearIndex = k * (numVoxelsByAxis[0] * numVoxelsByAxis[1]) + j * numVoxelsByAxis[0] + i;
                        if(!checkedBox[linearIndex] && allDists[linearIndex]<=minDist){
                            potentialVoxels.insert(gridPoint3<int>(i,j,k));
                            checkedBox[linearIndex]=true;
                        }
                    }
                }
            }
        } 
    }
    delete[] checkedBox;
    delete[] allDists;
    return minDist;
}
struct TbbFindClosestPoint {
    bool foundPoint;
    double minDist;
    const index_t *srcTriangleVertIndices;
    const float *srcPositions;
    void reset() {
        foundPoint = false;
        minDist = std::numeric_limits<double>::max();
    }
    TbbFindClosestPoint( 
const index_t * thisSrcTriangleVertIndices, 
const float *thisSrcPositions, 
const MIntArray &thisTriArray, 
const MPoint &thisQueryPoint ) :
        srcTriangleVertIndices(thisSrcTriangleVertIndices), srcPositions(thisSrcPositions), triArray(thisTriArray), queryPoint(thisQueryPoint) {
        reset();
    }
    TbbFindClosestPoint(const TbbFindClosestPoint& fCP, tbb::split) :
        srcTriangleVertIndices(fCP.srcTriangleVertIndices), srcPositions(fCP.srcPositions), triArray(fCP.triArray), queryPoint(fCP.queryPoint) {
        reset();
    }
    void operator()(tbb::blocked_range<size_t> r) {
        int end=r.end();
        for( int j=r.begin(); j!=end; ++j ) {
            int triIndex = triArray[j];
            index_t idx0=srcTriangleVertIndices[3*triIndex]*3;
            index_t idx1=srcTriangleVertIndices[3*triIndex+1]*3;
            index_t idx2=srcTriangleVertIndices[3*triIndex+2]*3;
            MPoint vertex1(srcPositions[idx0],srcPositions[idx0+1],srcPositions[idx0+2]);
 
            MPoint vertex2(srcPositions[idx1],srcPositions[idx1+1],srcPositions[idx1+2]);
 
            MPoint vertex3(srcPositions[idx2],srcPositions[idx2+1],srcPositions[idx2+2]);
 
            if(gpuCacheIsectUtil::getClosestPointOnTri(queryPoint, vertex1, vertex2, vertex3, clsPoint, minDist)){
                closestPoint = clsPoint;
                foundPoint = true;
            }
        }
    }
    void join( TbbFindClosestPoint &other ) {
        if( other.foundPoint && other.minDist < minDist ) {
            foundPoint = true;
            minDist = other.minDist;
            closestPoint = other.closestPoint;
        }
    }
};
bool gpuCacheSpatialSubdivision::closestPointToPoint(const unsigned int numTriangles, 
                                                     const index_t* srcTriangleVertIndices, 
                                                     const float*   srcPositions,   
{
    TbbFindClosestPoint fCP( srcTriangleVertIndices, srcPositions, triArray, queryPoint );
    tbb::parallel_reduce(tbb::blocked_range<size_t>(0, triArray.
length()), fCP );
    if( fCP.foundPoint ) {
        closestPoint = fCP.closestPoint;
        return true;
    }
    return false;
}
void gpuCacheSpatialSubdivision::closestPointToPoint(const unsigned int numTriangles, 
                                                     const index_t* srcTriangleVertIndices, 
                                                     const float*   srcPositions,   
{
    double minDist = std::numeric_limits<double>::max();
    
    std::set< gridPoint3<int> > potentialVoxels;
    std::set< gridPoint3<int> > checkedVoxels;
    gridPoint3<int> gridLocOrg;
    fVoxelGrid->getClosestVoxelCoords(queryPoint,gridLocOrg);
    potentialVoxels.insert(gridLocOrg);
    bool foundPoint = false;
    int expandVox = 0;
    while (!foundPoint)
    {
        while(potentialVoxels.size()>0){
            std::set< gridPoint3<int> >::iterator voxelIt = potentialVoxels.begin();
            gridPoint3<int> gridLoc = *voxelIt;
            fVoxelGrid->getTris( triArray, gridLoc ); 
            checkedVoxels.
insert(gridLoc);
            potentialVoxels.erase(voxelIt);
            if(closestPointToPoint(numTriangles,srcTriangleVertIndices,srcPositions,queryPoint,triArray,clsPoint)){
                if(dist<minDist){
                    minDist = dist;
                    closestPoint = clsPoint;
                    foundPoint = true;
                    gridPoint3<int> gridLocMin;
                    gridPoint3<int> gridLocMax;
                    fVoxelGrid->getClosestVoxelCoords(
MPoint(queryPoint[0] - dist, queryPoint[1] - dist, queryPoint[2] - dist),gridLocMin);
                    fVoxelGrid->getClosestVoxelCoords(
MPoint(queryPoint[0] + dist, queryPoint[1] + dist, queryPoint[2] + dist),gridLocMax);
                    for(int i=gridLocMin[0]; i<=gridLocMax[0]; i++) {
                        for(int j=gridLocMin[1]; j<=gridLocMax[1]; j++) {
                            for(int k=gridLocMin[2]; k<=gridLocMax[2]; k++) {
                                gridPoint3<int> gridLocNew = gridPoint3<int>(i,j,k);
                                if(fVoxelGrid->isValidVoxel(gridLocNew) && checkedVoxels.find(gridLocNew) == checkedVoxels.end()) {
                                    potentialVoxels.insert(gridLocNew);
                                }
                            }
                        }
                    }
                }
            } 
        }
        expandVox++;
        if(!foundPoint){
            for(int i=-expandVox; i<=expandVox; i++) {
                for(int j=-expandVox; j<=expandVox; j++) {
                    for(int k=-expandVox; k<=expandVox; k++) {
                        gridPoint3<int> gridLocNew = gridLocOrg + gridPoint3<int>(i,j,k);
                        if(fVoxelGrid->isValidVoxel(gridLocNew) && checkedVoxels.find(gridLocNew) == checkedVoxels.end()) {
                            potentialVoxels.insert(gridLocNew);
                        }
                    }
                }
            }
        }
    }
}
struct TbbFindClosestIntersection {
    bool foundIntersection;
    double minDist;
    const index_t *srcTriangleVertIndices;
    const float *srcPositions;
    void reset() {
        foundIntersection = false;
        minDist = std::numeric_limits<double>::max();
    }
    TbbFindClosestIntersection( 
const index_t * thisSrcTriangleVertIndices, 
const float *thisSrcPositions, 
const MIntArray &thisTriArray, 
const MPoint &thisRaySource, 
const MVector &thisRayDirection ) :
        srcTriangleVertIndices(thisSrcTriangleVertIndices), srcPositions(thisSrcPositions), triArray(thisTriArray), raySource(thisRaySource), rayDirection(thisRayDirection) {
        reset();
    }
    TbbFindClosestIntersection(const TbbFindClosestIntersection& fCIS, tbb::split) :
        srcTriangleVertIndices(fCIS.srcTriangleVertIndices), srcPositions(fCIS.srcPositions), triArray(fCIS.triArray), raySource(fCIS.raySource), rayDirection(fCIS.rayDirection) {
        reset();
    }
    void operator()(tbb::blocked_range<size_t> r) {
        int end=r.end();
        for( int i=r.begin(); i!=end; ++i ) {
            int triIndex = triArray[i];
            index_t idx0=srcTriangleVertIndices[3*triIndex]*3;
            index_t idx1=srcTriangleVertIndices[3*triIndex+1]*3;
            index_t idx2=srcTriangleVertIndices[3*triIndex+2]*3;
            MPoint vertex1(srcPositions[idx0],srcPositions[idx0+1],srcPositions[idx0+2]);
 
            MPoint vertex2(srcPositions[idx1],srcPositions[idx1+1],srcPositions[idx1+2]);
 
            MPoint vertex3(srcPositions[idx2],srcPositions[idx2+1],srcPositions[idx2+2]);
 
            MVector c0, c1, rhs, crossc1c2, crossc0rhs;
 
            double beta, gamm, t, M;
            c0 = vertex1 - vertex2;
            c1 = vertex1 - vertex3;
            rhs = vertex1 - 
MVector(raySource);
            crossc1c2 = c1 ^ rayDirection;
            crossc0rhs = c0 ^ rhs;
            M = c0 * crossc1c2;
            if (M==0) continue;
            t = -(c1 * crossc0rhs)/M; 
            if (t < 0.0 || t > minDist) continue;
            beta = (rhs * crossc1c2)/M;  
            if (beta < 0  || beta > 1) continue;
            gamm = (rayDirection * crossc0rhs)/M;
            if (gamm < 0 || gamm > 1 - beta) continue;
            
            minDist = t;
            closestIntersection = raySource + t * rayDirection;
            closestNormal = (c0 ^ c1).normal();
            foundIntersection = true;
        }
    }
    void join( TbbFindClosestIntersection &other ) {
        if( other.foundIntersection && other.minDist < minDist ) {
            foundIntersection = true;
            minDist = other.minDist;
            closestIntersection = other.closestIntersection;
            closestNormal = other.closestNormal;
        }
    }
};
MStatus gpuCacheSpatialSubdivision::closestIntersection( 
 
    const unsigned int numTriangles, 
    const index_t*  srcTriangleVertIndices, 
    const float*    srcPositions,   
    float           maxParam,
    )
{
    TbbFindClosestIntersection fCIS( srcTriangleVertIndices, srcPositions, triArray, origin, direction );
    tbb::parallel_reduce(tbb::blocked_range<size_t>(0, triArray.
length()), fCIS );
    if( fCIS.foundIntersection ) {
        closestIsect = fCIS.closestIntersection;
        isectNormal = fCIS.closestNormal;
    }
}
MStatus gpuCacheSpatialSubdivision::closestIntersection( 
 
    const unsigned int numTriangles, 
    const index_t*  srcTriangleVertIndices, 
    const float*    srcPositions,   
    float           maxParam,
    )
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
{
    
    
    SpatialGridWalker it = fVoxelGrid->getRayIterator( origin, direction );
    maxParam = fabs(maxParam);
    while( !it.isDone() )
    {
        
        
        if( it.curVoxelStartRayParam() > maxParam )
        {
            break;
        }
        
        
        gridPoint3<int> gridLoc = it.gridLocation(); 
        fVoxelGrid->getTris( triArray, gridLoc); 
        if ( triArray.
length() > 0 ) { 
 
            
            
            
            
            float voxelMaxParam = std::min( it.curVoxelEndRayParam(), maxParam );
            
            
            if( 
MStatus::kSuccess == closestIntersection( numTriangles, srcTriangleVertIndices, srcPositions, origin, direction, 
 
                triArray, voxelMaxParam, closestIsect, isectNormal ) )
            {
            }
        }
        it.next();
    }
}
float gpuCacheSpatialSubdivision::getMemoryFootprint()
    
    
    
    
    
{
    return fMemoryFootprint;
}   
float gpuCacheSpatialSubdivision::getBuildTime()
    
    
    
    
    
{
    return fBuildTime;
}
MString gpuCacheSpatialSubdivision::getDescription( 
bool includeStats )
 
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
{
    gridPoint3<int> numVoxels = fVoxelGrid->getNumVoxels();
    char buf[512];
    if( fAccelParams.fAlgorithm == gpuCacheIsectAccelParams::kUniformGrid )
    {
        sprintf( buf, "%dx%dx%d Uniform Grid", numVoxels[0], 
            numVoxels[1], 
            numVoxels[2] );
    }
    else if( fAccelParams.fAlgorithm == gpuCacheIsectAccelParams::kAutoUniformGrid )
    {
        sprintf( buf, "%dx%dx%d Auto-Configured Uniform Grid", 
            numVoxels[0], numVoxels[1], numVoxels[2] );
    }
    if( includeStats )
    {
        char buf2[512];
        sprintf( buf2, "build time %.2fs", fBuildTime );
        sprintf( buf2, "memory footprint %.2fKB", fMemoryFootprint );
    }
    return resultStr;
}
MString gpuCacheSpatialSubdivision::systemStats()
 
    
    
    
    
    
    
    
    
    
{
    char buf[1024];
    sprintf( buf, "total %d isect accelerators created (%d currently active - "
        "total current memory = %.2f KB), total build time = %f ms, "
        "peak memory = %.2f KB\n",
        fsTotalNumCreatedSpatialSubdivisions, 
        fsTotalNumActiveSpatialSubdivisions, 
        fsTotalMemoryFootprint, 
        fsTotalBuildTime, 
        fsPeakMemoryFootprint );
}
void gpuCacheSpatialSubdivision::resetSystemStats()
    
    
    
    
    
    
    
    
    
{
    fsTotalNumCreatedSpatialSubdivisions = 0;
    fsTotalBuildTime = 0.0f;
    fsPeakMemoryFootprint = 0.0f;
}
bool
    gpuCacheSpatialSubdivision::matchesParams( 
    const gpuCacheIsectAccelParams& accelParams 
    )
    
    
    
    
    
    
{
    if( fVoxelGrid != NULL )
    {
        return (fAccelParams == accelParams) ? true : false;
    }
    else
    {
        return false;
    }
}
}