//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $Workfile:     $
// $Date:         $
// $NoKeywords: $
//=============================================================================//

//#include <stdafx.h>
#include <stdlib.h>
#include <malloc.h>
#include "builddisp.h"
#include "collisionutils.h"
#include "tier1/strtools.h"
#include "tier0/dbg.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

//
// Node Functions (friend functions)
//

//-----------------------------------------------------------------------------
// should make this more programatic and extensible!
//-----------------------------------------------------------------------------
int GetNodeLevel( int index )
{
    // root
    if( index == 0 )
        return 1;

    // [1...4]
    if( index < 5 )
        return 2;

    // [5....20]
    if( index < 21 )
        return 3;

    // [21....84]
    if( index < 85 )
        return 4;

    // error!!!
    return -1;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetNodeCount( int power )
{
    return ( ( 1 << ( power << 1 ) ) / 3 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetNodeParent( int index )
{
	// ( index - 1 ) / 4
	return ( ( index - 1 ) >> 2 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetNodeChild( int power, int index, int direction )
{
    // ( index * 4 ) + direction
    return ( ( index << 2 ) + ( direction - 3 ) );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetNodeMinNodeAtLevel( int level )
{
    switch( level )
    {
    case 1: return 0;
    case 2: return 1;
    case 3: return 5;
    case 4: return 21;
    default: return -99999;
    }
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void GetComponentsFromNodeIndex( int index, int *x, int *y )
{
	*x = 0;
	*y = 0;

	for( int shift = 0; index != 0; shift++ )
	{
		*x |= ( index & 1 ) << shift;
		index >>= 1;

		*y |= ( index & 1 ) << shift;
		index >>= 1;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetNodeIndexFromComponents( int x, int y )
{
	int index = 0;

	// Interleave bits from the x and y values to create the index:
	
	int shift;
	for( shift = 0; x != 0; shift += 2, x >>= 1 )
	{
		index |= ( x & 1 ) << shift;
	}

	for( shift = 1; y != 0; shift += 2, y >>= 1 )
	{
		index |= ( y & 1 ) << shift;
	}

	return index;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CalcBarycentricCooefs( Vector const &v0, Vector const &v1, Vector const &v2,
						    Vector const &pt, float &c0, float &c1, float &c2 )
{
	Vector vSeg0, vSeg1, vCross;
	vSeg0 = v1 - v0;
	vSeg1 = v2 - v0;

	// get the area of the triangle
	vCross = vSeg0.Cross( vSeg1 );
	float totalArea = vCross.Length() * 0.5f;
	float ooTotalArea = totalArea ? 1.0f / totalArea : 0.0f;

	// get the area for cooeficient 0 (pt, v1, v2)
	vSeg0 = v1 - pt;
	vSeg1 = v2 - pt;
	vCross = vSeg0.Cross( vSeg1 );
	float subArea = vCross.Length() * 0.5f;
	c0 = subArea * ooTotalArea;

	// get the area for cooeficient 1 (v0, pt, v2)
	vSeg0 = v2 - pt;
	vSeg1 = v0 - pt;
	vCross = vSeg0.Cross( vSeg1 );
	subArea = vCross.Length() * 0.5f;
	c1 = subArea * ooTotalArea;

	// get the area for cooeficient 2 (v0, v1, pt)
	vSeg0 = v0 - pt;
	vSeg1 = v1 - pt;
	vCross = vSeg0.Cross( vSeg1 );
	subArea = vCross.Length() * 0.5f;
	c2 = subArea * ooTotalArea;

	float cTotal = c0 + c1 + c2;
	if ( FloatMakePositive( 1.0f - cTotal ) < 1e-3 )
		return true;

	return false;
}

// For some reason, the global optimizer screws up the recursion here.  disable the global optimizations to fix this.
// IN VC++ 6.0
#pragma optimize( "g", off )

CCoreDispSurface::CCoreDispSurface()
{
	Init();
}


//=============================================================================
//
// CDispSurface Functions
//

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispSurface::Init( void )
{
	m_Index = -1;

	m_PointCount = 0;
	int i;
	for( i = 0; i < QUAD_POINT_COUNT; i++ )
	{
		VectorClear( m_Points[i] );
		VectorClear( m_Normals[i] );
		Vector2DClear( m_TexCoords[i] );

		for( int j = 0; j < NUM_BUMP_VECTS+1; j++ )
		{
			Vector2DClear( m_LuxelCoords[i][j] );
		}

		m_Alphas[i] = 1.0f;
	}

	m_PointStartIndex = -1;
	VectorClear( m_PointStart );
	VectorClear( sAxis );
	VectorClear( tAxis );

	for( i = 0; i < 4; i++ )
	{
		m_EdgeNeighbors[i].SetInvalid();
		m_CornerNeighbors[i].SetInvalid();
	}

	m_Flags = 0;
	m_Contents = 0;
}


void CCoreDispSurface::SetNeighborData( const CDispNeighbor edgeNeighbors[4], const CDispCornerNeighbors cornerNeighbors[4] )
{
	for ( int i=0; i < 4; i++ )
	{
		m_EdgeNeighbors[i] = edgeNeighbors[i];
		m_CornerNeighbors[i] = cornerNeighbors[i];
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispSurface::GeneratePointStartIndexFromMappingAxes( Vector const &sAxis, Vector const &tAxis )
{
	if( m_PointStartIndex != -1 )
		return;
	
	int	numIndices = 0;
    int indices[4];
    int offsetIndex;

    //
    // project all points on to the v-axis first and find the minimum
    //
	float minValue = DotProduct( tAxis, m_Points[0] );
    indices[numIndices] = 0;
    numIndices++;

	int i;
    for( i = 1; i < m_PointCount; i++ )
    {
		float value = DotProduct( tAxis, m_Points[i] );
		float delta = ( value - minValue );
		delta = FloatMakePositive( delta );
        if( delta < 0.1 )
        {
            indices[numIndices] = i;
            numIndices++;
        }
        else if( value < minValue )
        {
            minValue = value;
            indices[0] = i;
            numIndices = 1;
        }
    }

    //
    // break ties with the u-axis projection
    //
	minValue = DotProduct( sAxis, m_Points[indices[0]] );
    offsetIndex = indices[0];
    
    for( i = 1; i < numIndices; i++ )
    {
		float value = DotProduct( sAxis, m_Points[indices[i]] );
        if( ( value < minValue ) )
        {
            minValue = value;
            offsetIndex = indices[i];
        }
    }

	m_PointStartIndex = offsetIndex;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CCoreDispSurface::GenerateSurfPointStartIndex( void )
{
	//
	// get the minimum surface component values
	//
	Vector bMin;
	VectorFill( bMin, 99999.0f );

	int i;
	for( i = 0; i < QUAD_POINT_COUNT; i++ )
	{
		for( int j = 0; j < 3; j++ )
		{
			if( m_Points[i][j] < bMin[j] )
			{
				bMin[j] = m_Points[i][j];
			}
		}
	}

	//
	// find the point closest to the minimum, that is the start point
	//
	int minIndex = -1;
	float minDistance = 999999999.0f;
	for( i = 0; i < QUAD_POINT_COUNT; i++ )
	{
		Vector segment;
		segment = m_Points[i] - bMin;
		float distanceSq = segment.LengthSqr();
		if( distanceSq < minDistance )
		{
			minDistance = distanceSq;
			minIndex = i;
		}
	}

	m_PointStartIndex = minIndex;

	return minIndex;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CCoreDispSurface::FindSurfPointStartIndex( void )
{
	if( m_PointStartIndex != -1 )
		return m_PointStartIndex;

	int minIndex = -1;
	float minDistance = 999999999.0f;

	for( int i = 0; i < QUAD_POINT_COUNT; i++ )
	{
		Vector segment;
		VectorSubtract( m_PointStart, m_Points[i], segment );
		float distanceSq = segment.LengthSqr();
		if( distanceSq < minDistance )
		{
			minDistance = distanceSq;
			minIndex = i;
		}
	}

	m_PointStartIndex = minIndex;

	return minIndex;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispSurface::AdjustSurfPointData( void )
{
	Vector tmpPoints[4];
	Vector tmpNormals[4];
	Vector2D tmpTexCoords[4];
	float  tmpAlphas[4];

	int i;
	for( i = 0; i < QUAD_POINT_COUNT; i++ )
	{
		VectorCopy( m_Points[i], tmpPoints[i] );
		VectorCopy( m_Normals[i], tmpNormals[i] );
		Vector2DCopy( m_TexCoords[i], tmpTexCoords[i] );

		tmpAlphas[i] = m_Alphas[i];
	}

	for( i = 0; i < QUAD_POINT_COUNT; i++ )
	{
		VectorCopy( tmpPoints[(i+m_PointStartIndex)%4], m_Points[i] );
		VectorCopy( tmpNormals[(i+m_PointStartIndex)%4], m_Normals[i] );
		Vector2DCopy( tmpTexCoords[(i+m_PointStartIndex)%4], m_TexCoords[i] );

		m_Alphas[i] = tmpAlphas[i];
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CCoreDispSurface::LongestInU( const Vector &vecU, const Vector &vecV )
{
	Vector vecNormU = vecU;
	Vector vecNormV = vecV;
	VectorNormalize( vecNormU );
	VectorNormalize( vecNormV );

	float flDistU[4];
	float flDistV[4];
	for ( int iPoint = 0; iPoint < 4; ++iPoint )
	{
		flDistU[iPoint] = vecNormU.Dot( m_Points[iPoint] );
		flDistV[iPoint] = vecNormV.Dot( m_Points[iPoint] );
	}

	float flULength = 0.0f;
	float flVLength = 0.0f;
	for ( int iPoint = 0; iPoint < 4; ++iPoint )
	{
		float flTestDist = fabs( flDistU[(iPoint+1)%4] - flDistU[iPoint] );
		if ( flTestDist > flULength )
		{
			flULength = flTestDist;
		}

		flTestDist = fabs( flDistV[(iPoint+1)%4] - flDistV[iPoint] );
		if ( flTestDist > flVLength )
		{
			flVLength = flTestDist;
		}
	}

	if ( flULength < flVLength )
	{
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  :  - 
//-----------------------------------------------------------------------------
bool CCoreDispSurface::CalcLuxelCoords( int nLuxels, bool bAdjust, const Vector &vecU, const Vector &vecV )
{
	// Valid value?
	if ( nLuxels <= 0.0f )
		return false;

	// Get the start point offset.
	int iOffset = 0;
	if ( bAdjust )
	{
		iOffset = GetPointStartIndex();
	}

	// Does projecting along U or V create the longest edge?
	bool bLongU = LongestInU( vecU, vecV );

	float flLengthTemp = 0.0f;
	float flULength = ( m_Points[(3+iOffset)%4] - m_Points[(0+iOffset)%4] ).Length();
	flLengthTemp = ( m_Points[(2+iOffset)%4] - m_Points[(1+iOffset)%4] ).Length();
	if ( flLengthTemp > flULength )
	{
		flULength = flLengthTemp;
	}

	// Find the largest edge in V.
	float flVLength = ( m_Points[(1+iOffset)%4] - m_Points[(0+iOffset)%4] ).Length();
	flLengthTemp = ( m_Points[(2+iOffset)%4] - m_Points[(3+iOffset)%4] ).Length();
	if ( flLengthTemp > flVLength )
	{
		flVLength = flLengthTemp;
	}

	float flOOLuxelScale = 1.0f / static_cast<float>( nLuxels );
	float flUValue = static_cast<float>( static_cast<int>( flULength * flOOLuxelScale ) + 1 );
	if ( flUValue > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER )
	{
		flUValue = MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER;
	}

	float flVValue = static_cast<float>( static_cast<int>( flVLength * flOOLuxelScale ) + 1 );
	if ( flVValue > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER )
	{
		flVValue = MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER;
	}

	// Swap if necessary.
	bool bSwapped = false;
	if ( bLongU )
	{
		if ( flVValue > flUValue )
		{
			bSwapped = true;
		}
	}
	else
	{
		if ( flUValue > flVValue )
		{
			bSwapped = true;
		}
	}

	m_nLuxelU = static_cast<int>( flUValue );
	m_nLuxelV = static_cast<int>( flVValue );

	// Generate luxel coordinates.
	for( int iBump = 0; iBump < NUM_BUMP_VECTS+1; ++iBump )
	{
		m_LuxelCoords[iBump][(0+iOffset)%4].Init( 0.5f, 0.5f );
		m_LuxelCoords[iBump][(1+iOffset)%4].Init( 0.5f, flVValue + 0.5 );
		m_LuxelCoords[iBump][(2+iOffset)%4].Init( flUValue + 0.5, flVValue + 0.5 );
		m_LuxelCoords[iBump][(3+iOffset)%4].Init( flUValue + 0.5, 0.5f );
	}

	return bSwapped;
}

//=============================================================================
//
// CDispNode Functions
//


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispNode::Init( void )
{
	VectorClear( m_BBox[0] );
	VectorClear( m_BBox[1] );
	
	m_ErrorTerm = 0.0f;
	
	m_VertIndex = -1;
	
	int j;
	for( j = 0; j < MAX_NEIGHBOR_NODE_COUNT; j++ )
	{
		m_NeighborVertIndices[j] = -1;
	}
	
	for( j = 0; j < MAX_SURF_AT_NODE_COUNT; j++ )
	{
		VectorClear( m_SurfBBoxes[j][0] );
		VectorClear( m_SurfBBoxes[j][1] );
		VectorClear( m_SurfPlanes[j].normal );
		m_SurfPlanes[j].dist = 0.0f;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void GetDispNodeTriVerts( CCoreDispInfo *pDisp, int nodeIndex, int triIndex, Vector& v1, Vector& v2, Vector& v3 )
{
	// get the node
	CCoreDispNode *pNode = pDisp->GetNode( nodeIndex );

	switch( triIndex )
	{
	case 0:
		{
			pDisp->GetVert( pNode->GetNeighborVertIndex( 4 ), v1 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v3 );
			return;
		}
	case 1:
		{
			pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v2 );
			pDisp->GetVert( pNode->GetCenterVertIndex(), v3 );
			return;
		}
	case 2:
		{
			pDisp->GetVert( pNode->GetNeighborVertIndex( 3 ), v1 );
			pDisp->GetVert( pNode->GetCenterVertIndex(), v2 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v3 );
			return;
		}
	case 3:
		{
			pDisp->GetVert( pNode->GetNeighborVertIndex( 5 ), v1 );
			pDisp->GetVert( pNode->GetCenterVertIndex(), v2 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 );
			return;
		}
	case 4:
		{
			pDisp->GetVert( pNode->GetNeighborVertIndex( 0 ), v1 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 );
			pDisp->GetVert( pNode->GetCenterVertIndex(), v3 );
			return;
		}
	case 5:
		{
			pDisp->GetVert( pNode->GetCenterVertIndex(), v1 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 6 ), v2 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v3 );
			return;
		}
	case 6:
		{
			pDisp->GetVert( pNode->GetCenterVertIndex(), v1 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v3 );
			return;
		}
	case 7:
		{
			pDisp->GetVert( pNode->GetNeighborVertIndex( 2 ), v1 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 1 ), v2 );
			pDisp->GetVert( pNode->GetNeighborVertIndex( 7 ), v3 );
			return;
		}
	default: { return; }
	}
}


//=============================================================================
//
// CCoreDispInfo Functions
//

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CCoreDispInfo::CCoreDispInfo()
{
	m_pVerts = NULL;
	m_RenderIndices = NULL;
	m_Nodes = NULL;
	m_pTris = NULL;

	// initialize the base surface data
	m_Surf.Init();
	
	//
	// initialize the disp info
	//
	m_Power = 0;
	m_Elevation = 0.0f;
	m_RenderIndexCount = 0;
	m_RenderCounter = 0;	
	m_bTouched = false;

	m_pNext = NULL;

	m_ppListBase = NULL;
	m_ListSize = 0;
	m_nListIndex = -1;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CCoreDispInfo::~CCoreDispInfo()
{
	if (m_pVerts)
		delete [] m_pVerts;
	if (m_RenderIndices)
		delete [] m_RenderIndices;
	if (m_Nodes)
		delete [] m_Nodes;
	if (m_pTris)
		delete [] m_pTris;
}


#if 0
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::InitSurf( int parentIndex, Vector points[4], Vector normals[4],
		                      Vector2D texCoords[4], Vector2D lightCoords[4][4], int contents, int flags,
							  bool bGenerateSurfPointStart, Vector& startPoint, 
				              bool bHasMappingAxes, Vector& uAxis, Vector& vAxis )
{	
	// save the "parent" index
	m_Surf.m_Index = parentIndex;
	
	//
	// save the surface points and point normals, texture coordinates, and
	// lightmap coordinates
	//
	m_Surf.m_PointCount = CSurface::QUAD_POINT_COUNT;
	for( int i = 0; i < CSurface::QUAD_POINT_COUNT; i++ )
	{
		VectorCopy( points[i], m_Surf.m_Points[i] );

		if( normals )
		{
			VectorCopy( normals[i], m_Surf.m_pVerts[i].m_Normal );
		}

		if( texCoords )
		{
			Vector2DCopy( texCoords[i], m_Surf.m_TexCoords[i] );
		}

		if( lightCoords )
		{
			Assert( NUM_BUMP_VECTS == 3 );
			Vector2DCopy( lightCoords[0][i], m_Surf.m_LightCoords[i][0] );
			Vector2DCopy( lightCoords[1][i], m_Surf.m_LightCoords[i][1] );
			Vector2DCopy( lightCoords[2][i], m_Surf.m_LightCoords[i][2] );
			Vector2DCopy( lightCoords[3][i], m_Surf.m_LightCoords[i][3] );
		}
	}
	
	// save the starting point
	if( startPoint )
	{
		VectorCopy( startPoint, m_Surf.m_PointStart );
	}

	//
	// save the surface contents and flags
	//
	m_Contents = contents;
	m_Flags = flags;	

	//
	// adjust surface points, texture coordinates, etc....
	//
	if( bHasMappingAxes && ( m_Surf.m_PointStartIndex == -1 ) )
	{
		GeneratePointStartIndexFromMappingAxes( uAxis, vAxis );
	}
	else
	{
		//
		// adjust the surf data
		//
		if( bGenerateSurfPointStart )
		{
			GenerateSurfPointStartIndex();
		}
		else
		{
			FindSurfPointStartIndex();
		}
	}

	AdjustSurfPointData();
}
#endif


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle,
		                          float *alphas, Vector *dispVectorField,  float *dispDistances )
{
	Assert( power >= MIN_MAP_DISP_POWER && power <= MAX_MAP_DISP_POWER );

	//
	// general displacement data
	//
	m_Power = power;

	if ( ( minTess & 0x80000000 ) != 0 )
	{
		// If the high bit is set, this represents FLAGS (SURF_NOPHYSICS_COLL, etc.) flags.
		int nFlags = minTess;
		nFlags &= ~0x80000000;
		GetSurface()->SetFlags( nFlags );
	}

	// Allocate + initialize verts
	int size = GetSize();
	m_pVerts = new CoreDispVert_t[size];

	int nIndexCount = size * 2 * 3;
	m_RenderIndices = new unsigned short[nIndexCount];

	int nNodeCount = GetNodeCount(power);
	m_Nodes = new CCoreDispNode[nNodeCount];

	int i;
	for( i = 0; i < size; i++ )
	{
		m_pVerts[i].m_FieldVector.Init();
		m_pVerts[i].m_SubdivPos.Init();
		m_pVerts[i].m_SubdivNormal.Init();
		
		m_pVerts[i].m_FieldDistance = 0.0f;
	
		m_pVerts[i].m_Vert.Init();
		m_pVerts[i].m_FlatVert.Init();
		m_pVerts[i].m_Normal.Init();
		m_pVerts[i].m_TangentS.Init();
		m_pVerts[i].m_TangentT.Init();
		m_pVerts[i].m_TexCoord.Init();

		for( int j = 0; j < ( NUM_BUMP_VECTS + 1 ); j++ )
		{
			m_pVerts[i].m_LuxelCoords[j].Init();
		}

		m_pVerts[i].m_Alpha = 0.0f;
	}

	for( i = 0; i < nIndexCount; i++ )
	{
		m_RenderIndices[i] = 0;
	}

	for( i = 0; i < nNodeCount; i++ )
	{
		m_Nodes[i].Init();
	}

	//
	// save the displacement vector field and distances within the field
	// offset have been combined with fieldvectors at this point!!!
	//
	if (alphas && dispVectorField && dispDistances)
	{
		for( i = 0; i < size; i++ )
		{
			VectorCopy( dispVectorField[i], m_pVerts[i].m_FieldVector );
			m_pVerts[i].m_FieldDistance = dispDistances[i];
			m_pVerts[i].m_Alpha = alphas[i];
		}
	}

	// Init triangle information.
	int nTriCount = GetTriCount();
	if ( nTriCount != 0 )
	{
		m_pTris = new CoreDispTri_t[nTriCount];
		if ( m_pTris )
		{
			InitTris();
		}
	}
}


void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts,
								  const CDispTri *pTris )
{
	Vector vectors[MAX_DISPVERTS];
	float dists[MAX_DISPVERTS];
	float alphas[MAX_DISPVERTS];

	int nVerts = NUM_DISP_POWER_VERTS( power );
	for ( int i=0; i < nVerts; i++ )
	{
		vectors[i] = pVerts[i].m_vVector;
		dists[i] = pVerts[i].m_flDist;
		alphas[i] = pVerts[i].m_flAlpha;
	}

	InitDispInfo( power, minTess, smoothingAngle, alphas, vectors, dists );

	int nTris = NUM_DISP_POWER_TRIS( power );
	for ( int iTri = 0; iTri < nTris; ++iTri )
	{
		m_pTris[iTri].m_uiTags = pTris[iTri].m_uiTags;
	}
}


void CCoreDispInfo::SetDispUtilsHelperInfo( CCoreDispInfo **ppListBase, int listSize )
{
	m_ppListBase = ppListBase;
	m_ListSize = listSize;
}

const CPowerInfo* CCoreDispInfo::GetPowerInfo() const
{
	return ::GetPowerInfo( GetPower() );
}

CDispNeighbor* CCoreDispInfo::GetEdgeNeighbor( int index )
{
	return GetSurface()->GetEdgeNeighbor( index );
}

CDispCornerNeighbors* CCoreDispInfo::GetCornerNeighbors( int index )
{
	return GetSurface()->GetCornerNeighbors( index );
}

CDispUtilsHelper* CCoreDispInfo::GetDispUtilsByIndex( int index )
{
	Assert( m_ppListBase );
	return index == 0xFFFF ? 0 : m_ppListBase[index];
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::BuildTriTLtoBR( int ndx )
{
    // get width and height of displacement maps
	int nWidth = ( ( 1 << m_Power ) + 1 );
	
	m_RenderIndices[m_RenderIndexCount] = ndx;
	m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth;
	m_RenderIndices[m_RenderIndexCount+2] = ndx + 1;
	m_RenderIndexCount += 3;

	m_RenderIndices[m_RenderIndexCount] = ndx + 1;
	m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth;
	m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1;
	m_RenderIndexCount += 3;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::BuildTriBLtoTR( int ndx )
{
    // get width and height of displacement maps
	int nWidth = ( ( 1 << m_Power ) + 1 );
	
	m_RenderIndices[m_RenderIndexCount] = ndx;
	m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth;
	m_RenderIndices[m_RenderIndexCount+2] = ndx + nWidth + 1;
	m_RenderIndexCount += 3;

	m_RenderIndices[m_RenderIndexCount] = ndx;
	m_RenderIndices[m_RenderIndexCount+1] = ndx + nWidth + 1;
	m_RenderIndices[m_RenderIndexCount+2] = ndx + 1;
	m_RenderIndexCount += 3;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::GenerateCollisionSurface( void )
{
    // get width and height of displacement maps
	int nWidth = ( ( 1 << m_Power ) + 1 );
	int nHeight = ( ( 1 << m_Power ) + 1 );

	//
	// generate a fan tesselated (at quadtree node) rendering index list
	//
	m_RenderIndexCount = 0;
	for ( int iV = 0; iV < ( nHeight - 1 ); iV++ )
	{
		for ( int iU = 0; iU < ( nWidth - 1 ); iU++ )
		{
			int ndx = ( iV * nWidth ) + iU;

			// test whether or not the index is odd
			bool bOdd = ( ( ndx %2 ) == 1 );

			// Top Left to Bottom Right
			if( bOdd )
			{
				BuildTriTLtoBR( ndx );
			}
			// Bottom Left to Top Right
			else
			{
				BuildTriBLtoTR( ndx );
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::GenerateCollisionData( void )
{
    GenerateCollisionSurface();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcTriSurfPlanes( int nodeIndex, int indices[8][3] )
{
    //
    // calculate plane info for each face
    //
    for( int i = 0; i < 8; i++ )
    {
		Vector v[3];
        VectorCopy( m_pVerts[indices[i][0]].m_Vert, v[0] );
        VectorCopy( m_pVerts[indices[i][1]].m_Vert, v[1] );
        VectorCopy( m_pVerts[indices[i][2]].m_Vert, v[2] );

		Vector seg[2];
        VectorSubtract( v[1], v[0], seg[0] );
        VectorSubtract( v[2], v[0], seg[1] );

		Vector normal;
        CrossProduct( seg[1], seg[0], normal );
        VectorNormalize( normal );
        float dist = DotProduct( v[0], normal );

		m_Nodes[nodeIndex].SetTriPlane( i, normal, dist );
    }
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcRayBoundingBoxes( int nodeIndex, int indices[8][3] )
{
	Vector triMin, triMax;

	for( int i = 0; i < 4; i++ )
	{
		triMin[0] = triMax[0] = m_pVerts[indices[(i*2)][0]].m_Vert[0];
		triMin[1] = triMax[1] = m_pVerts[indices[(i*2)][0]].m_Vert[1];
		triMin[2] = triMax[2] = m_pVerts[indices[(i*2)][0]].m_Vert[2];

		for( int j = 0; j < 3; j++ )
		{
			//
			// minimum
			//
			if( triMin[0] > m_pVerts[indices[(i*2)][j]].m_Vert[0] )
				triMin[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0];
			if( triMin[0] > m_pVerts[indices[(i*2+1)][j]].m_Vert[0] )
				triMin[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0];
			
			if( triMin[1] > m_pVerts[indices[(i*2)][j]].m_Vert[1] )
				triMin[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1];
			if( triMin[1] > m_pVerts[indices[(i*2+1)][j]].m_Vert[1] )
				triMin[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1];

			if( triMin[2] > m_pVerts[indices[(i*2)][j]].m_Vert[2] )
				triMin[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2];
			if( triMin[2] > m_pVerts[indices[(i*2+1)][j]].m_Vert[2] )
				triMin[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2];

			//
			// maximum
			//
			if( triMax[0] < m_pVerts[indices[(i*2)][j]].m_Vert[0] )
				triMax[0] = m_pVerts[indices[(i*2)][j]].m_Vert[0];
			if( triMax[0] < m_pVerts[indices[(i*2+1)][j]].m_Vert[0] )
				triMax[0] = m_pVerts[indices[(i*2+1)][j]].m_Vert[0];
			
			if( triMax[1] < m_pVerts[indices[(i*2)][j]].m_Vert[1] )
				triMax[1] = m_pVerts[indices[(i*2)][j]].m_Vert[1];
			if( triMax[1] < m_pVerts[indices[(i*2+1)][j]].m_Vert[1] )
				triMax[1] = m_pVerts[indices[(i*2+1)][j]].m_Vert[1];

			if( triMax[2] < m_pVerts[indices[(i*2)][j]].m_Vert[2] )
				triMax[2] = m_pVerts[indices[(i*2)][j]].m_Vert[2];
			if( triMax[2] < m_pVerts[indices[(i*2+1)][j]].m_Vert[2] )
				triMax[2] = m_pVerts[indices[(i*2+1)][j]].m_Vert[2];
		}

		m_Nodes[nodeIndex].SetRayBoundingBox( i, triMin, triMax );
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcTriSurfBoundingBoxes( int nodeIndex, int indices[8][3] )
{
	Vector triMin, triMax;

    for( int i = 0; i < 8; i++ )
    {
		m_Nodes[nodeIndex].GetTriBoundingBox( i, triMin, triMax );

        for( int j = 0; j < 3; j++ )
        {
            //
            // minimum
            //
            if( triMin[0] > m_pVerts[indices[i][j]].m_Vert[0] )
                triMin[0] = m_pVerts[indices[i][j]].m_Vert[0];

            if( triMin[1] > m_pVerts[indices[i][j]].m_Vert[1] )
                triMin[1] = m_pVerts[indices[i][j]].m_Vert[1];

            if( triMin[2] > m_pVerts[indices[i][j]].m_Vert[2] )
                triMin[2] = m_pVerts[indices[i][j]].m_Vert[2];

            //
            // maximum
            //
            if( triMax[0] < m_pVerts[indices[i][j]].m_Vert[0] )
                triMax[0] = m_pVerts[indices[i][j]].m_Vert[0];

            if( triMax[1] < m_pVerts[indices[i][j]].m_Vert[1] )
                triMax[1] = m_pVerts[indices[i][j]].m_Vert[1];

            if( triMax[2] < m_pVerts[indices[i][j]].m_Vert[2] )
                triMax[2] = m_pVerts[indices[i][j]].m_Vert[2];
        }

		m_Nodes[nodeIndex].SetTriBoundingBox( i, triMin, triMax );
    }
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcTriSurfIndices( int nodeIndex, int indices[8][3] )
{
    indices[0][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 4 );
    indices[0][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 );
    indices[0][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 );

    indices[1][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 );
    indices[1][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 );
    indices[1][2] = m_Nodes[nodeIndex].GetCenterVertIndex();

    indices[2][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 3 );
    indices[2][1] = m_Nodes[nodeIndex].GetCenterVertIndex();
    indices[2][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 );

    indices[3][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 5 );
    indices[3][1] = m_Nodes[nodeIndex].GetCenterVertIndex();
    indices[3][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 );

    indices[4][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 0 );
    indices[4][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 );
    indices[4][2] = m_Nodes[nodeIndex].GetCenterVertIndex();

    indices[5][0] = m_Nodes[nodeIndex].GetCenterVertIndex();
    indices[5][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 6 );
    indices[5][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 );

    indices[6][0] = m_Nodes[nodeIndex].GetCenterVertIndex();
    indices[6][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 );
    indices[6][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 );

    indices[7][0] = m_Nodes[nodeIndex].GetNeighborVertIndex( 2 );
    indices[7][1] = m_Nodes[nodeIndex].GetNeighborVertIndex( 1 );
    indices[7][2] = m_Nodes[nodeIndex].GetNeighborVertIndex( 7 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcTriSurfInfoAtNode( int nodeIndex )
{
    int indices[8][3];

	CalcTriSurfIndices( nodeIndex, indices );
	CalcTriSurfBoundingBoxes( nodeIndex, indices );
	CalcRayBoundingBoxes( nodeIndex, indices );
	CalcTriSurfPlanes( nodeIndex, indices );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcMinMaxBoundingBoxAtNode( int nodeIndex, Vector& bMin, Vector& bMax )
{
    // get the child node index
    int childNodeIndex = GetNodeChild( m_Power, nodeIndex, 4 );

    // get initial bounding box values
	m_Nodes[childNodeIndex].GetBoundingBox( bMin, bMax );

	Vector nodeMin, nodeMax;
    for( int i = 1, j = 5; i < 4; i++, j++ )
    {
		//
        // get the child node bounding box
		//
        childNodeIndex = GetNodeChild( m_Power, nodeIndex, j );
		m_Nodes[childNodeIndex].GetBoundingBox( nodeMin, nodeMax );

        // minimum
        if( bMin[0] > nodeMin[0] )
            bMin[0] = nodeMin[0];

        if( bMin[1] > nodeMin[1] )
            bMin[1] = nodeMin[1];

        if( bMin[2] > nodeMin[2] )
            bMin[2] = nodeMin[2];

        // maximum
        if( bMax[0] < nodeMax[0] )
            bMax[0] = nodeMax[0];

        if( bMax[1] < nodeMax[1] )
            bMax[1] = nodeMax[1];

        if( bMax[2] < nodeMax[2] )
            bMax[2] = nodeMax[2];
    }
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcBoundingBoxAtNode( int nodeIndex )
{
    Vector bMin, bMax;

    //
    // initialize the minimum and maximum values for the bounding box
    //
    int level = GetNodeLevel( nodeIndex );

	int vertIndex = m_Nodes[nodeIndex].GetCenterVertIndex();
    if( level == m_Power )
    {
        VectorCopy( m_pVerts[vertIndex].m_Vert, bMin );
        VectorCopy( m_pVerts[vertIndex].m_Vert, bMax );
    }
    else
    {
		CalcMinMaxBoundingBoxAtNode( nodeIndex, bMin, bMax );

        if( bMin[0] > m_pVerts[vertIndex].m_Vert[0] )
            bMin[0] = m_pVerts[vertIndex].m_Vert[0];

        if( bMin[1] > m_pVerts[vertIndex].m_Vert[1] )
            bMin[1] = m_pVerts[vertIndex].m_Vert[1];

        if( bMin[2] > m_pVerts[vertIndex].m_Vert[2] )
            bMin[2] = m_pVerts[vertIndex].m_Vert[2];


        if( bMax[0] < m_pVerts[vertIndex].m_Vert[0] )
            bMax[0] = m_pVerts[vertIndex].m_Vert[0];

        if( bMax[1] < m_pVerts[vertIndex].m_Vert[1] )
            bMax[1] = m_pVerts[vertIndex].m_Vert[1];

        if( bMax[2] < m_pVerts[vertIndex].m_Vert[2] )
            bMax[2] = m_pVerts[vertIndex].m_Vert[2];
    }

    for( int i = 0; i < 8; i++ )
    {
		int neighborVertIndex = m_Nodes[nodeIndex].GetNeighborVertIndex( i );

        //
        // minimum
        //
        if( bMin[0] > m_pVerts[neighborVertIndex].m_Vert[0] )
            bMin[0] = m_pVerts[neighborVertIndex].m_Vert[0];

        if( bMin[1] > m_pVerts[neighborVertIndex].m_Vert[1] )
            bMin[1] = m_pVerts[neighborVertIndex].m_Vert[1];

        if( bMin[2] > m_pVerts[neighborVertIndex].m_Vert[2] )
            bMin[2] = m_pVerts[neighborVertIndex].m_Vert[2];

        //
        // maximum
        //
        if( bMax[0] < m_pVerts[neighborVertIndex].m_Vert[0] )
            bMax[0] = m_pVerts[neighborVertIndex].m_Vert[0];

        if( bMax[1] < m_pVerts[neighborVertIndex].m_Vert[1] )
            bMax[1] = m_pVerts[neighborVertIndex].m_Vert[1];

        if( bMax[2] < m_pVerts[neighborVertIndex].m_Vert[2] )
            bMax[2] = m_pVerts[neighborVertIndex].m_Vert[2];
    }

	m_Nodes[nodeIndex].SetBoundingBox( bMin, bMax );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
float CCoreDispInfo::GetMaxErrorFromChildren( int nodeIndex, int level )
{
	//
	// check for children nodes
	//
	if( level == m_Power )
		return 0.0f;

	//
	// get the child's error term and save the greatest error -- SW, SE, NW, NE
	//
	float errorTerm = 0.0f;
	for( int i = 4; i < 8; i++ )
	{
		int childIndex = GetNodeChild( m_Power, nodeIndex, i );

		float nodeErrorTerm = m_Nodes[childIndex].GetErrorTerm();
		if( errorTerm < nodeErrorTerm )
		{
			errorTerm = nodeErrorTerm;
		}
	}

	return errorTerm;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcErrorTermAtNode( int nodeIndex, int level )
{
    if( level == m_Power )
        return;

	//
	// get the vertex indices
	//
	int neighborVertIndices[9];
	for( int i = 0; i < 8; i++ )
	{
		neighborVertIndices[i] = m_Nodes[nodeIndex].GetNeighborVertIndex( i );
	}
	neighborVertIndices[8] = m_Nodes[nodeIndex].GetCenterVertIndex();


	//
	// calculate the error terms
	//
    Vector          segment;
    Vector          v;

    VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v );
    VectorScale( v, 0.5f, v );
    VectorSubtract( m_pVerts[neighborVertIndices[0]].m_Vert, v, segment );
    float errorTerm = ( float )VectorLength( segment );

    VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v );
    VectorScale( v, 0.5f, v );
    VectorSubtract( m_pVerts[neighborVertIndices[1]].m_Vert, v, segment );
    if( errorTerm < ( float )VectorLength( segment ) )
        errorTerm = ( float )VectorLength( segment );

    VectorAdd( m_pVerts[neighborVertIndices[6]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v );
    VectorScale( v, 0.5f, v );
    VectorSubtract( m_pVerts[neighborVertIndices[2]].m_Vert, v, segment );
    if( errorTerm < ( float )VectorLength( segment ) )
        errorTerm = ( float )VectorLength( segment );

    VectorAdd( m_pVerts[neighborVertIndices[7]].m_Vert, m_pVerts[neighborVertIndices[4]].m_Vert, v );
    VectorScale( v, 0.5f, v );
    VectorSubtract( m_pVerts[neighborVertIndices[3]].m_Vert, v, segment );
    if( errorTerm < ( float )VectorLength( segment ) )
        errorTerm = ( float )VectorLength( segment );

    VectorAdd( m_pVerts[neighborVertIndices[4]].m_Vert, m_pVerts[neighborVertIndices[6]].m_Vert, v );
    VectorScale( v, 0.5f, v );
    VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment );
    if( errorTerm < ( float )VectorLength( segment ) )
        errorTerm = ( float )VectorLength( segment );

    VectorAdd( m_pVerts[neighborVertIndices[5]].m_Vert, m_pVerts[neighborVertIndices[7]].m_Vert, v );
    VectorScale( v, 0.5f, v );
    VectorSubtract( m_pVerts[neighborVertIndices[8]].m_Vert, v, segment );
    if( errorTerm < ( float )VectorLength( segment ) )
        errorTerm = ( float )VectorLength( segment );

	//
	// add the max child's error term
	//
	errorTerm += GetMaxErrorFromChildren( nodeIndex, level );

	// set the error term
	m_Nodes[nodeIndex].SetErrorTerm( errorTerm );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcNeighborVertIndicesAtNode( int nodeIndex, int level )
{
	// calculate the shift in direction in the matrix
	int shift = ( 1 << ( m_Power - level ) );
	
	// calculate the width, height of the displacement surface (are uniform)
	int extent = ( ( 1 << m_Power ) + 1 );

	//
	// get the neighbor vertex indices (defining the surface at the node level)
	//
	for( int direction = 0; direction < 8; direction++ )
	{
		//
		// get the parent vertex index in component form
		//
		int posX = m_Nodes[nodeIndex].GetCenterVertIndex() % extent;
		int posY = m_Nodes[nodeIndex].GetCenterVertIndex() / extent;

		//
		// calculate the neighboring vertex indices for surface rendering
		//
		bool bError = false;
		switch( direction )
		{
		case WEST: { posX -= shift; break; }
		case NORTH: { posY += shift; break; }
		case EAST: { posX += shift; break; }
		case SOUTH: { posY -= shift; break; }
		case SOUTHWEST: { posX -= shift; posY -= shift; break; }
		case SOUTHEAST: { posX += shift; posY -= shift; break; }
		case NORTHWEST: { posX -= shift; posY += shift; break; }
		case NORTHEAST: { posX += shift; posY += shift; break; }
		default: { bError = true; break; }
		}
	
		if( bError )
		{
			m_Nodes[nodeIndex].SetNeighborVertIndex( direction, -99999 );
		}
		else
		{
			m_Nodes[nodeIndex].SetNeighborVertIndex( direction, ( ( posY * extent ) + posX ) );
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcNodeInfo( int nodeIndex, int terminationLevel )
{
	// get the level of the current node
	int level = GetNodeLevel( nodeIndex );

	//
	// get the node data at the termination level
	//
    if( level == terminationLevel )
	{	
		// get the neighbor vertex indices (used to create surface at node level)
		CalcNeighborVertIndicesAtNode( nodeIndex, level );

		// get the neighbor node indices
		//CalcNeighborNodeIndicesAtNode( nodeIndex, level );

		// calculate the error term at the node
		CalcErrorTermAtNode( nodeIndex, level );

		// calcluate the axial-aligned bounding box at the node
		CalcBoundingBoxAtNode( nodeIndex );

		// calculate the triangular surface info at the node
		CalcTriSurfInfoAtNode( nodeIndex );

		return;
	}

	//
	// continue recursion (down to nodes "children")
	//
    for( int i = 4; i < 8; i++ )
    {
		int childIndex = GetNodeChild( m_Power, nodeIndex, i );
		CalcNodeInfo( childIndex, terminationLevel );
    }	
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CCoreDispInfo::GetNodeVertIndexFromParentIndex( int level, int parentVertIndex, int direction )
{
	// calculate the "shift"
	int shift = ( 1 << ( m_Power - ( level + 1 ) ) );

	// calculate the width and height of displacement (is uniform)
	int extent = ( ( 1 << m_Power ) + 1 );

	// get the parent vertex index in component form
	int posX = parentVertIndex % extent;
	int posY = parentVertIndex / extent;

    //
    // calculate the child index based on the parent index and child
    // direction
    //
    switch( direction )
    {
    case SOUTHWEST: { posX -= shift; posY -= shift; break; }
    case SOUTHEAST: { posX += shift; posY -= shift; break; }
    case NORTHWEST: { posX -= shift; posY += shift; break; }
    case NORTHEAST: { posX += shift; posY += shift; break; }
    default: return -99999;
    }

    // return the child vertex index
    return ( ( posY * extent ) + posX );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcVertIndicesAtNodes( int nodeIndex )
{
	//
	// check for recursion termination ( node level = power )
	//
	int level = GetNodeLevel( nodeIndex );
	if( level == m_Power )
		return;

	//
	// get the children indices - SW, SE, NW, NE
	//
	int childIndices[4];
	int i, j;
	for( i = 0, j = 4; i < 4; i++, j++ )
	{
		childIndices[i] = GetNodeChild( m_Power, nodeIndex, j );
		int centerIndex = GetNodeVertIndexFromParentIndex( level, m_Nodes[nodeIndex].GetCenterVertIndex(), j );
		m_Nodes[childIndices[i]].SetCenterVertIndex( centerIndex );
	}

	//
	// calculate the children's node vertex indices
	//
	for( i = 0; i < 4; i++ )
	{
		CalcVertIndicesAtNodes( childIndices[i] );
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::GenerateLODTree( void )
{
	//
	// calculate the displacement surface's vertex index at each quad-tree node
	// centroid
	//
	int size = GetSize();
	int initialIndex = ( ( size - 1 ) >> 1 );
	m_Nodes[0].SetCenterVertIndex( initialIndex );
	CalcVertIndicesAtNodes( 0 );

    //
    // calculate the error terms, bounding boxes, and neighboring vertex indices
    // at each node
    //
    for( int i = m_Power; i > 0; i-- )
    {
		CalcNodeInfo( 0, i );
    }
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcDispSurfCoords( bool bLightMap, int lightmapID )
{
	//
	// get base surface texture coords
	//
	Vector2D texCoords[4];
	Vector2D luxelCoords[4];
	CCoreDispSurface *pSurf = GetSurface();

	int i;
	for( i = 0; i < 4; i++ )
	{
		pSurf->GetTexCoord( i, texCoords[i] );
		pSurf->GetLuxelCoord( lightmapID, i, luxelCoords[i] );
	}

	//
	// get images width and intervals along the edge
	//
    int postSpacing = GetPostSpacing();
    float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) );

    //
    // calculate the parallel edge intervals
    //
	Vector2D edgeInt[2];
    if( !bLightMap )
    {
        Vector2DSubtract( texCoords[1], texCoords[0], edgeInt[0] );
        Vector2DSubtract( texCoords[2], texCoords[3], edgeInt[1] );
    }
    else
    {
        Vector2DSubtract( luxelCoords[1], luxelCoords[0], edgeInt[0] );
        Vector2DSubtract( luxelCoords[2], luxelCoords[3], edgeInt[1] );
    }
    Vector2DMultiply( edgeInt[0], ooInt, edgeInt[0] );
    Vector2DMultiply( edgeInt[1], ooInt, edgeInt[1] );

    //
    // calculate the displacement points
    //    
	for( i = 0; i < postSpacing; i++ )
	{
        //
        // position along parallel edges (start and end for a perpendicular segment)
        //
		Vector2D endPts[2];
        Vector2DMultiply( edgeInt[0], ( float )i, endPts[0] );
        Vector2DMultiply( edgeInt[1], ( float )i, endPts[1] );
        if( !bLightMap )
        {
            Vector2DAdd( endPts[0], texCoords[0], endPts[0] );
            Vector2DAdd( endPts[1], texCoords[3], endPts[1] );
        }
        else
        {
            Vector2DAdd( endPts[0], luxelCoords[0], endPts[0] );
            Vector2DAdd( endPts[1], luxelCoords[3], endPts[1] );
        }
        
        //
        // interval length for perpendicular edge
        //
		Vector2D seg, segInt;
        Vector2DSubtract( endPts[1], endPts[0], seg );
        Vector2DMultiply( seg, ooInt, segInt );

        //
		// calculate the material (texture or light) coordinate at each point
        //
        for( int j = 0; j < postSpacing; j++ )
        {
            Vector2DMultiply( segInt, ( float )j, seg );

            if( !bLightMap )
            {
                Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_TexCoord );
            }
            else
            {
                Vector2DAdd( endPts[0], seg, m_pVerts[i*postSpacing+j].m_LuxelCoords[lightmapID] );
            }
        }
	}
}

#if 0
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcDispSurfAlphas( void )
{
	//
	// get images width and intervals along the edge
	//
    int postSpacing = GetPostSpacing();
    float ooInt = ( 1.0f / ( float )( postSpacing - 1 ) );

    //
    // calculate the parallel edge intervals
    //
	float edgeInt[2];
	edgeInt[0] = m_Surf.m_Alpha[1] - m_Surf.m_Alpha[0];
	edgeInt[1] = m_Surf.m_Alpha[2] - m_Surf.m_Alpha[3];
	edgeInt[0] *= ooInt;
	edgeInt[1] *= ooInt;

    //
    // calculate the displacement points
    //    
	for( int i = 0; i < postSpacing; i++ )
	{
        //
        // position along parallel edges (start and end for a perpendicular segment)
        //
		float endValues[2];

		endValues[0] = edgeInt[0] * ( float )i;
		endValues[1] = edgeInt[1] * ( float )i;
		endValues[0] += m_Surf.m_Alpha[0];
		endValues[1] += m_Surf.m_Alpha[3];
		
        //
        // interval length for perpendicular edge
        //
		float seg, segInt;
		seg = endValues[1] - endValues[0];
		segInt = seg * ooInt;

        //
		// calculate the alpha value at each point
        //
		for( int j = 0; j < postSpacing; j++ )
		{
			seg = segInt * ( float )j;
			m_Alphas[i*postSpacing+j] = endValues[0] + seg;
		}
	}
}
#endif

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::GenerateDispSurfTangentSpaces( void )
{
	//
	// get texture axes from base surface
	//
	CCoreDispSurface *pSurf = GetSurface();
	Vector sAxis, tAxis;
	pSurf->GetSAxis( sAxis );
	pSurf->GetTAxis( tAxis );

	//
	// calculate the tangent spaces
	//
	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		//
		// create the axes - normals, tangents, and binormals
		//
		VectorCopy( tAxis, m_pVerts[i].m_TangentT );
		VectorNormalize( m_pVerts[i].m_TangentT );
		CrossProduct( m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT, m_pVerts[i].m_TangentS );
		VectorNormalize( m_pVerts[i].m_TangentS );
		CrossProduct( m_pVerts[i].m_TangentS, m_pVerts[i].m_Normal, m_pVerts[i].m_TangentT );
		VectorNormalize( m_pVerts[i].m_TangentT );

		Vector tmpVect;
		Vector planeNormal;
		pSurf->GetNormal( planeNormal );
		CrossProduct( sAxis, tAxis, tmpVect );
		if( DotProduct( planeNormal, tmpVect ) > 0.0f )
		{
			VectorScale( m_pVerts[i].m_TangentS, -1.0f, m_pVerts[i].m_TangentS );
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::CalcNormalFromEdges( int indexRow, int indexCol, bool bIsEdge[4],
										 Vector& normal )
{
	// get the post spacing (size/interval of displacement surface)
	int postSpacing = ( ( 1 << m_Power ) + 1 );

	// initialize the normal accumulator - counter
	Vector accumNormal;
	int	   normalCount = 0;

	VectorClear( accumNormal );

	Vector tmpVect[2];
	Vector tmpNormal;

	//
	// check quadrant I (posX, posY)
	//
	if( bIsEdge[1] && bIsEdge[2] )
	{
		// tri i
		VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] );
		VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] );
		CrossProduct( tmpVect[1], tmpVect[0], tmpNormal );
		VectorNormalize( tmpNormal );
		VectorAdd( accumNormal, tmpNormal, accumNormal );
		normalCount++;

		// tri 2
		VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] );
		VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] );
		CrossProduct( tmpVect[1], tmpVect[0], tmpNormal );
		VectorNormalize( tmpNormal );
		VectorAdd( accumNormal, tmpNormal, accumNormal );
		normalCount++;
	}

	//
	// check quadrant II (negX, posY)
	//
	if( bIsEdge[0] && bIsEdge[1] )
	{
		// tri i
		VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] );
		VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] );
		CrossProduct( tmpVect[1], tmpVect[0], tmpNormal );
		VectorNormalize( tmpNormal );
		VectorAdd( accumNormal, tmpNormal, accumNormal );
		normalCount++;

		// tri 2
		VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+(indexRow-1)].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[0] );
		VectorSubtract( m_pVerts[(indexCol+1)*postSpacing+indexRow].m_Vert, m_pVerts[indexCol*postSpacing+indexRow].m_Vert, tmpVect[1] );
		CrossProduct( tmpVect[1], tmpVect[0], tmpNormal );
		VectorNormalize( tmpNormal );
		VectorAdd( accumNormal, tmpNormal, accumNormal );
		normalCount++;
	}

	//
	// check quadrant III (negX, negY)
	//
	if( bIsEdge[0] && bIsEdge[3] )
	{
		// tri i
		VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[0] );
		VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow-1)].m_Vert, tmpVect[1] );
		CrossProduct( tmpVect[1], tmpVect[0], tmpNormal );
		VectorNormalize( tmpNormal );
		VectorAdd( accumNormal, tmpNormal, accumNormal );
		normalCount++;

		// tri 2
		VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow-1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] );
		VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] );
		CrossProduct( tmpVect[1], tmpVect[0], tmpNormal );
		VectorNormalize( tmpNormal );
		VectorAdd( accumNormal, tmpNormal, accumNormal );
		normalCount++;
	}

	//
	// check quadrant IV (posX, negY)
	//
	if( bIsEdge[2] && bIsEdge[3] )
	{
		// tri i
		VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[0] );
		VectorSubtract( m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+indexRow].m_Vert, tmpVect[1] );
		CrossProduct( tmpVect[1], tmpVect[0], tmpNormal );
		VectorNormalize( tmpNormal );
		VectorAdd( accumNormal, tmpNormal, accumNormal );
		normalCount++;

		// tri 2
		VectorSubtract( m_pVerts[indexCol*postSpacing+indexRow].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[0] );
		VectorSubtract( m_pVerts[indexCol*postSpacing+(indexRow+1)].m_Vert, m_pVerts[(indexCol-1)*postSpacing+(indexRow+1)].m_Vert, tmpVect[1] );
		CrossProduct( tmpVect[1], tmpVect[0], tmpNormal );
		VectorNormalize( tmpNormal );
		VectorAdd( accumNormal, tmpNormal, accumNormal );
		normalCount++;
	}

	VectorScale( accumNormal, ( 1.0f / ( float )normalCount ), normal );
}


//-----------------------------------------------------------------------------
// Purpose: This function determines if edges exist in each of the directions
//          off of the given point (given in component form).  We know ahead of
//          time that there are only 4 possibilities.
//
//            1     "directions"
//          0 + 2
//            3
//
//   Input: indexRow - row position
//          indexCol - col position
//          direction - the direction (edge) currently being evaluated
//          postSpacing - the number of intervals in the row and col directions
//  Output: the edge existed? (true/false)
//-----------------------------------------------------------------------------
bool CCoreDispInfo::DoesEdgeExist( int indexRow, int indexCol, int direction, int postSpacing )
{
    switch( direction )
    {
    case 0:
        // left edge
        if( ( indexRow - 1 ) < 0 )
            return false;
        return true;
    case 1:
        // top edge
        if( ( indexCol + 1 ) > ( postSpacing - 1 ) )
            return false;
        return true;
    case 2:
        // right edge
        if( ( indexRow + 1 ) > ( postSpacing - 1 ) )
            return false;
        return true;
    case 3:
        // bottom edge
        if( ( indexCol - 1 ) < 0 )
            return false;
        return true;
    default:
        return false;
    }
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::GenerateDispSurfNormals( void )
{
	// get the post spacing (size/interval of displacement surface)
	int postSpacing = GetPostSpacing();

	//
	// generate the normals at each displacement surface vertex
	//
	for( int i = 0; i < postSpacing; i++ )
	{
		for( int j = 0; j < postSpacing; j++ )
		{
			bool bIsEdge[4];

			// edges
			for( int k = 0; k < 4; k++ )
			{
				bIsEdge[k] = DoesEdgeExist( j, i, k, postSpacing );
			}

			Vector normal;
			CalcNormalFromEdges( j, i, bIsEdge, normal );

			// save generated normal
			VectorCopy( normal, m_pVerts[i*postSpacing+j].m_Normal );
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::GenerateDispSurf( void )
{
	int i;
	CCoreDispSurface *pSurf = GetSurface();
	Vector points[4];
	for( i = 0; i < 4; i++ )
	{
		pSurf->GetPoint( i, points[i] );
	}

    //
    // get the spacing (interval = width/height, are equal because it is uniform) along the edge
    //
	int postSpacing = GetPostSpacing();
	float ooInt = 1.0f / ( float )( postSpacing - 1 );

	//
	// calculate the opposite edge intervals
	//
	Vector edgeInt[2];
    VectorSubtract( points[1], points[0], edgeInt[0] );
    VectorScale( edgeInt[0], ooInt, edgeInt[0] );
    VectorSubtract( points[2], points[3], edgeInt[1] );
    VectorScale( edgeInt[1], ooInt, edgeInt[1] );

	Vector elevNormal;
	elevNormal.Init();
	if( m_Elevation != 0.0f )
	{
		pSurf->GetNormal( elevNormal );
		VectorScale( elevNormal, m_Elevation, elevNormal );
	}

	//
	// calculate the displaced vertices
	//
	for( i = 0; i < postSpacing; i++ )
	{
		//
		// calculate segment interval between opposite edges
		//
		Vector endPts[2];
        VectorScale( edgeInt[0], ( float )i, endPts[0] );
        VectorAdd( endPts[0], points[0], endPts[0] );
        VectorScale( edgeInt[1], ( float )i, endPts[1] );
        VectorAdd( endPts[1], points[3], endPts[1] );

		Vector seg, segInt;
        VectorSubtract( endPts[1], endPts[0], seg );
        VectorScale( seg, ooInt, segInt );

		//
		// calculate the surface vertices
		//
		for( int j = 0; j < postSpacing; j++ )
		{	
			int ndx = i * postSpacing + j;

			CoreDispVert_t *pVert = &m_pVerts[ndx];

			// calculate the flat surface position -- saved separately
			pVert->m_FlatVert = endPts[0] + ( segInt * ( float )j );

			// start with the base surface position
			pVert->m_Vert = pVert->m_FlatVert;

			// add the elevation vector -- if it exists
			if( m_Elevation != 0.0f )
			{
				pVert->m_Vert += elevNormal;
			}

			// add the subdivision surface position
			pVert->m_Vert += pVert->m_SubdivPos;

			// add the displacement field direction(normalized) and distance
			pVert->m_Vert += pVert->m_FieldVector * pVert->m_FieldDistance;
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//bool CCoreDispInfo::Create( int creationFlags )
bool CCoreDispInfo::Create( void )
{
	// sanity check
	CCoreDispSurface *pSurf = GetSurface();
	if( pSurf->GetPointCount() != 4 )
		return false;
	
	// generate the displacement surface
	GenerateDispSurf();
	
	GenerateDispSurfNormals();
	
	GenerateDispSurfTangentSpaces();
	
	CalcDispSurfCoords( false, 0 );
	
	for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ )
	{
		CalcDispSurfCoords( true, bumpID );
	}
	
	GenerateLODTree();
	
	GenerateCollisionData();
	
	CreateTris();

    return true;
}

//-----------------------------------------------------------------------------
// Purpose: Create a displacement surface without generating the LOD for it.
//-----------------------------------------------------------------------------
bool CCoreDispInfo::CreateWithoutLOD( void )
{
	// sanity check
	CCoreDispSurface *pSurf = GetSurface();
	if( pSurf->GetPointCount() != 4 )
		return false;
	
	GenerateDispSurf();
	
	GenerateDispSurfNormals();
	
	GenerateDispSurfTangentSpaces();
	
	CalcDispSurfCoords( false, 0 );
	
	for( int bumpID = 0; bumpID < ( NUM_BUMP_VECTS + 1 ); bumpID++ )
	{
		CalcDispSurfCoords( true, bumpID );
	}
	GenerateCollisionData();

	CreateTris();
	
    return true;
}



//-----------------------------------------------------------------------------
// Purpose: This function calculates the neighbor node index given the base
//          node and direction of the neighbor node in the tree.
//   Input: power - the size in one dimension of the displacement map (2^power + 1 )
//          index - the "base" node index
//          direction - the direction of the neighbor { W = 1, N = 2, E = 3, S = 4 }
//  Output: returns the index of the neighbor node
//-----------------------------------------------------------------------------
int GetNodeNeighborNode( int power, int index, int direction, int level )
{
    // adjust the index to range [0...?]
	int minNodeIndex = GetNodeMinNodeAtLevel( level );

    // get node extent (uniform: height = width)
    int nodeExtent = ( 1 << ( level - 1 ) );

	//
	// get node's component positions in quad-tree
	//
	int posX, posY;
	GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY );

	//
	// find the neighbor in the "direction"
	//
    switch( direction )
    {
    case CCoreDispInfo::WEST:
        {
            if( ( posX - 1 ) < 0 )
            {
                return -( CCoreDispInfo::WEST + 1 );
            }
            else
            {
                return ( GetNodeIndexFromComponents( ( posX - 1 ), posY ) + minNodeIndex );
            }
        }
    case CCoreDispInfo::NORTH:
        {
            if( ( posY + 1 ) == nodeExtent )
            {
                return -( CCoreDispInfo::NORTH + 1 );
            }
            else
            {
                return ( GetNodeIndexFromComponents( posX, ( posY + 1 ) ) + minNodeIndex );
            }
        }
    case CCoreDispInfo::EAST:
        {
            if( ( posX + 1 ) == nodeExtent )
            {
                return -( CCoreDispInfo::EAST + 1 );
            }
            else
            {
                return ( GetNodeIndexFromComponents( ( posX + 1 ), posY ) + minNodeIndex );
            }
        }
    case CCoreDispInfo::SOUTH: 
        {
            if( ( posY - 1 ) < 0 )
            {
                return -( CCoreDispInfo::SOUTH + 1 );
            }
            else
            {
                return ( GetNodeIndexFromComponents( posX, ( posY - 1 ) ) + minNodeIndex );
            }
        }
    default:
        {
            return -99999;
        }
    }
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int GetNodeNeighborNodeFromNeighborSurf( int power, int index, int direction, int level, int neighborOrient )
{
    // adjust the index to range [0...?]
	int minNodeIndex = GetNodeMinNodeAtLevel( level );

    // get node extent (uniform: height = width)
    int nodeExtent = ( 1 << ( level - 1 ) );

	//
	// get node's component positions in quad-tree
	//
	int posX, posY;
	GetComponentsFromNodeIndex( ( index - minNodeIndex ), &posX, &posY );

    switch( direction )
    {
    case CCoreDispInfo::WEST:
        {
            switch( neighborOrient )
            {
            case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex );
            case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ) - posY, ( nodeExtent - 1 ) ) ) + minNodeIndex );
            case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), posY ) ) + minNodeIndex );
            case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex );
            default: return -99999;
            }
        }
    case CCoreDispInfo::NORTH:
        {
            switch( neighborOrient )
            {
            case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex );
            case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex );
            case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex );
            case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex );
            default: return -99999;
            }
        }
    case CCoreDispInfo::EAST:
        {
            switch( neighborOrient )
            {
            case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex );
            case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex );
            case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( posX, ( ( nodeExtent - 1 ) - posY ) ) ) + minNodeIndex );
            case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posY ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex );
            default: return -99999;
            }
        }
    case CCoreDispInfo::SOUTH:
        {
            switch( neighborOrient )
            {
            case CCoreDispInfo::WEST: return -( ( GetNodeIndexFromComponents( posY, posX ) ) + minNodeIndex );
            case CCoreDispInfo::NORTH: return -( ( GetNodeIndexFromComponents( posX, ( nodeExtent - 1 ) ) ) + minNodeIndex );
            case CCoreDispInfo::EAST: return -( ( GetNodeIndexFromComponents( ( nodeExtent - 1 ), ( ( nodeExtent - 1 ) - posX ) ) ) + minNodeIndex );
            case CCoreDispInfo::SOUTH: return -( ( GetNodeIndexFromComponents( ( ( nodeExtent - 1 ) - posX ), posY ) ) + minNodeIndex );
            default: return -99999;
            }
        }
    default:
        {
            return -99999;
        }
    }
}



// Turn the optimizer back on
#pragma optimize( "", on )

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::GetPositionOnSurface( float u, float v, Vector &vPos,
										  Vector *pNormal, float *pAlpha )
{
	Vector2D dispUV( u, v );
	DispUVToSurf( dispUV, vPos, pNormal, pAlpha );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::BaseFacePlaneToDispUV( Vector const &planePt, Vector2D &dispUV )
{
	// Get the base surface points.
	CCoreDispSurface *pSurf = GetSurface();
	Vector vecPoints[4];
	for( int iPoint = 0; iPoint < 4; ++iPoint )
	{
		pSurf->GetPoint( iPoint, vecPoints[iPoint] );
	}

	PointInQuadToBarycentric( vecPoints[0], vecPoints[3], vecPoints[2], vecPoints[1], planePt, dispUV );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::DispUVToSurf_TriTLToBR_1( const Vector &vecIntersectPoint,   
											  int nSnapU, int nNextU, int nSnapV, int nNextV, 
											  Vector &vecPoint, Vector *pNormal, float *pAlpha,
											  bool bBackup )
{
	int nWidth = GetWidth();

	int nIndices[3];
	nIndices[0] = nNextV * nWidth + nSnapU;
	nIndices[1] = nNextV * nWidth + nNextU;	
	nIndices[2] = nSnapV * nWidth + nNextU;

	Vector vecFlatVerts[3], vecVerts[3];
	float flAlphas[3];
	for ( int iVert = 0; iVert < 3; ++iVert )
	{
		vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert;
		vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert;
		flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha;
	}

	if ( nSnapU == nNextU )
	{
		if ( nSnapV == nNextV )
		{
			vecPoint = vecVerts[0];
			*pAlpha = flAlphas[0];
		}
		else
		{
			float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
			vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
			
			if ( pAlpha )
			{
				*pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) );
			}
		}

		if( pNormal )
		{
			Vector edgeU = vecVerts[0] - vecVerts[1];
			Vector edgeV = vecVerts[2] - vecVerts[1];
			*pNormal = CrossProduct( edgeU, edgeV );
			VectorNormalize( *pNormal );
		}			
	}
	else if ( nSnapV == nNextV )
	{
		if ( nSnapU == nNextU )
		{
			vecPoint = vecVerts[0];
			*pAlpha = flAlphas[0];
		}
		else
		{
			float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
			vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
			
			if ( pAlpha )
			{
				*pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) );
			}
		}

		if( pNormal )
		{
			Vector edgeU = vecVerts[0] - vecVerts[1];
			Vector edgeV = vecVerts[2] - vecVerts[1];
			*pNormal = CrossProduct( edgeU, edgeV );
			VectorNormalize( *pNormal );
		}			
	}
	else
	{
		float flCfs[3];
		if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) )
		{
			vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );

			if( pAlpha )
			{
				*pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] );
			}
			
			if( pNormal )
			{
				Vector edgeU = vecVerts[0] - vecVerts[1];
				Vector edgeV = vecVerts[2] - vecVerts[1];
				*pNormal = CrossProduct( edgeU, edgeV );
				VectorNormalize( *pNormal );
			}			
		}
		else
		{
			if ( !bBackup )
			{
				DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::DispUVToSurf_TriTLToBR_2( const Vector &vecIntersectPoint,   
											  int nSnapU, int nNextU, int nSnapV, int nNextV, 
											  Vector &vecPoint, Vector *pNormal, float *pAlpha,
											  bool bBackup )
{
	int nWidth = GetWidth();

	int nIndices[3];
	nIndices[0] = nSnapV * nWidth + nSnapU;
	nIndices[1] = nNextV * nWidth + nSnapU;
	nIndices[2] = nSnapV * nWidth + nNextU;

	Vector vecFlatVerts[3], vecVerts[3];
	float flAlphas[3];
	for ( int iVert = 0; iVert < 3; ++iVert )
	{
		vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert;
		vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert;
		flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha;
	}

	if ( nSnapU == nNextU )
	{
		if ( nSnapV == nNextV )
		{
			vecPoint = vecVerts[0];
			*pAlpha = flAlphas[0];
		}
		else
		{
			float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length();
			vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) );
			
			if ( pAlpha )
			{
				*pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) );
			}
		}

		if( pNormal )
		{
			Vector edgeU = vecVerts[2] - vecVerts[0];
			Vector edgeV = vecVerts[1] - vecVerts[0];
			*pNormal = CrossProduct( edgeU, edgeV );
			VectorNormalize( *pNormal );
		}			
	}
	else if ( nSnapV == nNextV )
	{
		if ( nSnapU == nNextU )
		{
			vecPoint = vecVerts[0];
			*pAlpha = flAlphas[0];
		}
		else
		{
			float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
			vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
			
			if ( pAlpha )
			{
				*pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) );
			}
		}

		if( pNormal )
		{
			Vector edgeU = vecVerts[2] - vecVerts[0];
			Vector edgeV = vecVerts[1] - vecVerts[0];
			*pNormal = CrossProduct( edgeU, edgeV );
			VectorNormalize( *pNormal );
		}			
	}
	else
	{
		float flCfs[3];
		if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) )
		{
			vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );

			if( pAlpha )
			{
				*pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] );
			}
	
			if( pNormal )
			{
				Vector edgeU = vecVerts[2] - vecVerts[0];
				Vector edgeV = vecVerts[1] - vecVerts[0];
				*pNormal = CrossProduct( edgeU, edgeV );
				VectorNormalize( *pNormal );
			}			
		}
		else
		{
			if ( !bBackup )
			{
				DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::DispUVToSurf_TriTLToBR( Vector &vecPoint, Vector *pNormal, float *pAlpha, 
											float flU, float flV, const Vector &vecIntersectPoint )
{
	const float TRIEDGE_EPSILON = 0.00001f;

	int nWidth = GetWidth();
	int nHeight = GetHeight();

	int nSnapU = static_cast<int>( flU );
	int nSnapV = static_cast<int>( flV );
	int nNextU = nSnapU + 1;
	int nNextV = nSnapV + 1;
	if ( nNextU == nWidth)	 { --nNextU; }
	if ( nNextV == nHeight ) { --nNextV; }

	float flFracU = flU - static_cast<float>( nSnapU );
	float flFracV = flV - static_cast<float>( nSnapV );

	if ( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) )
	{
		DispUVToSurf_TriTLToBR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false );
	}
	else
	{
		DispUVToSurf_TriTLToBR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false );
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::DispUVToSurf_TriBLToTR_1( const Vector &vecIntersectPoint,   
											  int nSnapU, int nNextU, int nSnapV, int nNextV, 
											  Vector &vecPoint, Vector *pNormal, float *pAlpha,
											  bool bBackup )
{
	int nWidth = GetWidth();

	int nIndices[3];
	nIndices[0] = nSnapV * nWidth + nSnapU;
	nIndices[1] = nNextV * nWidth + nSnapU;
	nIndices[2] = nNextV * nWidth + nNextU;

	Vector vecFlatVerts[3], vecVerts[3];
	float flAlphas[3];
	for ( int iVert = 0; iVert < 3; ++iVert )
	{
		vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert;
		vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert;
		flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha;
	}

	if ( nSnapU == nNextU )
	{
		if ( nSnapV == nNextV )
		{
			vecPoint = vecVerts[0];
			*pAlpha = flAlphas[0];
		}
		else
		{
			float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
			vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
			
			if ( pAlpha )
			{
				*pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) );
			}
		}

		if( pNormal )
		{
			Vector edgeU = vecVerts[2] - vecVerts[1];
			Vector edgeV = vecVerts[0] - vecVerts[1];
			*pNormal = CrossProduct( edgeU, edgeV );
			VectorNormalize( *pNormal );
		}			
	}
	else if ( nSnapV == nNextV )
	{
		if ( nSnapU == nNextU )
		{
			vecPoint = vecVerts[0];
			*pAlpha = flAlphas[0];
		}
		else
		{
			float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
			vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
			
			if ( pAlpha )
			{
				*pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) );
			}
		}

		if( pNormal )
		{
			Vector edgeU = vecVerts[2] - vecVerts[1];
			Vector edgeV = vecVerts[0] - vecVerts[1];
			*pNormal = CrossProduct( edgeV, edgeU );
			VectorNormalize( *pNormal );
		}			
	}
	else
	{
		float flCfs[3];
		if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) )
		{
			vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
			
			if( pAlpha )
			{
				*pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] );
			}

			if( pNormal )
			{
				Vector edgeU = vecVerts[2] - vecVerts[1];
				Vector edgeV = vecVerts[0] - vecVerts[1];
				*pNormal = CrossProduct( edgeV, edgeU );
				VectorNormalize( *pNormal );
			}
		}
		else
		{
			if ( !bBackup )
			{
				DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::DispUVToSurf_TriBLToTR_2( const Vector &vecIntersectPoint,   
											  int nSnapU, int nNextU, int nSnapV, int nNextV, 
											  Vector &vecPoint, Vector *pNormal, float *pAlpha,
											  bool bBackup )
{
	int nWidth = GetWidth();

	int nIndices[3];
	nIndices[0] = nSnapV * nWidth + nSnapU;
	nIndices[1] = nNextV * nWidth + nNextU;
	nIndices[2] = nSnapV * nWidth + nNextU;

	Vector vecFlatVerts[3], vecVerts[3];
	float flAlphas[3];
	for ( int iVert = 0; iVert < 3; ++iVert )
	{
		vecFlatVerts[iVert] = m_pVerts[nIndices[iVert]].m_FlatVert;
		vecVerts[iVert] = m_pVerts[nIndices[iVert]].m_Vert;
		flAlphas[iVert] = m_pVerts[nIndices[iVert]].m_Alpha;
	}

	if ( nSnapU == nNextU )
	{
		if ( nSnapV == nNextV )
		{
			vecPoint = vecVerts[0];
			*pAlpha = flAlphas[0];
		}
		else
		{
			float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length();
			vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) );
			
			if ( pAlpha )
			{
				*pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[1] - flAlphas[0] ) );
			}
		}

		if( pNormal )
		{
			Vector edgeU = vecVerts[0] - vecVerts[2];
			Vector edgeV = vecVerts[1] - vecVerts[2];
			*pNormal = CrossProduct( edgeV, edgeU );
			VectorNormalize( *pNormal );
		}			
	}
	else if ( nSnapV == nNextV )
	{
		if ( nSnapU == nNextU )
		{
			vecPoint = vecVerts[0];
			*pAlpha = flAlphas[0];
		}
		else
		{
			float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
			vecPoint = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );

			if ( pAlpha )
			{
				*pAlpha = flAlphas[0] + ( flFrac * ( flAlphas[2] - flAlphas[0] ) );
			}
		}

		if( pNormal )
		{
			Vector edgeU = vecVerts[0] - vecVerts[2];
			Vector edgeV = vecVerts[1] - vecVerts[2];
			*pNormal = CrossProduct( edgeV, edgeU );
			VectorNormalize( *pNormal );
		}			
	}
	else
	{
		float flCfs[3];
		if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) )
		{
			vecPoint = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
			
			if( pAlpha )
			{
				*pAlpha = ( flAlphas[0] * flCfs[0] ) + ( flAlphas[1] * flCfs[1] ) + ( flAlphas[2] * flCfs[2] );
			}

			if( pNormal )
			{
				Vector edgeU = vecVerts[0] - vecVerts[2];
				Vector edgeV = vecVerts[1] - vecVerts[2];
				*pNormal = CrossProduct( edgeV, edgeU );
				VectorNormalize( *pNormal );
			}			
		}
		else
		{
			if ( !bBackup )
			{
				DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, true );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::DispUVToSurf_TriBLToTR( Vector &vecPoint, Vector *pNormal, float *pAlpha, 
											float flU, float flV, const Vector &vecIntersectPoint )
{
	int nWidth = GetWidth();
	int nHeight = GetHeight();

	int nSnapU = static_cast<int>( flU );
	int nSnapV = static_cast<int>( flV );
	int nNextU = nSnapU + 1;
	int nNextV = nSnapV + 1;
	if ( nNextU == nWidth)	 { --nNextU; }
	if ( nNextV == nHeight ) { --nNextV; }

	float flFracU = flU - static_cast<float>( nSnapU );
	float flFracV = flV - static_cast<float>( nSnapV );

	if( flFracU < flFracV )
	{
		DispUVToSurf_TriBLToTR_1( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false );
	}
	else
	{
		DispUVToSurf_TriBLToTR_2( vecIntersectPoint, nSnapU, nNextU, nSnapV, nNextV, vecPoint, pNormal, pAlpha, false );
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::DispUVToSurf( Vector2D const &dispUV, Vector &vecPoint,
								  Vector *pNormal, float *pAlpha )
{
	// Check to see that the point is on the surface.
	if ( dispUV.x < 0.0f || dispUV.x > 1.0f || dispUV.y < 0.0f || dispUV.y > 1.0f )
		return;

	// Get the base surface points.
	Vector vecIntersectPoint;
	CCoreDispSurface *pSurf = GetSurface();
	PointInQuadFromBarycentric( pSurf->GetPoint( 0 ), pSurf->GetPoint( 3 ), pSurf->GetPoint( 2 ), pSurf->GetPoint( 1 ), dispUV, vecIntersectPoint );

	// Get the displacement power.
	int nWidth = GetWidth();
	int nHeight = GetHeight();
 
	// Scale the U, V coordinates to the displacement grid size.
	float flU = dispUV.x * ( static_cast<float>( nWidth ) - 1.000001f );
	float flV = dispUV.y * ( static_cast<float>( nHeight ) - 1.000001f );

	// Find the base U, V.
	int nSnapU = static_cast<int>( flU );
	int nSnapV = static_cast<int>( flV );

	// Use this to get the triangle orientation.
	bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 );

	// Top Left to Bottom Right
	if( bOdd )
	{
		DispUVToSurf_TriTLToBR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint );
	}
	// Bottom Left to Top Right
	else
	{
		DispUVToSurf_TriBLToTR( vecPoint, pNormal, pAlpha, flU, flV, vecIntersectPoint );
	}
}

//-----------------------------------------------------------------------------
// Purpose: Create bounding boxes around pairs of triangles (in a grid-like) 
//          fashion; used for culling
//-----------------------------------------------------------------------------
void CCoreDispInfo::CreateBoundingBoxes( CoreDispBBox_t *pBBox, int count )
{
	//
	// Initialize the bounding boxes.
	//
	int iBox;
	for( iBox = 0; iBox < count; ++iBox )
	{
		pBBox[iBox].vMin.Init( FLT_MAX, FLT_MAX, FLT_MAX );
		pBBox[iBox].vMax.Init( FLT_MIN, FLT_MIN, FLT_MIN );
	}

	// Get the width and height of the displacement surface.
	int nHeight = GetHeight();
	int nWidth = GetWidth();

	// Find bounding box of every two consecutive triangles
	iBox = 0;
	int nIndex = 0;
	for( int iHgt = 0; iHgt < ( nHeight - 1 ); ++iHgt )
	{
		for( int iWid = 0; iWid < ( nWidth - 1 ); ++iWid )
		{
			for( int iPoint = 0; iPoint < 4; ++iPoint )
			{
				switch( iPoint )
				{
				case 0: { nIndex = ( nHeight * iHgt ) + iWid; break; }
				case 1: { nIndex = ( nHeight * ( iHgt + 1 ) ) + iWid; break; }
				case 2: { nIndex = ( nHeight * ( iHgt + 1 ) ) + ( iWid + 1 ); break; }
				case 3: { nIndex = ( nHeight * iHgt ) + ( iWid + 1 ); break; }
				default: { break; }
				}

				Vector vecPoint;
				GetVert( nIndex, vecPoint );
				if( vecPoint[0] < pBBox[iBox].vMin[0] ) { pBBox[iBox].vMin[0] = vecPoint[0]; }
				if( vecPoint[1] < pBBox[iBox].vMin[1] ) { pBBox[iBox].vMin[1] = vecPoint[1]; }
				if( vecPoint[2] < pBBox[iBox].vMin[2] ) { pBBox[iBox].vMin[2] = vecPoint[2]; }
				
				if( vecPoint[0] > pBBox[iBox].vMax[0] ) { pBBox[iBox].vMax[0] = vecPoint[0]; }
				if( vecPoint[1] > pBBox[iBox].vMax[1] ) { pBBox[iBox].vMax[1] = vecPoint[1]; }
				if( vecPoint[2] > pBBox[iBox].vMax[2] ) { pBBox[iBox].vMax[2] = vecPoint[2]; }
			}

			iBox++;
		}
	}

	// Verify.
	Assert( iBox == count );

	// Bloat.
	for ( iBox = 0; iBox < count; ++iBox )
	{
		for( int iAxis = 0; iAxis < 3; ++iAxis )
		{
			pBBox[iBox].vMin[iAxis] -= 1.0f;
			pBBox[iBox].vMax[iAxis] += 1.0f;
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
inline bool PointInDispBBox( CoreDispBBox_t *pBox, const Vector &vecPoint )
{
	// Check to see if point lies in box
	if( ( vecPoint.x < pBox->vMin.x ) || ( vecPoint.x > pBox->vMax.x ) )
		return false;
	
	if( ( vecPoint.y < pBox->vMin.y ) || ( vecPoint.y > pBox->vMax.y ) )
		return false;
	
	if( ( vecPoint.z < pBox->vMin.z ) || ( vecPoint.z > pBox->vMax.z ) )
		return false;

	return true;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CCoreDispInfo::GetTriangleIndicesForDispBBox( int nIndex, int nTris[2][3] )
{
	// Test to see whether or not the index is odd.
	bool bOdd = ( ( nIndex % 2 ) == 1 );

	int nWidth = GetWidth();

	// Tris for TLtoBR
	if ( bOdd )
	{
		nTris[0][0] = nIndex;
		nTris[0][1] = nIndex + nWidth;
		nTris[0][2] = nIndex + 1;
	
		nTris[1][0] = nIndex + 1;
		nTris[1][1] = nIndex + nWidth;
		nTris[1][2] = nIndex + nWidth + 1;
	}
	// Tris for BLtoTR
	else
	{
		nTris[0][0] = nIndex;
		nTris[0][1] = nIndex + nWidth;
		nTris[0][2] = nIndex + nWidth + 1;
	
		nTris[1][0] = nIndex;
		nTris[1][1] = nIndex + nWidth + 1;
		nTris[1][2] = nIndex + 1;
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CCoreDispInfo::SurfToBaseFacePlane( Vector const &surfPt, Vector &planePt )
{
	// Create bounding boxes
	int nBoxCount = ( GetHeight() - 1 ) * ( GetWidth() - 1 );
	CoreDispBBox_t *pBBox = new CoreDispBBox_t[nBoxCount];
	CreateBoundingBoxes( pBBox, nBoxCount );

	// Use the boxes as a first-pass culling mechanism.
	for( int iBox = 0; iBox < nBoxCount; ++iBox )
	{
		// Get the current displacement triangle-pair bounding-box.
		CoreDispBBox_t *pBox = &pBBox[iBox];
		if( !pBox )
			continue;

		// Check the point against the current displacement bounding-box.
		if ( !PointInDispBBox( pBox, surfPt ) )
			continue;

		// Point lies within the bounding box.
		int nIndex = iBox + ( iBox / ( GetWidth() - 1 ) );

		// Get the triangle coordinates for this box.
		int aTris[2][3];
		GetTriangleIndicesForDispBBox( nIndex, aTris );
		
		// Barycentrically test the triangles on the displacement surface.
		Vector vecPoints[3];
		for ( int iTri = 0; iTri < 2; ++iTri )
		{
			for ( int iVert = 0; iVert < 3; ++iVert )
			{
				GetVert( aTris[iTri][iVert], vecPoints[iVert] );
			}

			float c[3];
			if ( CalcBarycentricCooefs( vecPoints[0], vecPoints[1], vecPoints[2], surfPt, c[0], c[1], c[2] ) )
			{
				Vector vecFlatPoints[3];
				for ( int iVert = 0; iVert < 3; ++iVert )
				{
					GetFlatVert( aTris[iTri][iVert], vecFlatPoints[iVert] );
				}

				planePt = ( vecFlatPoints[0] * c[0] ) + ( vecFlatPoints[1] * c[1] ) + ( vecFlatPoints[2] * c[2] );

				// Delete temporary memory.
				delete [] pBBox;
				return true;
			}
		}
	}

	// Delete temporary memory
	delete [] pBBox;
	return false;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int CCoreDispInfo::GetTriCount( void )
{
	return ( ( GetHeight() - 1 ) * ( GetWidth() -1 ) * 2 );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::GetTriIndices( int iTri, unsigned short &v1, unsigned short &v2, unsigned short &v3 )
{
	// Verify we have the correct data (only build when collision data is built).
	if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) )
	{
		Assert( iTri >= 0 );
		Assert( iTri < GetTriCount() );
		Assert( m_pTris );
		return;
	}

	CoreDispTri_t *pTri = &m_pTris[iTri];
	v1 = pTri->m_iIndex[0];
	v2 = pTri->m_iIndex[1];
	v3 = pTri->m_iIndex[2];
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::SetTriIndices( int iTri, unsigned short v1, unsigned short v2, unsigned short v3 )
{
	// Verify we have the correct data (only build when collision data is built).
	if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) )
	{
		Assert( iTri >= 0 );
		Assert( iTri < GetTriCount() );
		Assert( m_pTris );
		return;
	}

	CoreDispTri_t *pTri = &m_pTris[iTri];
	pTri->m_iIndex[0] = v1;
	pTri->m_iIndex[1] = v2;
	pTri->m_iIndex[2] = v3;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::GetTriPos( int iTri, Vector &v1, Vector &v2, Vector &v3 )
{
	// Verify we have the correct data (only build when collision data is built).
	if ( !m_pTris || ( iTri < 0 ) || ( iTri >= GetTriCount() ) )
	{
		Assert( iTri >= 0 );
		Assert( iTri < GetTriCount() );
		Assert( m_pTris );
		return;
	}

	CoreDispTri_t *pTri = &m_pTris[iTri];
	v1 = m_pVerts[pTri->m_iIndex[0]].m_Vert;
	v2 = m_pVerts[pTri->m_iIndex[1]].m_Vert;
	v3 = m_pVerts[pTri->m_iIndex[2]].m_Vert;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::InitTris( void )
{
	// Verify we have the correct data (only build when collision data is built).
	if ( !m_pTris )
	{
		Assert( m_pTris );
		return;
	}

	int nTriCount = GetTriCount();
	for ( int iTri = 0; iTri < nTriCount; ++iTri )
	{
		m_pTris[iTri].m_uiTags = 0;
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::CreateTris( void )
{
	// Verify we have the correct data (only build when collision data is built).
	if ( !m_pTris )
	{
		Assert( m_pTris );
		return;
	}

	// Extra sanity check if wanted!
	Assert( GetTriCount() == ( m_RenderIndexCount / 3 ) );

	int nTriCount = GetTriCount();
	for ( int iTri = 0, iRender = 0; iTri < nTriCount; ++iTri, iRender += 3 )
	{
		m_pTris[iTri].m_iIndex[0] = m_RenderIndices[iRender];
		m_pTris[iTri].m_iIndex[1] = m_RenderIndices[iRender+1];
		m_pTris[iTri].m_iIndex[2] = m_RenderIndices[iRender+2];
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CCoreDispInfo::IsTriWalkable( int iTri )
{ 
	if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) )
	{
		return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_VAL );
	}

	return IsTriTag( iTri, COREDISPTRI_TAG_WALKABLE );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CCoreDispInfo::IsTriBuildable( int iTri )						
{ 
	if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) )
	{
		return IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_VAL );
	}

	return IsTriTag( iTri, COREDISPTRI_TAG_BUILDABLE );
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CCoreDispInfo::Position_Update( int iVert, Vector vecPos )
{
	Vector vSPos, vFlat;
	GetFlatVert( iVert, vFlat );
	GetSubdivPosition( iVert, vSPos );
				
	Vector vSeg;
	vSeg = vecPos - vFlat;
	vSeg -= vSPos;
				
	// Subtract out the elevation.
	float elev = GetElevation();
	if( elev != 0.0 )
	{
		Vector vNormal;
		GetSurface()->GetNormal( vNormal );
		vNormal *= elev;
		
		vSeg -= vNormal;
	}
				
	float flDistance = VectorNormalize( vSeg );
	
	SetFieldVector( iVert, vSeg );
	SetFieldDistance( iVert, flDistance );
}
