//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include <stdafx.h>
#include "MainFrm.h"
#include "MapDoc.h"
#include "GlobalFunctions.h"
#include "Subdiv.h"
#include "History.h"

//=============================================================================
//
// Subdivision Point Functions
//

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivPoint::Clear( void )
{
	VectorClear( m_Point );
	VectorClear( m_NewPoint );
	VectorClear( m_Normal );
	VectorClear( m_NewNormal );

	m_Type = -1;
	m_Valence = 0;

	for( int i = 0; i < NUM_SUBDIV_EDGES; i++ )
	{
		m_pEdges[i] = NULL;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivPoint::Copy( const CSubdivPoint *pFrom )
{
	m_Point = pFrom->m_Point;
	m_NewPoint = pFrom->m_NewPoint;
	m_Normal = pFrom->m_Normal;
	m_NewNormal = pFrom->m_NewNormal;

	m_Type = pFrom->m_Type;
	m_Valence = pFrom->m_Valence;

	for( int i = 0; i < NUM_SUBDIV_EDGES; i++ )
	{
		m_pEdges[i] = pFrom->m_pEdges[i];
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivPoint::CalcNewVertexNormal( void )
{
	switch( m_Type )
	{
	case POINT_CORNER:
		{
			m_NewNormal = m_Normal;
			break;
		}
	case POINT_CREASE:
		{
			Vector edgeAccum;
			VectorClear( edgeAccum );
			for( int i = 0; i < m_Valence; i++ )
			{
				if( m_pEdges[i]->m_Sharpness > 0.0f )
				{
					VectorAdd( edgeAccum, m_pEdges[i]->m_NewEdgeNormal, edgeAccum );
				}
			}

			//
			// normal
			//
			VectorScale( m_Normal, 6.0f, m_NewNormal );
			VectorAdd( m_NewNormal, edgeAccum, m_NewNormal );
			VectorScale( m_NewNormal, 0.125f, m_NewNormal );

			break;
		}
	case POINT_ORDINARY:
		{
			//
			// accumulate edge data and multiply by valence ratio
			//
			Vector edgeAccum;
			VectorClear( edgeAccum );
			for( int i = 0; i < m_Valence; i++ )
			{
				VectorAdd( edgeAccum, m_pEdges[i]->m_NewEdgeNormal, edgeAccum );
			}
			float ratio = ( 1.0f / ( float )( m_Valence * m_Valence ) );
			VectorScale( edgeAccum, ratio, edgeAccum );

			//
			// accumulate centroid data and multiply by valence ratio
			//
			int	        quadCount = 0;
			CSubdivQuad *quadList[16];
			for( i = 0; i < m_Valence; i++ )
			{
				for( int j = 0; j < 2; j++ )
				{
					if( m_pEdges[i]->m_pQuads[j] )
					{
						for( int k = 0; k < quadCount; k++ )
						{
							if( m_pEdges[i]->m_pQuads[j] == quadList[k] )
								break;
						}

						if( k != quadCount )
							continue;

						quadList[quadCount] = m_pEdges[i]->m_pQuads[j];
						quadCount++;
					}
				}
			}

			Vector centroidAccum;
			VectorClear( centroidAccum );
			for( i = 0; i < quadCount; i++ )
			{
				Vector centroid;
				quadList[i]->GetNormal( centroid );
				VectorAdd( centroidAccum, centroid, centroidAccum );
			}
			VectorScale( centroidAccum, ratio, centroidAccum );

			//
			// normal
			//
			VectorScale( m_Normal, ratio, m_NewNormal );
			VectorAdd( m_NewNormal, edgeAccum, m_NewNormal );
			VectorAdd( m_NewNormal, centroidAccum, m_NewNormal );

			break;
		}
	default:
		break;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivPoint::CalcNewVertexPoint( void )
{
	switch( m_Type )
	{
	case POINT_CORNER:
		{
			m_NewPoint = m_Point;
			break;
		}
	case POINT_CREASE:
		{
			Vector edgeAccum;
			VectorClear( edgeAccum );
			for( int i = 0; i < m_Valence; i++ )
			{
				if( m_pEdges[i]->m_Sharpness > 0.0f )
				{
					VectorAdd( edgeAccum, m_pEdges[i]->m_NewEdgePoint, edgeAccum );
				}
			}

			//
			// point
			//
			VectorScale( m_Point, 6.0f, m_NewPoint );
			VectorAdd( m_NewPoint, edgeAccum, m_NewPoint );
			VectorScale( m_NewPoint, 0.125f, m_NewPoint );

			break;
		}
	case POINT_ORDINARY:
		{
			//
			// accumulate edge data and multiply by valence ratio
			//
			Vector edgeAccum;
			VectorClear( edgeAccum );
			for( int i = 0; i < m_Valence; i++ )
			{
				VectorAdd( edgeAccum, m_pEdges[i]->m_NewEdgePoint, edgeAccum );
			}
			float ratio = ( 1.0f / ( float )( m_Valence * m_Valence ) );
			VectorScale( edgeAccum, ratio, edgeAccum );

			//
			// accumulate centroid data and multiply by valence ratio
			//
			int	        quadCount = 0;
			CSubdivQuad *quadList[16];
			for( i = 0; i < m_Valence; i++ )
			{
				for( int j = 0; j < 2; j++ )
				{
					if( m_pEdges[i]->m_pQuads[j] )
					{
						for( int k = 0; k < quadCount; k++ )
						{
							if( m_pEdges[i]->m_pQuads[j] == quadList[k] )
								break;
						}

						if( k != quadCount )
							continue;

						quadList[quadCount] = m_pEdges[i]->m_pQuads[j];
						quadCount++;
					}
				}
			}

			Vector centroidAccum;
			VectorClear( centroidAccum );
			for( i = 0; i < quadCount; i++ )
			{
				Vector centroid;
				quadList[i]->GetCentroid( centroid );
				VectorAdd( centroidAccum, centroid, centroidAccum );
			}
			VectorScale( centroidAccum, ratio, centroidAccum );

			//
			// point contribution to eqtn.
			//
			ratio = ( ( float )m_Valence - 2.0f ) / ( float )m_Valence;
			VectorScale( m_Point, ratio, m_NewPoint );

			VectorAdd( m_NewPoint, edgeAccum, m_NewPoint );
			VectorAdd( m_NewPoint, centroidAccum, m_NewPoint );

			break;
		}
	default:
		break;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CompareSubdivPoints( const CSubdivPoint *pPoint1, const CSubdivPoint *pPoint2, float tolerance )
{
	for( int i = 0 ; i < 3 ; i++ )
	{
		if( fabs( pPoint1->m_Point[i] - pPoint2->m_Point[i] ) > tolerance )
			return false;
	}
	
	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CompareSubdivPointToPoint( const CSubdivPoint *pSubdivPoint, const Vector& point, float tolerance )
{
	for( int i = 0 ; i < 3 ; i++ )
	{
		if( fabs( pSubdivPoint->m_Point[i] - point[i] ) > tolerance )
			return false;
	}
	
	return true;
}


//=============================================================================
//
// Subdivision Edge Functions
//

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivEdge::Clear( void )
{
	for( int i = 0; i < 2; i++ )
	{
		m_ndxPoint[i] = -1;
		m_pQuads[i] = NULL;
		m_ndxQuadEdge[i] = -1;
	}

	m_Sharpness = 1.0f;
	VectorClear( m_NewEdgePoint );
	m_Active = false;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivEdge::Copy( const CSubdivEdge *pFrom )
{
	for( int i = 0; i < 2; i++ )
	{
		m_ndxPoint[i] = pFrom->m_ndxPoint[i];
		m_pQuads[i] = pFrom->m_pQuads[i];
		m_ndxQuadEdge[i] = pFrom->m_ndxQuadEdge[i];
	}

	m_Sharpness = pFrom->m_Sharpness;
	m_NewEdgePoint = pFrom->m_NewEdgePoint;
	m_Active = pFrom->m_Active;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivEdge::CalcNewEdgeNormal( void )
{
	if( !m_Active )
		return;

	//
	// get the subdivision mesh
	//
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if( !pDoc )
		return;

	CSubdivMesh *pMesh = pDoc->GetSubdivMesh();

	//
	// get the edge data
	//
	Vector normal0, normal1;
	pMesh->GetNormal( m_ndxPoint[0], normal0 );
	pMesh->GetNormal( m_ndxPoint[1], normal1 );

	//
	// calculate the "sharp" new edge point
	//
	Vector vSharp;
	VectorClear( vSharp );
	VectorAdd( normal0, normal1, vSharp );
	VectorScale( vSharp, 0.5f, vSharp );

	//
	// calculate the "smooth" new edge point if necessary
	//
	Vector vSmooth;
	VectorClear( vSmooth );
	if( m_pQuads[1] && ( m_Sharpness != 1.0f ) )
	{
		Vector quadNormals[2];
		m_pQuads[0]->GetNormal( quadNormals[0] );
		m_pQuads[1]->GetNormal( quadNormals[1] );
		VectorAdd( normal0, normal1, vSmooth );
		VectorAdd( vSmooth, quadNormals[0], vSmooth );
		VectorAdd( vSmooth, quadNormals[1], vSmooth );
		VectorScale( vSmooth, 0.25f, vSmooth );
	}
	else
	{
		// make sure -- if here because of no neighboring quad
		m_Sharpness = 1.0f;
		m_pQuads[0]->CalcNormal();
	}
			
	//
	// calculate the new edge point
	//
	// ( 1 - edge(sharpness) ) * vSmooth + edge(sharpness) * vSharp
	//
	VectorScale( vSmooth, ( 1.0f - m_Sharpness ), vSmooth );
	VectorScale( vSharp, m_Sharpness, vSharp );
	VectorAdd( vSmooth, vSharp, m_NewEdgeNormal );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivEdge::CalcNewEdgePoint( void )
{
	if( !m_Active )
		return;

	//
	// get the subdivision mesh
	//
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if( !pDoc )
		return;

	CSubdivMesh *pMesh = pDoc->GetSubdivMesh();

	//
	// get the edge data
	//
	Vector edgePt0, edgePt1;
	pMesh->GetPoint( m_ndxPoint[0], edgePt0 );
	pMesh->GetPoint( m_ndxPoint[1], edgePt1 );

	//
	// calculate the "sharp" new edge point
	//
	Vector vSharp;
	VectorClear( vSharp );
	VectorAdd( edgePt0, edgePt1, vSharp );
	VectorScale( vSharp, 0.5f, vSharp );

	//
	// calculate the "smooth" new edge point if necessary
	//
	Vector vSmooth;
	VectorClear( vSmooth );
	if( m_pQuads[1] && ( m_Sharpness != 1.0f ) )
	{
		Vector centroids[2];
		m_pQuads[0]->GetCentroid( centroids[0] );
		m_pQuads[1]->GetCentroid( centroids[1] );
		VectorAdd( edgePt0, edgePt1, vSmooth );
		VectorAdd( vSmooth, centroids[0], vSmooth );
		VectorAdd( vSmooth, centroids[1], vSmooth );
		VectorScale( vSmooth, 0.25f, vSmooth );
	}
	else
	{
		// make sure -- if here because of no neighboring quad
		m_Sharpness = 1.0f;
		m_pQuads[0]->CalcCentroid();
	}
			
	//
	// calculate the new edge point
	//
	// ( 1 - edge(sharpness) ) * vSmooth + edge(sharpness) * vSharp
	//
	VectorScale( vSmooth, ( 1.0f - m_Sharpness ), vSmooth );
	VectorScale( vSharp, m_Sharpness, vSharp );
	VectorAdd( vSmooth, vSharp, m_NewEdgePoint );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CompareSubdivEdges( const CSubdivEdge *pEdge1, const CSubdivEdge *pEdge2 )
{
	if( ( ( pEdge1->m_ndxPoint[0] == pEdge2->m_ndxPoint[0] ) && ( pEdge1->m_ndxPoint[1] == pEdge2->m_ndxPoint[1] ) ) ||
		( ( pEdge1->m_ndxPoint[0] == pEdge2->m_ndxPoint[1] ) && ( pEdge1->m_ndxPoint[1] == pEdge2->m_ndxPoint[0] ) ) )
		return true;

	return false;	
}


//=============================================================================
//
// Subdivision Quad Functions
//

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivQuad::GetCentroid( Vector& centroid )
{
	// get the subdivision mesh
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if( !pDoc )
		return;

	CSubdivMesh *pMesh = pDoc->GetSubdivMesh();

	VectorClear( centroid );
	for( int i = 0; i < 4; i++ )
	{
		Vector point;
		pMesh->GetPoint( m_ndxVert[i], point );
		VectorAdd( centroid, point, centroid );
	}

	VectorScale( centroid, 0.25f, centroid );

	// keep to surface creation
	m_Centroid = centroid;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivQuad::CalcCentroid( void )
{
	// get the subdivision mesh
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if( !pDoc )
		return;

	CSubdivMesh *pMesh = pDoc->GetSubdivMesh();

	VectorClear( m_Centroid );
	for( int i = 0; i < 4; i++ )
	{
		Vector point;
		pMesh->GetPoint( m_ndxVert[i], point );
		VectorAdd( m_Centroid, point, m_Centroid );
	}

	VectorScale( m_Centroid, 0.25f, m_Centroid );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivQuad::GetNormal( Vector& normal )
{
	// get the subdivision mesh
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if( !pDoc )
		return;

	CSubdivMesh *pMesh = pDoc->GetSubdivMesh();

	Vector points[3];
	Vector segs[2];

	pMesh->GetPoint( m_ndxVert[0], points[0] );
	pMesh->GetPoint( m_ndxVert[1], points[1] );
	pMesh->GetPoint( m_ndxVert[2], points[2] );

	VectorSubtract( points[1], points[0], segs[0] );
	VectorSubtract( points[2], points[0], segs[1] );

	CrossProduct( segs[1], segs[0], normal );
	VectorNormalize( normal );

	m_Normal = normal;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivQuad::CalcNormal( void )
{
	// get the subdivision mesh
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if( !pDoc )
		return;

	CSubdivMesh *pMesh = pDoc->GetSubdivMesh();

	Vector points[3];
	Vector segs[2];

	pMesh->GetPoint( m_ndxVert[0], points[0] );
	pMesh->GetPoint( m_ndxVert[1], points[1] );
	pMesh->GetPoint( m_ndxVert[2], points[2] );

	VectorSubtract( points[1], points[0], segs[0] );
	VectorSubtract( points[2], points[0], segs[1] );

	CrossProduct( segs[1], segs[0], m_Normal );
	VectorNormalize( m_Normal );
}


//=============================================================================
//
// Subdivision Mesh Functions
//

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CSubdivMesh::CSubdivMesh()
{
	Clear();
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CSubdivMesh::~CSubdivMesh()
{
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CSubdivMesh::AddPoint( const Vector& point, const Vector& normal )
{
	//
	// check for existing point within CSubdivPoints
	//
	for( int i = 0; i < m_PointCount; i++ )
	{
		if( CompareSubdivPointToPoint( &m_pPoints[i], point, 0.01f ) )
			return i;
	}

	if( m_PointCount >= m_MaxPointCount )
	{
		// error message!
		return -1;
	}

	m_pPoints[m_PointCount].m_Point = point;
	m_pPoints[m_PointCount].m_Normal = normal;
	m_PointCount++;

	return ( m_PointCount - 1 );
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::RemovePoint( Vector& point )
{
	//
	// find point in list (and remove it)
	//
	for( int i = 0; i < m_PointCount; i++ )
	{
		if( !CompareSubdivPointToPoint( &m_pPoints[i], point, 0.01f ) )
			continue;

		if( i == ( m_PointCount - 1 ) )
		{
			m_pPoints[i].Clear();
		}
		else
		{
			m_pPoints[i].Copy( &m_pPoints[m_PointCount-1] );
			m_pPoints[m_PointCount-1].Clear();
		}
		
		m_PointCount--;
	}
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CSubdivMesh::AddEdge( CSubdivEdge *edge )
{
	//
	// check for existing edge
	//
	for( int i = 0; i < m_EdgeCount; i++ )
	{
		if( CompareSubdivEdges( edge, &m_pEdges[i] ) )
		{
			//
			// check for "quads" on both sides of edge (add if necessary)
			//
			if( ( !m_pEdges[i].m_pQuads[1] ) && ( edge->m_pQuads[0] != m_pEdges[i].m_pQuads[0] ) )
			{
				m_pEdges[i].m_pQuads[1] = edge->m_pQuads[0];
				m_pEdges[i].m_ndxQuadEdge[1] = edge->m_ndxQuadEdge[0];
				m_pEdges[i].m_Sharpness = 0.0f;
			}

			return i;
		}
	}

	if( m_EdgeCount >= m_MaxEdgeCount )
	{
		// error message!
		return -1;
	}

	m_pEdges[m_EdgeCount].Copy( edge );
	m_pEdges[m_EdgeCount].m_Active = true;
	m_EdgeCount++;

	return ( m_EdgeCount - 1 );
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::RemoveEdge( CSubdivEdge *edge )
{
	return;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::CatmullClarkSubdivide( void )
{
	//
	// calculate the "new edge points"
	//
	for( int i = 0; i < m_EdgeCount; i++ )
	{
		m_pEdges[i].CalcNewEdgePoint();
		m_pEdges[i].CalcNewEdgeNormal();
	}

	//
	// if point index if part of edge, add to point edge list and increment valence
	//
	for( i = 0; i < m_PointCount; i++ )
	{
		for( int j = 0; j < m_EdgeCount; j++ )
		{
			if( !m_pEdges[j].m_Active )
				continue;

			if( ( i == m_pEdges[j].m_ndxPoint[0] ) || ( i == m_pEdges[j].m_ndxPoint[1] ) )
			{
				m_pPoints[i].m_pEdges[m_pPoints[i].m_Valence] = &m_pEdges[j];
				m_pPoints[i].m_Valence++;
			}
		}
	}

	//
	// determine the point's "type"
	//
	for( i = 0; i < m_PointCount; i++ )
	{
		//
		// get the number of sharp incident edges and neighbor data
		//
		int sharpnessCount = 0;
		int sharpnessThreshold = m_pPoints[i].m_Valence - 1;
		bool bHasNeighbors = false;

		for( int j = 0; j < m_pPoints[i].m_Valence; j++ )
		{
			if( m_pPoints[i].m_pEdges[j]->m_Sharpness > 0.0f )
			{
				sharpnessCount++;
			}

			if( m_pPoints[i].m_pEdges[j]->m_pQuads[1] )
			{
				bHasNeighbors = true;
			}
		}

		//
		// determine point type
		//
		if( ( sharpnessCount >= sharpnessThreshold ) || !bHasNeighbors )
//		if( ( sharpnessCount > 2 ) || !bHasNeighbors )
		{
			m_pPoints[i].m_Type = CSubdivPoint::POINT_CORNER;
			continue;
		}

		if( sharpnessCount > 1 )
//		if( sharpnessCount == 2 )
		{
			m_pPoints[i].m_Type = CSubdivPoint::POINT_CREASE;
			continue;
		}

		m_pPoints[i].m_Type = CSubdivPoint::POINT_ORDINARY;
	}

	//
	// calculate the new vertex point
	//
	for( i = 0; i < m_PointCount; i++ )
	{
		m_pPoints[i].CalcNewVertexPoint();
		m_pPoints[i].CalcNewVertexNormal();
	}

	//
	// move all new points to points
	//
	for( i = 0; i < m_PointCount; i++ )
	{
		m_pPoints[i].m_Point = m_pPoints[i].m_NewPoint;
		m_pPoints[i].m_Normal = m_pPoints[i].m_NewNormal;
		VectorClear( m_pPoints[i].m_NewPoint );
		VectorClear( m_pPoints[i].m_NewNormal );
		m_pPoints[i].m_Valence = 0;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CSubdivMesh::AddTree( CSubdivQuad *pTree )
{
	//
	// check to see if tree already exists in list
	//
	for( int i = 0; i < m_TreeCount; i++ )
	{
		if( pTree == m_ppTrees[i] )
			return i;
	}

	//
	// check tree count
	//
	if( m_TreeCount >= m_MaxTreeCount )
	{
		// error message
		_asm int 3;
		return -1;
	}

	//
	// add tree to list
	//
	m_ppTrees[m_TreeCount] = pTree;
	m_TreeCount++;

	return ( m_TreeCount - 1 );
}

static HCURSOR preSubdivCursor;

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CSubdivMesh::PreSubdivide( void )
{
	// change the mouse to hourglass -- so level designers know something is
	// happening
	preSubdivCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) );

	// clear the mesh
	Clear();

	//
	// get the selection set
	//
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if( !pDoc )
		return false;

	CDispManager *pDispManager = pDoc->GetDispManager();
	if( !pDispManager )
		return false;

	// get number of displacements in selection
	int selectionCount = pDispManager->GetSelectionListCount();

	// allocate memory
	if( !AllocCache( selectionCount ) )
		return false;

	// mark the subdivision undo
	GetHistory()->MarkUndoPosition( NULL, "Subdivision" );
	
	//
	// add all surfaces to mesh to subdivide
	//
	for( int i = 0; i < selectionCount; i++ )
	{
		// get the current displacement surface
		CMapDisp *pDisp = pDispManager->GetFromSelectionList( i );
		if( !pDisp )
			continue;
			
		//
		// setup for undo
		//
		CMapFace *pFace = ( CMapFace* )pDisp->GetParent();
		CMapSolid *pSolid = ( CMapSolid* )pFace->GetParent();
		GetHistory()->Keep( ( CMapClass* )pSolid );

		//
		// add displacement's subdivision tree to mesh list
		//
		if( AddTree( pDisp->PreSubdivide( this ) ) == -1 )
			return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::SetEdgeData( CSubdivQuad *pRoot, int index, int parentIndex, int subdivIndex )
{
	for( int i = 0; i < 4; i++ )
	{
		CSubdivEdge edge;
		
		//
		// add vert indices
		//
		edge.m_ndxPoint[0] = pRoot[index].m_ndxVert[i];
		edge.m_ndxPoint[1] = pRoot[index].m_ndxVert[(i+1)%4];
		
		//
		// set initial quads and edges data
		//
		edge.m_pQuads[0] = &pRoot[index];
		edge.m_pQuads[1] = NULL;
		
		edge.m_ndxQuadEdge[0] = i;
		edge.m_ndxQuadEdge[1] = -1;

		//
		// set edge sharpness
		//
		if( ( i == subdivIndex ) || ( i == ( (subdivIndex+3)%4 ) ) )
		{
			edge.m_Sharpness = m_pEdges[pRoot[parentIndex].m_ndxEdge[i]].m_Sharpness;
		}
		else
		{
			edge.m_Sharpness = 0.0f;
		}
	
		// add edge to global list
		pRoot[index].m_ndxEdge[i] = AddEdge( &edge );
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::CreateChildQuad4( CSubdivQuad *pRoot, int index, int parentIndex )
{
	//
	// set quad indices -- displacement index values
	//
	pRoot[index].m_ndxQuad[0] = ( ( pRoot[parentIndex].m_ndxQuad[0] + pRoot[parentIndex].m_ndxQuad[3] ) / 2 );
	pRoot[index].m_ndxQuad[1] = ( ( pRoot[parentIndex].m_ndxQuad[0] + pRoot[parentIndex].m_ndxQuad[2] ) / 2 );
	pRoot[index].m_ndxQuad[2] = ( ( pRoot[parentIndex].m_ndxQuad[2] + pRoot[parentIndex].m_ndxQuad[3] ) / 2 );
	pRoot[index].m_ndxQuad[3] = pRoot[parentIndex].m_ndxQuad[3];

	//
	// set vert indices
	//
	pRoot[index].m_ndxVert[0] = AddPoint( m_pEdges[pRoot[parentIndex].m_ndxEdge[3]].m_NewEdgePoint, 
		                                  m_pEdges[pRoot[parentIndex].m_ndxEdge[3]].m_NewEdgeNormal );
	pRoot[index].m_ndxVert[1] = AddPoint( pRoot[parentIndex].m_Centroid, pRoot[parentIndex].m_Normal );
	pRoot[index].m_ndxVert[2] = AddPoint( m_pEdges[pRoot[parentIndex].m_ndxEdge[2]].m_NewEdgePoint,
		                                  m_pEdges[pRoot[parentIndex].m_ndxEdge[2]].m_NewEdgeNormal );
	pRoot[index].m_ndxVert[3] = pRoot[parentIndex].m_ndxVert[3];

	// set edge data
	SetEdgeData( pRoot, index, parentIndex, 3 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::CreateChildQuad3( CSubdivQuad *pRoot, int index, int parentIndex )
{
	//
	// set quad indices -- displacement index values
	//
	pRoot[index].m_ndxQuad[0] = ( ( pRoot[parentIndex].m_ndxQuad[0] + pRoot[parentIndex].m_ndxQuad[2] ) / 2 );
	pRoot[index].m_ndxQuad[1] = ( ( pRoot[parentIndex].m_ndxQuad[1] + pRoot[parentIndex].m_ndxQuad[2] ) / 2 );
	pRoot[index].m_ndxQuad[2] = pRoot[parentIndex].m_ndxQuad[2];
	pRoot[index].m_ndxQuad[3] = ( ( pRoot[parentIndex].m_ndxQuad[2] + pRoot[parentIndex].m_ndxQuad[3] ) / 2 );

	//
	// set vert indices
	//
	pRoot[index].m_ndxVert[0] = AddPoint( pRoot[parentIndex].m_Centroid, pRoot[parentIndex].m_Normal );
	pRoot[index].m_ndxVert[1] = AddPoint( m_pEdges[pRoot[parentIndex].m_ndxEdge[1]].m_NewEdgePoint,
		                                  m_pEdges[pRoot[parentIndex].m_ndxEdge[1]].m_NewEdgeNormal );
	pRoot[index].m_ndxVert[2] = pRoot[parentIndex].m_ndxVert[2];
	pRoot[index].m_ndxVert[3] = AddPoint( m_pEdges[pRoot[parentIndex].m_ndxEdge[2]].m_NewEdgePoint,
		                                  m_pEdges[pRoot[parentIndex].m_ndxEdge[2]].m_NewEdgeNormal );

	// set edge data
	SetEdgeData( pRoot, index, parentIndex, 2 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::CreateChildQuad2( CSubdivQuad *pRoot, int index, int parentIndex )
{
	//
	// set quad indices -- displacement index values
	//
	pRoot[index].m_ndxQuad[0] = ( ( pRoot[parentIndex].m_ndxQuad[0] + pRoot[parentIndex].m_ndxQuad[1] ) / 2 );
	pRoot[index].m_ndxQuad[1] = pRoot[parentIndex].m_ndxQuad[1];
	pRoot[index].m_ndxQuad[2] = ( ( pRoot[parentIndex].m_ndxQuad[1] + pRoot[parentIndex].m_ndxQuad[2] ) / 2 );
	pRoot[index].m_ndxQuad[3] = ( ( pRoot[parentIndex].m_ndxQuad[0] + pRoot[parentIndex].m_ndxQuad[2] ) / 2 );

	//
	// set vert indices
	//
	pRoot[index].m_ndxVert[0] = AddPoint( m_pEdges[pRoot[parentIndex].m_ndxEdge[0]].m_NewEdgePoint,
		                                  m_pEdges[pRoot[parentIndex].m_ndxEdge[0]].m_NewEdgeNormal );
	pRoot[index].m_ndxVert[1] = pRoot[parentIndex].m_ndxVert[1];
	pRoot[index].m_ndxVert[2] = AddPoint( m_pEdges[pRoot[parentIndex].m_ndxEdge[1]].m_NewEdgePoint,
		                                  m_pEdges[pRoot[parentIndex].m_ndxEdge[1]].m_NewEdgeNormal );
	pRoot[index].m_ndxVert[3] = AddPoint( pRoot[parentIndex].m_Centroid, pRoot[parentIndex].m_Normal );

	// set edge data
	SetEdgeData( pRoot, index, parentIndex, 1 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::CreateChildQuad1( CSubdivQuad *pRoot, int index, int parentIndex )
{
	//
	// set quad indices -- displacement index values
	//
	pRoot[index].m_ndxQuad[0] = pRoot[parentIndex].m_ndxQuad[0];
	pRoot[index].m_ndxQuad[1] = ( ( pRoot[parentIndex].m_ndxQuad[0] + pRoot[parentIndex].m_ndxQuad[1] ) / 2 );
	pRoot[index].m_ndxQuad[2] = ( ( pRoot[parentIndex].m_ndxQuad[0] + pRoot[parentIndex].m_ndxQuad[2] ) / 2 );
	pRoot[index].m_ndxQuad[3] = ( ( pRoot[parentIndex].m_ndxQuad[0] + pRoot[parentIndex].m_ndxQuad[3] ) / 2 );

	//
	// set vert indices
	//
	pRoot[index].m_ndxVert[0] = pRoot[parentIndex].m_ndxVert[0];
	pRoot[index].m_ndxVert[1] = AddPoint( m_pEdges[pRoot[parentIndex].m_ndxEdge[0]].m_NewEdgePoint,
		                                  m_pEdges[pRoot[parentIndex].m_ndxEdge[0]].m_NewEdgeNormal );
	pRoot[index].m_ndxVert[2] = AddPoint( pRoot[parentIndex].m_Centroid, pRoot[parentIndex].m_Normal );
	pRoot[index].m_ndxVert[3] = AddPoint( m_pEdges[pRoot[parentIndex].m_ndxEdge[3]].m_NewEdgePoint,
		                                  m_pEdges[pRoot[parentIndex].m_ndxEdge[3]].m_NewEdgeNormal );

	// set edge data
	SetEdgeData( pRoot, index, parentIndex, 0 );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::CreateChildQuads( CSubdivQuad *pRoot, int quadIndex )
{
	//
	// create children
	//
	CreateChildQuad1( pRoot, ( ( quadIndex << 2 ) + 1 ), quadIndex );
	CreateChildQuad2( pRoot, ( ( quadIndex << 2 ) + 2 ), quadIndex );
	CreateChildQuad3( pRoot, ( ( quadIndex << 2 ) + 3 ), quadIndex );
	CreateChildQuad4( pRoot, ( ( quadIndex << 2 ) + 4 ), quadIndex );

	for( int i = 0; i < 4; i++ )
	{
		m_pEdges[pRoot[quadIndex].m_ndxEdge[i]].m_Active = false;
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::AddQuadToMesh( CSubdivQuad *pQuad )
{
	return;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CSubdivMesh::GetEndIndexFromLevel( int levelIndex )
{
	switch( levelIndex )
	{
	case 0: { return 0; }
	case 1: { return 4; }
	case 2: { return 20; }
	case 3: { return 84; }
	default: { return 0; }
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
int CSubdivMesh::GetStartIndexFromLevel( int levelIndex )
{
	switch( levelIndex )
	{
	case 0: { return 0; }
	case 1: { return 1; }
	case 2: { return 5; }
	case 3: { return 21; }
	default: { return 0; }
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::Subdivide( void )
{
	//
	// subdivide to four levels always (what the trees hold)
	//
	for( int subdivLevel = 0; subdivLevel < 4; subdivLevel++ )
	{
		int startIndex = GetStartIndexFromLevel( subdivLevel );
		int endIndex = GetEndIndexFromLevel( subdivLevel );

		// subdivide
		CatmullClarkSubdivide();

		//
		// add subdivision data to subdivision tree
		//
		for( int treeIndex = 0; treeIndex < m_TreeCount; treeIndex++ )
		{
			//
			// get the current tree
			//
			CSubdivQuad *pTree = m_ppTrees[treeIndex];
			if( !pTree )
				continue;
			
			//
			// for each quad in the tree (at the given level)
			//
			for( int index = startIndex; index <= endIndex; index++ )
			{
				CreateChildQuads( pTree, index );			
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::PostSubdivide( void )
{
	//
	// get the selection set
	//
	CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
	if( !pDoc )
		return;

	CDispManager *pDispManager = pDoc->GetDispManager();
	if( !pDispManager )
		return;

	//
	// add all surfaces to mesh to subdivide
	//
	int selectionCount = pDispManager->GetSelectionListCount();
	for( int i = 0; i < selectionCount; i++ )
	{
		// get the current displacement surface
		CMapDisp *pDisp = pDispManager->GetFromSelectionList( i );
		if( !pDisp )
			continue;

		// post subdivide
		pDisp->PostSubdivide( this );
	}

	// destroy cache!!!
	FreeCache();

	// set the cursor back to its previous state (before subdivision
	SetCursor( preSubdivCursor );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::DoSubdivide( void )
{
	PreSubdivide();
	Subdivide();
	PostSubdivide();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CSubdivMesh::AllocCache( int dispCount )
{
#define POINTS_PER_DISP		512
#define EDGES_PER_DISP		1024

	m_MaxPointCount = POINTS_PER_DISP * dispCount;
	m_MaxEdgeCount = EDGES_PER_DISP * dispCount;
	m_MaxTreeCount = dispCount;

	m_pPoints = new CSubdivPoint[m_MaxPointCount];
	m_pEdges = new CSubdivEdge[m_MaxEdgeCount];
	m_ppTrees = new CSubdivQuad*[m_MaxTreeCount];

	if( !m_pPoints || !m_pEdges || !m_ppTrees )
	{
		FreeCache();
		return false;
	}

	//
	// clear cache
	//
	for( int i = 0; i < m_MaxPointCount; i++ )
	{
		m_pPoints[i].Clear();
	}

	for( i = 0; i < m_MaxEdgeCount; i++ )
	{
		m_pEdges[i].Clear();
	}

	//
	// tell size of cache
	//
	int size = m_MaxPointCount * sizeof( CSubdivPoint );
	size += m_MaxEdgeCount * sizeof( CSubdivEdge );
	size += m_MaxTreeCount * sizeof( CSubdivQuad );

	TRACE1( "Subdiv Cache: %d\n", size );

	return true;

#undef POINTS_PER_DISP
#undef EDGES_PER_DISP
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CSubdivMesh::FreeCache( void )
{
	if( m_pPoints )
	{
		delete [] m_pPoints;
		m_pPoints = NULL;
		m_PointCount = 0;
	}

	if( m_pEdges )
	{
		delete [] m_pEdges;
		m_pEdges = NULL;
		m_EdgeCount = 0;
	}

	if( m_ppTrees )
	{
		delete [] m_ppTrees;
		m_ppTrees = NULL;
		m_TreeCount = 0;
	}

	// tell cache destroyed!!
	TRACE0( "Subdiv Cache Destroyed!\n" );
}