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

#include <stdafx.h>
#include "bitmap/tgaloader.h"
#include "ChunkFile.h"
#include "MapDefs.h"
#include "MapDisp.h"
#include "MapDoc.h"
#include "MapFace.h"
#include "MapSolid.h"
#include "MapWorld.h"
#include "MainFrm.h"
#include "GlobalFunctions.h"
#include "SaveInfo.h"
#include "TextureSystem.h"
#include "materialsystem/imesh.h"
#include "Material.h"
#include "CollisionUtils.h"
#include "CModel.h"
#include "History.h"
#include "ToolDisplace.h"
#include "ToolManager.h"
#include "mathlib/mathlib.h"
#include "dispshore.h"
#include "Color.h"
#include "render2d.h"
#include "faceeditsheet.h"

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

#define OVERLAY_CHECK_BLOAT		16.0f

bool CMapDisp::m_bSelectMask = false;
bool CMapDisp::m_bGridMask = false;

//-----------------------------------------------------------------------------
// Purpose : CMapDisp constructor
//-----------------------------------------------------------------------------
CMapDisp::CMapDisp()
{
	// clear neighbor data
	ResetNeighbors();

	//
	// initialize the hit indices
	//
	ResetTexelHitIndex();
	ResetDispMapHitIndex();

	m_bHasMappingAxes = false;
	VectorClear( m_MapAxes[0] );
	VectorClear( m_MapAxes[1] );

	m_Scale = 1.0f;

	m_bSubdiv = false;
	m_bReSubdiv = false;

	m_CoreDispInfo.InitDispInfo( 4, 0, 0, NULL, NULL, NULL ); 
	Paint_Init( DISPPAINT_CHANNEL_POSITION );

	m_CoreDispInfo.AllowedVerts_Clear();
}

//-----------------------------------------------------------------------------
// Purpose : CMapDisp deconstructor
//-----------------------------------------------------------------------------
CMapDisp::~CMapDisp()
{
	m_aWalkableVerts.Purge();
	m_aWalkableIndices.Purge();
	m_aForcedWalkableIndices.Purge();

	m_aBuildableVerts.Purge();
	m_aBuildableIndices.Purge();
	m_aForcedBuildableIndices.Purge();

	m_aRemoveVerts.Purge();
	m_aRemoveIndices.Purge();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::InitDispSurfaceData( CMapFace *pFace, bool bGenerateStartPoint )
{
	//
	// verify face is a "quad"
	//
	int pointCount = pFace->GetPointCount();
	if( pointCount != 4 )
		return false;

	// get the displacement surface 
	CCoreDispSurface *pSurf = m_CoreDispInfo.GetSurface();

	//
	// set face point data - pos, normal, texture, etc....
	//
	Vector v3;
	Vector2D v2;
	pSurf->SetPointCount( 4 );
	for( int i = 0; i < 4; i++ )
	{
		// position
		pFace->GetPoint( v3, i );
		pSurf->SetPoint( i, v3 );

		// normal
		pFace->GetFaceNormal( v3 );
		pSurf->SetPointNormal( i, v3 );

		// texture coords
		pFace->GetTexCoord( v2, i );
		pSurf->SetTexCoord( i, v2 );
	}

	//
	// get displacement surface point start index
	//
	int pointStartIndex = pSurf->GetPointStartIndex();
	if( m_bHasMappingAxes && ( pointStartIndex == -1 ) )
	{
		pSurf->GeneratePointStartIndexFromMappingAxes( m_MapAxes[0], m_MapAxes[1] );
	}
	else
	{
		if( bGenerateStartPoint )
		{
			pSurf->GenerateSurfPointStartIndex();
		}
		else
		{
			pSurf->FindSurfPointStartIndex();
		}
	}
	pSurf->AdjustSurfPointData();

	// Luxel coords.
	int nLightmapScale = pFace->texture.nLightmapScale;
	pSurf->CalcLuxelCoords( nLightmapScale, false, pFace->texture.UAxis.AsVector3D(), pFace->texture.VAxis.AsVector3D() );

	// Set the lightmap coordinates.
	for ( int iLuxelCoord = 0; iLuxelCoord < 4; ++iLuxelCoord )
	{
		Vector2D vecCoord;
		pSurf->GetLuxelCoord( 0, iLuxelCoord, vecCoord );
		pFace->SetLightmapCoord( vecCoord, iLuxelCoord );
	}

	// reset the has mapping axes flag (surface has been created! - use new method now)
	m_bHasMappingAxes = false;

	// set the s and t texture mapping axes so that tangent spaces can be calculated
	pSurf->SetSAxis( pFace->texture.UAxis.AsVector3D() );
	pSurf->SetTAxis( pFace->texture.VAxis.AsVector3D() );

	// successful init
	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::ResetFieldData( void )
{
	ResetFieldVectors();
	ResetFieldDistances();
	ResetSubdivPositions();
	ResetSubdivNormals();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::InitData( int power )
{
	// set surface "power" (defines size)
	SetPower( power );

	// clear vector field distances, subdiv positions and normals
	ResetFieldData();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::Create( void )
{
	if ( m_CoreDispInfo.CreateWithoutLOD() )
	{
		PostCreate();
		return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::PostCreate( void )
{
	UpdateBoundingBox();
	UpdateNeighborDependencies( false );
	UpdateLightmapExtents();
	UpdateWalkable();
	UpdateBuildable();

	// Get the current face and create/update any detail objects
	CMapFace *pFace = static_cast<CMapFace*>( GetParent() );
	if ( pFace )
		DetailObjects::BuildAnyDetailObjects(pFace);
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
CMapDisp *CMapDisp::CopyFrom( CMapDisp *pMapDisp, bool bUpdateDependencies )
{
	//
	// check for valid displacement to copy from
	//
    if( !pMapDisp )
        return NULL;

	//
	// copy the base surface data - positions, normals, texture coords, etc...
	//
	CCoreDispSurface *pFromSurf = pMapDisp->m_CoreDispInfo.GetSurface();
	CCoreDispSurface *pToSurf = m_CoreDispInfo.GetSurface();

	int pointCount = pFromSurf->GetPointCount();
	pToSurf->SetPointCount( pointCount );

	Vector2D v2;
	Vector v3;
	for( int i = 0; i < pointCount; i++ )
	{
		pFromSurf->GetPoint( i, v3 );
		pToSurf->SetPoint( i, v3 );

		pFromSurf->GetPointNormal( i, v3 );
		pToSurf->SetPointNormal( i, v3 );

		pFromSurf->GetTexCoord( i, v2 );
		pToSurf->SetTexCoord( i, v2 );

		pFromSurf->GetLuxelCoord( 0, i, v2 );
		pToSurf->SetLuxelCoord( 0, i, v2 );
	}

	pToSurf->SetFlags( pFromSurf->GetFlags() );
	pToSurf->SetContents( pFromSurf->GetContents() );
	pToSurf->SetPointStartIndex( pFromSurf->GetPointStartIndex() );

	//
	// copy displacement surface data
	//
	SetPower( pMapDisp->GetPower() );
	SetElevation( pMapDisp->GetElevation() );

	// save the scale -- don't want to rescale!!
	m_Scale = pMapDisp->GetScale();

	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		pMapDisp->GetFieldVector( i, v3 );
		SetFieldVector( i, v3 );

		pMapDisp->GetSubdivPosition( i, v3 );
		SetSubdivPosition( i, v3 );

		pMapDisp->GetSubdivNormal( i, v3 );
		SetSubdivNormal( i, v3 );

		SetFieldDistance( i, pMapDisp->GetFieldDistance( i ) );

		pMapDisp->GetVert( i, v3 );
		SetVert( i, v3 );

		pMapDisp->GetFlatVert( i, v3 );
		SetFlatVert( i, v3 );

		SetAlpha( i, pMapDisp->GetAlpha( i ) );
	}

	int renderCount = pMapDisp->m_CoreDispInfo.GetRenderIndexCount();
	m_CoreDispInfo.SetRenderIndexCount( renderCount );
	for( int i = 0; i < renderCount; i++ )
	{
		m_CoreDispInfo.SetRenderIndex( i, pMapDisp->m_CoreDispInfo.GetRenderIndex( i ) );
	}

	// Copy the triangle data.
	int nTriCount = GetTriCount();
	for ( int iTri = 0; iTri < nTriCount; ++iTri )
	{
		unsigned short triIndices[3];
		pMapDisp->GetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] );
		m_CoreDispInfo.SetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] );

		unsigned short triValue = pMapDisp->m_CoreDispInfo.GetTriTagValue( iTri );
		m_CoreDispInfo.SetTriTagValue( iTri, triValue );
	}

	//
	// copy editor specific data
	//
	m_bSubdiv = pMapDisp->IsSubdivided();
	m_bReSubdiv = pMapDisp->NeedsReSubdivision();

	ResetTexelHitIndex();
	ResetDispMapHitIndex();
	ResetTouched();
	m_CoreDispInfo.AllowedVerts_Clear();

	//
	// re-build the surface??? an undo, etc...
	//
	if( bUpdateDependencies )
	{
		UpdateData();
		CheckAndUpdateOverlays( true );
	}

    return this;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateSurfData( CMapFace *pFace )
{
	InitDispSurfaceData( pFace, false );
	Create();
}

	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateSurfDataAndVectorField( CMapFace *pFace )
{
	InitDispSurfaceData( pFace, false );

//	ResetFieldVectors();
	ResetSubdivPositions();
	ResetSubdivNormals();

	Create();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateData( void )
{
	Create();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateDataAndNeighborData( void )
{
	// update itself
	Create();

	// update neighbors
	for( int i = 0; i < 4; i++ )
	{
		EditDispHandle_t handle = GetEdgeNeighbor( i );
		if( handle != EDITDISPHANDLE_INVALID )
		{
			CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
			pNeighborDisp->UpdateData();
		}

		int cornerCount = GetCornerNeighborCount( i );
		if( cornerCount > 0 )
		{
			for( int j = 0; j < cornerCount; j++ )
			{
				handle = GetCornerNeighbor( i, j );
				if( handle != EDITDISPHANDLE_INVALID )
				{
					CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
					pNeighborDisp->UpdateData();
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateLightmapExtents( void )
{
	// Get the parent face.
	CMapFace *pFace = ( CMapFace* )GetParent();
	if( !pFace )
		return;

	// Check for valid lightmap size and correct if need be.
	ValidLightmapSize();
}

//-----------------------------------------------------------------------------
// Purpose: Returns TRUE if the lightmap scale on this face is within the acceptable range.
//-----------------------------------------------------------------------------
bool CMapDisp::ValidLightmapSize( void )
{
	// Get the current face and lightmap scale.
	CMapFace *pFace = static_cast<CMapFace*>( GetParent() );
	if ( !pFace )
		return false;

	int nLightmapScale = pFace->texture.nLightmapScale;

	// Get the surface points.
	Vector vecPoints[4];
	for ( int iPoint = 0; iPoint < 4; ++iPoint )
	{
		GetSurfPoint( iPoint, vecPoints[iPoint] );
	}

	// Find the largest edge.
	float flMaxLength = 0.0f;
	for ( int iPoint = 0; iPoint < 4; ++iPoint )
	{
		float flLength = ( vecPoints[(iPoint+1)%4] - vecPoints[iPoint] ).Length();
		if ( flLength > flMaxLength )
		{
			flMaxLength = flLength;
		}
	}

	float flOOLightmapScale = 1.0f / static_cast<float>( nLightmapScale );
	float flSize = static_cast<float>( static_cast<int>( flMaxLength * flOOLightmapScale ) + 1 );
	if ( flSize > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER )
	{
		while ( flSize > MAX_DISP_LIGHTMAP_DIM_WITHOUT_BORDER )
		{
			nLightmapScale++;
			flOOLightmapScale = 1.0f / static_cast<float>( nLightmapScale );
			flSize = static_cast<float>( static_cast<int>( flMaxLength * flOOLightmapScale ) + 1 );
		}

		// Save the next to last.
		pFace->texture.nLightmapScale = nLightmapScale;

		// Re-calculate texture coordinates now.
		pFace->CalcTextureCoords();

		CFaceEditSheet *pSheet = GetMainWnd()->GetFaceEditSheet();
		if( pSheet )
		{
			pSheet->m_MaterialPage.UpdateDialogData();
		}
	}

	return true;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::EntityInBoundingBox( Vector const &vOrigin )
{
	Vector vMin, vMax;

	for( int axis = 0; axis < 3; axis++ )
	{
		vMin[axis] = m_BBox[0][axis] - OVERLAY_CHECK_BLOAT;
		vMax[axis] = m_BBox[1][axis] + OVERLAY_CHECK_BLOAT;
	}

	if( ( vOrigin.x < vMin.x ) || ( vOrigin.x > vMax.x ) ||
		( vOrigin.y < vMin.y ) || ( vOrigin.y > vMax.y ) ||
		( vOrigin.z < vMin.z ) || ( vOrigin.z > vMax.z ) )
		return false;

	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::CheckAndUpdateOverlays( bool bFull )
{
	CMapFace *pFace = ( CMapFace* )GetParent();
	if ( pFace )
	{
		CMapSolid *pSolid = ( CMapSolid* )pFace->GetParent();
		if ( pSolid )
		{
			if ( !bFull )
			{
				pSolid->PostUpdate(Notify_Rebuild);
			}
			else
			{
				pSolid->PostUpdate(Notify_Rebuild_Full);
			}
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpSample( int oldPower )
{
	//
	// allocate temporary memory to hold new displacement distances
	//
	int width = GetWidth();
	int height = GetHeight();

	float *dists = new float[height*width];
	float *alphas = new float[height*width];
	Vector *dispVectors = new Vector[height*width];
	Vector *subdivPositions = new Vector[height*width];
	Vector *subdivNormals = new Vector[height*width];

	if( !dists || !alphas || !dispVectors || !subdivPositions || !subdivNormals )
	{
		delete [] dists;
		delete [] alphas;
		delete [] dispVectors;
		delete [] subdivPositions;
		delete [] subdivNormals;
		return;
	}

	//
	// get old width and height
	//
	int oldWidth = ( ( 1 << oldPower ) + 1 );
	int oldHeight = ( ( 1 << oldPower ) + 1 );

	for( int oh = 0, nh = 0; oh < oldHeight; oh++, nh += 2 )
	{
		for( int ow = 0, nw = 0; ow < oldWidth; ow++, nw += 2 )
		{
			bool bRight = false;
			bool bUp = false;

			int oldIndex = oh * oldHeight + ow;
			int newIndex = nh * height + nw;

			int x = oldIndex % oldWidth;
			int y = oldIndex / oldHeight;

			float dist = GetFieldDistance( oldIndex );
			dists[newIndex] = dist;

			float alpha = GetAlpha( oldIndex );
			alphas[newIndex] = alpha;

			Vector dVector[2], subPVector[2], subNVector[2];
			GetFieldVector( oldIndex, dVector[0] );
			GetSubdivPosition( oldIndex, subPVector[0] );
			GetSubdivNormal( oldIndex, subNVector[0] );
			dispVectors[newIndex] = dVector[0];
			subdivPositions[newIndex] = subPVector[0];
			subdivNormals[newIndex] = subNVector[0];

			if( ( x + 1 ) < oldWidth )
			{
				dist = ( GetFieldDistance( oldIndex ) + GetFieldDistance( oldIndex + 1 ) ) * 0.5f;
				dists[newIndex+1] = dist;

				alpha = ( GetAlpha( oldIndex ) + GetAlpha( oldIndex + 1 ) ) * 0.5f;
				alphas[newIndex+1] = alpha;

				GetFieldVector( oldIndex, dVector[0] );
				GetFieldVector( oldIndex + 1, dVector[1] );
				dispVectors[newIndex+1] = ( dVector[0] + dVector[1] ) * 0.5f;

				GetSubdivPosition( oldIndex, subPVector[0] );
				GetSubdivPosition( oldIndex + 1, subPVector[1] );
				subdivPositions[newIndex+1] = ( subPVector[0] + subPVector[1] ) * 0.5f;

				GetSubdivNormal( oldIndex, subNVector[0] );
				GetSubdivNormal( oldIndex + 1, subNVector[1] );
				subdivNormals[newIndex+1] = ( subNVector[0] + subNVector[1] ) * 0.5f;

				bRight = true;
			}

			if( ( y + 1 ) < oldHeight )
			{
				dist = ( GetFieldDistance( oldIndex ) + GetFieldDistance( oldIndex + oldHeight ) ) * 0.5f;
				dists[newIndex+height] = dist;

				alpha = ( GetAlpha( oldIndex ) + GetAlpha( oldIndex + oldHeight ) ) * 0.5f;
				alphas[newIndex+height] = alpha;

				GetFieldVector( oldIndex, dVector[0] );
				GetFieldVector( oldIndex + oldHeight, dVector[1] );
				dispVectors[newIndex+height] = ( dVector[0] + dVector[1] ) * 0.5f;

				GetSubdivPosition( oldIndex, subPVector[0] );
				GetSubdivPosition( oldIndex + oldHeight, subPVector[1] );
				subdivPositions[newIndex+height] = ( subPVector[0] + subPVector[1] ) * 0.5f;

				GetSubdivNormal( oldIndex, subNVector[0] );
				GetSubdivNormal( oldIndex + oldHeight, subNVector[1] );
				subdivNormals[newIndex+height] = ( subNVector[0] + subNVector[1] ) * 0.5f;
				
				bUp = true;
			}

			if( bRight && bUp )
			{
				dist = ( GetFieldDistance( oldIndex + 1 ) + GetFieldDistance( oldIndex + oldHeight ) ) * 0.5f;
				dists[newIndex+height+1] = dist;

				alpha = ( GetAlpha( oldIndex + 1 ) + GetAlpha( oldIndex + oldHeight ) ) * 0.5f;
				alphas[newIndex+height+1] = alpha;

				GetFieldVector( oldIndex + 1, dVector[0] );
				GetFieldVector( oldIndex + oldHeight, dVector[1] );
				dispVectors[newIndex+height+1] = ( dVector[0] + dVector[1] ) * 0.5f;

				GetSubdivPosition( oldIndex + 1, subPVector[0] );
				GetSubdivPosition( oldIndex + oldHeight, subPVector[1] );
				subdivPositions[newIndex+height+1] = ( subPVector[0] + subPVector[1] ) * 0.5f;

				GetSubdivNormal( oldIndex + 1, subNVector[0] );
				GetSubdivNormal( oldIndex + oldHeight, subNVector[1] );
				subdivNormals[newIndex+height+1] = ( subNVector[0] + subNVector[1] ) * 0.5f;
			}
		}
	}

	//
	// copy sampled list
	//
	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		SetAlpha( i, alphas[i] );
		SetFieldVector( i, dispVectors[i] );
		SetFieldDistance( i, dists[i] );
		SetSubdivPosition( i, subdivPositions[i] );
		SetSubdivNormal( i, subdivNormals[i] );
	}

	//
	// free temporary memory
	//
	delete [] dists;
	delete [] alphas;
	delete [] dispVectors;
	delete [] subdivPositions;
	delete [] subdivNormals;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::SamplePoints( int index, int width, int height, bool *pValidPoints, 
							 float *pValue, float *pAlpha, Vector& newDispVector, 
							 Vector& newSubdivPos, Vector &newSubdivNormal )
{
	//
	// set initial sample values
	//
	Vector vField, vSPos, vSNormal;
	int value = GetFieldDistance( index );
	float alpha = GetAlpha( index );
	GetFieldVector( index, vField );
	GetSubdivPosition( index, vSPos );
	GetSubdivNormal( index, vSNormal );

	int count = 1;

	//
	// accumulate other sample values from around the given index
	//
	int ndx;
	Vector vTmp;
	for( int i = 0; i < 8; i++ )
	{
		if( !pValidPoints[i] )
			continue;

		switch( i )
		{
			case 0: { ndx = index - height - 1; break; }				// down and left
			case 1: { ndx = index - 1; break; }							// left
			case 2: { ndx = index + height - 1; break; }				// up and left
			case 3: { ndx = index + height; break; }					// up
			case 4: { ndx = index + height + 1; break; }				// up and right
			case 5: { ndx = index + 1; break; }							// right
			case 6: { ndx = index - height + 1; break; }				// down and right
			case 7: { ndx = index - height; break; }					// down
			default: continue;
		}

		value += GetFieldDistance( ndx );
		alpha += GetAlpha( ndx );

		GetFieldVector( ndx, vTmp );
		vField += vTmp;

		GetSubdivPosition( ndx, vTmp );
		vSPos += vTmp;

		GetSubdivNormal( ndx, vTmp );
		vSNormal += vTmp;

		// increment count
		count++;
	}

	// average
	*pValue = value / ( float )count;
	*pAlpha = alpha / ( float )count;
	newDispVector = vField / ( float )count;
	newSubdivPos = vSPos / ( float )count;
	newSubdivNormal = vSNormal / ( float )count;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::GetValidSamplePoints( int index, int width, int height, bool *pValidPoints )
{
	int x = index % width;
	int y = index / height;

	// down and left
	if( ( ( x - 1 ) >= 0 ) && ( ( y - 1 ) >= 0 ) ) { pValidPoints[0] = true; }

	// left
	if( ( x - 1 ) >= 0 ) { pValidPoints[1] = true; }

	// up and left
	if( ( ( x - 1 ) >= 0 ) && ( ( y + 1 ) < height ) ) { pValidPoints[2] = true; }

	// up
	if( ( y + 1 ) < height ) { pValidPoints[3] = true; }

	// up and right
	if( ( ( x + 1 ) < width ) && ( ( y + 1 ) < height ) ) { pValidPoints[4] = true; }

	// right
	if( ( x + 1 ) < width ) { pValidPoints[5] = true; }

	// down and right
	if( ( ( x + 1 ) < width ) && ( ( y - 1 ) >= 0 ) ) { pValidPoints[6] = true; }

	// down
	if( ( y - 1 ) >= 0 ) { pValidPoints[7] = true; }
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::DownSample( int oldPower )
{
	//
	// allocate temporary memory to hold new displacement distances
	//
	int width = GetWidth();
	int height = GetHeight();

	float *dists = new float[height*width];
	float *alphas = new float[height*width];
	Vector *dispVectors = new Vector[height*width];
	Vector *subdivPos = new Vector[height*width];
	Vector *subdivNormals = new Vector[height*width];

	if( !dists || !alphas || !dispVectors || !subdivPos || !subdivNormals )
	{
		delete [] dists;
		delete [] alphas;
		delete [] dispVectors;
		delete [] subdivPos;
		delete [] subdivNormals;
		return;
	}

	//
	// get old width and height
	//
	int oldWidth = ( ( 1 << oldPower ) + 1 );
	int oldHeight = ( ( 1 << oldPower ) + 1 );

	for( int oh = 0, nh = 0; oh < oldHeight; oh += 2, nh++ )
	{
		for( int ow = 0, nw = 0; ow < oldWidth; ow += 2, nw++ )
		{
			int oldIndex = oh * oldHeight + ow;
			int newIndex = nh * height + nw;

			//
			// clear valid point list and gather valid sample points
			//
			bool validPoints[8];
			for( int i = 0; i < 8; i++ ) { validPoints[i] = false; }
			GetValidSamplePoints( oldIndex, oldWidth, oldHeight, validPoints );

			//
			// sample the points, vector field vectors, and offset vectors
			//
			float newValue;
			float newAlpha;
			Vector newDispVector;
			Vector newSubdivPos;
			Vector newSubdivNormal;
			SamplePoints( oldIndex, oldWidth, oldHeight, validPoints, &newValue, &newAlpha, 
				          newDispVector, newSubdivPos, newSubdivNormal );

			//
			// save sampled values
			//
			dists[newIndex] = newValue;
			alphas[newIndex] = newAlpha;
			dispVectors[newIndex] = newDispVector;
			subdivPos[newIndex] = newSubdivPos;
			subdivNormals[newIndex] = newSubdivNormal;
		}
	}

	//
	// copy sampled list
	//
	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		SetAlpha( i, alphas[i] );
		SetFieldDistance( i, dists[i] );
		SetFieldVector( i, dispVectors[i] );
		SetSubdivPosition( i, subdivPos[i] );
		SetSubdivNormal( i, subdivNormals[i] );
	}

	//
	// free temporary memory
	//
	delete [] dists;
	delete [] alphas;
	delete [] dispVectors;
	delete [] subdivPos;
	delete [] subdivNormals;
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapDisp::InvertAlpha( void )
{
	int nVertCount = GetSize();
	for ( int iVert = 0; iVert < nVertCount; ++iVert )
	{
		float flAlpha = GetAlpha( iVert );
		float flInvAlpha = 255.0f - flAlpha;
		SetAlpha( iVert, flInvAlpha );
	}

	// Update the surface with new data.
	UpdateData();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Resample( int power )
{
	//
	// save old power for resampling, update to new power
	//
	int oldPower = GetPower();
	if( oldPower > power )
	{
		int delta = oldPower - power;
		for( int i = 0; i < delta; i++ )
		{
			SetPower( oldPower - ( i + 1 ) );
			DownSample( oldPower - i );
		}
	}
	else
	{
		int delta = power - oldPower;
		for( int i = 0; i < delta; i++ )
		{
			SetPower( oldPower + ( i + 1 ) );
			UpSample( oldPower + i );
		}
	}

	// update the surface with the new data
	UpdateData();
	CheckAndUpdateOverlays( true );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Elevate( float elevation )
{
	// set the new elevation
	SetElevation( elevation );

	// update the displacement
	UpdateData();
}

//-----------------------------------------------------------------------------
// Purpose: Resample a displacement map to be a clipped version of this surface.
//			Called when we split a face with a displacement surface.
//          NOTE: The new surface must be a quad as well, otherwise return false;
//	hBuilderDisp - The displacement surface to receive the new clipped data.
//-----------------------------------------------------------------------------
void CMapDisp::Split( EditDispHandle_t hBuilderDisp )
{
#define SPLIT_EPSILON 0.001f

	CMapDisp *pBuilderDisp = EditDispMgr()->GetDisp( hBuilderDisp );

	static Vector vecSurfPoints[4];
	for ( int iPoint = 0; iPoint < 4; ++iPoint )
	{
		GetSurfPoint( iPoint, vecSurfPoints[iPoint] );
	}

	// Prepare the destination surface for painting.
	pBuilderDisp->Paint_Init( DISPPAINT_CHANNEL_POSITION );	

	int nVertCount = pBuilderDisp->GetSize();
	for ( int iVert = 0; iVert < nVertCount; ++iVert )
	{
		Vector vecVert;
		pBuilderDisp->GetVert( iVert, vecVert );

		Vector2D vecDispUV;
		PointInQuadToBarycentric( vecSurfPoints[0], vecSurfPoints[3], vecSurfPoints[2], vecSurfPoints[1], vecVert, vecDispUV );

		// A little clean-up here.
		for ( int iComp = 0; iComp < 2; ++iComp )
		{
			vecDispUV[iComp] = clamp( vecDispUV[iComp], 0.0f, 1.0f );
		}

		Vector vecNewVert, vecNewNormal;
		float flNewAlpha;
		m_CoreDispInfo.DispUVToSurf( vecDispUV, vecNewVert, &vecNewNormal, &flNewAlpha );

		pBuilderDisp->SetAlpha( iVert, flNewAlpha );
		pBuilderDisp->Paint_SetValue(iVert, vecNewVert );
	}
	
	pBuilderDisp->Paint_Update( true );

#undef SPLIT_EPSILON
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::ComparePoints( const Vector& pt1, const Vector& pt2, const float tolerance )
{
	for( int i = 0 ; i < 3 ; i++ )
	{
		if( fabs( pt1[i] - pt2[i] ) > tolerance )
			return false;
	}
	
	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
float CMapDisp::CollideWithTriangles( const Vector& RayStart, const Vector& RayEnd, Tri_t *pTris, int triCount,
									  Vector& surfNormal )
{
	// create a ray
	Ray_t ray;
	ray.m_Start = RayStart;
	ray.m_Delta = RayEnd - RayStart;
	ray.m_IsRay = true;

	Vector vNormal;
	float minFraction = 1.0f;
	for( int ndxTri = 0; ndxTri < triCount; ndxTri++ )
	{
		Tri_t &tri = pTris[ndxTri];
		float fraction = IntersectRayWithTriangle( ray, tri.v[0], tri.v[2], tri.v[1], true );
		if( fraction == -1 )
			continue;

		if( fraction < minFraction )
		{
			minFraction = fraction;

			// calculate the triangle normal
			Vector edge1, edge2;
			VectorSubtract( tri.v[2], tri.v[0], edge1 );
			VectorSubtract( tri.v[1], tri.v[0], edge2 );
			CrossProduct( edge1, edge2, surfNormal );
			VectorNormalize( surfNormal );
		}
	}

	return minFraction;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::CreatePlanesFromBoundingBox( Plane_t *planes, const Vector& bbMin, const Vector& bbMax )
{
	for( int i = 0; i < 6; i++ )
	{
		VectorClear( planes[i].normal );
	}

	//
	// use pads to store minor axes
	//
	planes[0].normal[0] = -1;	planes[0].dist = -bbMin[0];
	planes[1].normal[0] = 1;	planes[1].dist = bbMax[0];

	planes[2].normal[1] = -1;	planes[2].dist = -bbMin[1];
	planes[3].normal[1] = 1;	planes[3].dist = bbMax[1];

	planes[4].normal[2] = -1;	planes[4].dist = -bbMin[2];
	planes[5].normal[2] = 1;	planes[5].dist = bbMax[2];
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::CollideWithBoundingBoxes( const Vector& rayStart, const Vector& rayEnd, 
										 BBox_t *pBBox, int bboxCount, Tri_t *pTris, int *triCount )
{
	const float DIST_EPSILON = 0.01f;

	//
	// collide against all bounding boxes
	//
	for( int i = 0; i < bboxCount; i++ )
	{
		//
		// make copy of vectors so they can be cut up
		//
		Vector start, end;
		start = rayStart;
		end = rayEnd;

		//
		// make planes for bbox
		//
		Plane_t planes[6];
		CreatePlanesFromBoundingBox( planes, pBBox[i].min, pBBox[i].max );

		//
		// collide against bounding box planes
		//
		int j;
		for( j = 0; j < 6; j++ )
		{
			float dist1 = DotProduct( planes[j].normal, start ) - planes[j].dist;
			float dist2 = DotProduct( planes[j].normal, end ) - planes[j].dist;
		
			//
			// entry intersection point - move ray start up to intersection
			//
			if( ( dist1 > DIST_EPSILON ) && ( dist2 < -DIST_EPSILON ) )
			{
				float fraction = ( dist1 / ( dist1 - dist2 ) );
				Vector segment, addOn;
				VectorSubtract( end, start, segment );
				VectorScale( segment, fraction, addOn );
				VectorNormalize( segment );
				VectorAdd( addOn, segment, addOn );
				VectorAdd( start, addOn, start );
			}
			else if( ( dist1 > DIST_EPSILON ) && ( dist2 > DIST_EPSILON ) )
			{
				break;
			}
		}

		//
		// collision add triangles to list
		//
		if( j == 6 )
		{
			// gross! shouldn't know value (64) and handle error better
			if( *triCount >= 256 )
			{
				// error!!!!!
				return;
			}

			int postSpacing = m_CoreDispInfo.GetPostSpacing();
			int index = i + ( i / ( postSpacing - 1 ) );

			m_CoreDispInfo.GetVert( index, pTris[*triCount].v[0] );
			m_CoreDispInfo.GetVert( index+postSpacing, pTris[*triCount].v[1] );
			m_CoreDispInfo.GetVert( index+1, pTris[*triCount].v[2] );
			*triCount += 1;

			m_CoreDispInfo.GetVert( index+1, pTris[*triCount].v[0] );
			m_CoreDispInfo.GetVert( index+postSpacing, pTris[*triCount].v[1] );
			m_CoreDispInfo.GetVert( index+postSpacing+1, pTris[*triCount].v[2] );
			*triCount += 1;
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::CreateBoundingBoxes( BBox_t *pBBox, int count, float bloat )
{
	//
	// initialize the bounding boxes
	//
	for( int i = 0; i < count; i++ )
	{
		VectorFill( pBBox[i].min, COORD_NOTINIT );
		VectorFill( pBBox[i].max, -COORD_NOTINIT );
	}

	// get the width and height of the displacement surface
	int postSpacing = m_CoreDispInfo.GetPostSpacing();

	//
	// find bounding box of every two consecutive triangles
	//
	int bboxIndex = 0;
	int index = 0;
	for( int i = 0; i < ( postSpacing - 1 ); i++ )
	{
		for( int j = 0; j < ( postSpacing - 1 ); j++ )
		{
			for( int k = 0; k < 4; k++ )
			{
				switch( k )
				{
					case 0: { index = ( postSpacing * i ) + j; break; }
					case 1: { index = ( postSpacing * ( i + 1 ) ) + j; break; }
					case 2: { index = ( postSpacing * i ) + ( j + 1 ); break; }
					case 3: { index = ( postSpacing * ( i + 1 ) ) + ( j + 1 ); break; }
				}

				Vector v;
				m_CoreDispInfo.GetVert( index, v );
				if( v[0] < pBBox[bboxIndex].min[0] ) { pBBox[bboxIndex].min[0] = v[0]; }
				if( v[1] < pBBox[bboxIndex].min[1] ) { pBBox[bboxIndex].min[1] = v[1]; }
				if( v[2] < pBBox[bboxIndex].min[2] ) { pBBox[bboxIndex].min[2] = v[2]; }
				
				if( v[0] > pBBox[bboxIndex].max[0] ) { pBBox[bboxIndex].max[0] = v[0]; }
				if( v[1] > pBBox[bboxIndex].max[1] ) { pBBox[bboxIndex].max[1] = v[1]; }
				if( v[2] > pBBox[bboxIndex].max[2] ) { pBBox[bboxIndex].max[2] = v[2]; }
			}

			// bloat all the boxes a little
			for( int axis = 0; axis < 3; axis++ )
			{
				pBBox[bboxIndex].min[axis] -= bloat;
				pBBox[bboxIndex].max[axis] += bloat;
			}

			bboxIndex++;
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose:
// NOTE: Performance, look into it here.  This is doing way more work than
//       necessary.  We should probably update a collision representation, a
//       simple one at least whenever we update a displacement and use it as
//       a first level cull here.  But for now....it works...ship, ship, ship.
//-----------------------------------------------------------------------------
bool CMapDisp::TraceLine( Vector &vecHitPos, Vector &vecHitNormal, Vector const &vecRayStart, Vector const &vecRayEnd )
{
	// Just do the slow thing for now.
	float flFraction;
	int iTri = CollideWithDispTri( vecRayStart, vecRayEnd, flFraction );
	if ( iTri == -1 )
		return false;

	// Get hit position and normal.
	Vector vecRay = vecRayEnd - vecRayStart;
	vecRay = vecRay * flFraction;
	vecHitPos = vecRayStart + vecRay;

	Vector vecTriPoints[3];
	GetTriPos( iTri, vecTriPoints[0], vecTriPoints[1], vecTriPoints[2] );
	Vector vecEdge1 = vecTriPoints[2] - vecTriPoints[0];
	Vector vecEdge2 = vecTriPoints[1] - vecTriPoints[0];
	vecHitNormal = CrossProduct( vecEdge1, vecEdge2 );
	VectorNormalize( vecHitNormal );

	return true;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::TraceLineSnapTo( Vector &HitPos, Vector &HitNormal, 
							    Vector const &RayStart, Vector const &RayEnd )
{
#define LOWER_TOLERANCE		-0.1f
#define UPPER_TOLERANCE		1.1f

	// get width and height
	int width = GetWidth();
	int height = GetHeight();

	// build the ray
	Ray_t ray;
	ray.m_Start = RayStart;
	ray.m_Delta = RayEnd - RayStart;
	ray.m_IsRay = true;

	float u, v;
	Tri_t tri;

	// test edge 0
	for( int ndx = 0; ndx < ( width - 1 ); ndx++ )
	{
		GetVert( ndx, tri.v[0] );
		GetVert( ndx + width, tri.v[1] );
		GetVert( ndx + 1, tri.v[2] );

		ComputeIntersectionBarycentricCoordinates( ray, tri.v[0], tri.v[1], tri.v[2], u, v );
		
		// along edge (0.0 < v < 1.0) and below (u < 0.0)
		if( ( v >= LOWER_TOLERANCE ) && ( v <= UPPER_TOLERANCE ) && ( u < 0.0f ) )
		{
			v = clamp( v, 0.0f, 1.0f );

			// snap u (u = 0.0)
			HitPos = tri.v[0] + ( tri.v[2] - tri.v[0] ) * v; 
			return true;
		}

		// special corner 0
		if( ( ndx == 0 ) && ( v < 0.0f ) && ( u < 0.0f ) )
		{
			HitPos = tri.v[0];
			return true;
		}
	}

	// test edge 1
	for( int ndx = 0; ndx < ( height - 1 ); ndx++ )
	{
		GetVert( ndx * width, tri.v[0] );
		GetVert( ( ndx * width )+ width, tri.v[1] );
		GetVert( ( ndx * width ) + 1, tri.v[2] );

		ComputeIntersectionBarycentricCoordinates( ray, tri.v[0], tri.v[1], tri.v[2], u, v );
		
		// along edge (0.0 < u < 1.0) and left (v < 0.0)
		if( ( u >= LOWER_TOLERANCE ) && ( u <= UPPER_TOLERANCE ) && ( v < 0.0f ) )
		{
			u = clamp( u, 0.0f, 1.0f );

			// snap v (v = 0.0)
			HitPos = tri.v[0] + ( tri.v[1] - tri.v[0] ) * u; 
			return true;
		}	

		// special corner 1
		if( ( ndx == ( height - 2 ) ) && ( u > 1.0f ) && ( v < 0.0f ) )
		{
			HitPos = tri.v[1];
			return true;
		}
	}

	// test edge 2
	for( int ndx = 0; ndx < ( width - 1 ); ndx++ )
	{
		GetVert( ( ( height - 1 ) * width ) + ndx + 1, tri.v[0] );
		GetVert( ( ( height - 2 ) * width ) + ndx + 1, tri.v[1] );
		GetVert( ( ( height - 1 ) * width ) + ndx, tri.v[2] );

		ComputeIntersectionBarycentricCoordinates( ray, tri.v[0], tri.v[1], tri.v[2], u, v );
		
		// along edge (0.0 < v < 1.0) and above (u < 0.0)
		if( ( v >= LOWER_TOLERANCE ) && ( v <= UPPER_TOLERANCE ) && ( u < 0.0f ) )
		{
			v = clamp( v, 0.0f, 1.0f );

			// snap u (u = 0.0)
			HitPos = tri.v[0] + ( tri.v[2] - tri.v[0] ) * v; 
			return true;
		}

		// special corner 2
		if( ( ndx == ( width - 2 ) ) && ( v < 0.0f ) && ( u < 0.0f ) )
		{
			HitPos = tri.v[0];
			return true;
		}
	}

	// test edge 3
	for( int ndx = 0; ndx < ( height - 1 ); ndx++ )
	{
		GetVert( ( ndx * width ) + ( ( 2 * width ) - 1 ), tri.v[0] );
		GetVert( ( ndx * width ) + ( width - 1 ), tri.v[1] );
		GetVert( ( ndx * width ) + ( ( 2 * width ) - 2 ), tri.v[2] );

		ComputeIntersectionBarycentricCoordinates( ray, tri.v[0], tri.v[1], tri.v[2], u, v );
		
		// along edge (0.0 < u < 1.0) and right (v < 0.0)
		if( ( u >= LOWER_TOLERANCE ) && ( u <= UPPER_TOLERANCE ) && ( v < 0.0f ) )
		{
			u = clamp( u, 0.0f, 1.0f );

			// snap v (v = 0.0)
			HitPos = tri.v[0] + ( tri.v[1] - tri.v[0] ) * u; 
			return true;
		}	

		// special corner 3
		if( ( ndx == 0 ) && ( u > 1.0f ) && ( v < 0.0f ) )
		{
			HitPos = tri.v[1];
			return true;
		}
	}

	return false;

#undef LOWER_TOLERANCE
#undef UPPER_TOLERANCE
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Flip( int flipType )
{
	int width = GetWidth();
	int height = GetHeight();

	switch( flipType )
	{
		case FLIP_HORIZONTAL:
		{
			return;
		}
		case FLIP_VERTICAL:
		{
			return;
		}
		case FLIP_TRANSPOSE:
		{
			for( int ndxHeight = 0; ndxHeight < height; ndxHeight++ )
			{
				for( int ndxWidth = ndxHeight; ndxWidth < width; ndxWidth++ )
				{
					float dist1 = GetFieldDistance( ( ndxHeight * width ) + ndxWidth );
					float dist2 = GetFieldDistance( ( ndxWidth * height ) + ndxHeight );
					SetFieldDistance( ( ndxHeight * width ) + ndxWidth, dist2 );
					SetFieldDistance( ( ndxWidth * height ) + ndxHeight, dist1 );

					Vector v1, v2;
					GetFieldVector( ( ndxHeight * width ) + ndxWidth, v1 );
					GetFieldVector( ( ndxWidth * height ) + ndxHeight, v2 );
					SetFieldVector( ( ndxHeight * width ) + ndxWidth, v2 );
					SetFieldVector( ( ndxWidth * height ) + ndxHeight, v1 );

					GetSubdivPosition( ( ndxHeight * width ) + ndxWidth, v1 );
					GetSubdivPosition( ( ndxWidth * height ) + ndxHeight, v2 );
					SetSubdivPosition( ( ndxHeight * width ) + ndxWidth, v2 );
					SetSubdivPosition( ( ndxWidth * height ) + ndxHeight, v1 );

					GetSubdivNormal( ( ndxHeight * width ) + ndxWidth, v1 );
					GetSubdivNormal( ( ndxWidth * height ) + ndxHeight, v2 );
					SetSubdivNormal( ( ndxHeight * width ) + ndxWidth, v2 );
					SetSubdivNormal( ( ndxWidth * height ) + ndxHeight, v1 );

					float alpha1 = GetAlpha( ( ndxHeight * width ) + ndxWidth );
					float alpha2 = GetAlpha( ( ndxWidth * height ) + ndxHeight );
					SetAlpha( ( ndxHeight * width ) + ndxWidth, alpha2 );
					SetAlpha( ( ndxWidth * height ) + ndxHeight, alpha1 );
				}
			}
			return;
		}
		default:
		{
			return;
		}
	}
}



//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateWalkable( void )
{
	// Set the walkable tag.
	int nTriCount = GetTriCount();
	for ( int iTri = 0; iTri < nTriCount; ++iTri )
	{
		Vector v1, v2, v3;
		GetTriPos( iTri, v1, v2, v3 );

		Vector vecEdge1, vecEdge2;
		vecEdge1 = v2 - v1;
		vecEdge2 = v3 - v1;

		Vector vecTriNormal;
		CrossProduct( vecEdge2, vecEdge1, vecTriNormal );
		VectorNormalize( vecTriNormal );

		ResetTriTag( iTri, COREDISPTRI_TAG_WALKABLE );
		if ( vecTriNormal.z >= WALKABLE_NORMAL_VALUE )
		{
			SetTriTag( iTri, COREDISPTRI_TAG_WALKABLE );
		}
	}

	// Create the walkable render list.
	m_aWalkableVerts.RemoveAll();
	m_aWalkableIndices.RemoveAll();
	m_aForcedWalkableIndices.RemoveAll();

	for ( int iTri = 0; iTri < nTriCount; ++iTri )
	{
		if ( !IsTriWalkable( iTri ) )
		{
			unsigned short triIndices[3];
			unsigned short newTriIndices[3];
			GetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] );

			newTriIndices[0] = m_aWalkableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[0] ) );
			newTriIndices[1] = m_aWalkableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[1] ) );
			newTriIndices[2] = m_aWalkableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[2] ) );

			if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_WALKABLE_BIT ) )
			{
				m_aForcedWalkableIndices.AddToTail( newTriIndices[0] );
				m_aForcedWalkableIndices.AddToTail( newTriIndices[1] );
				m_aForcedWalkableIndices.AddToTail( newTriIndices[2] );
			}
			else
			{
				m_aWalkableIndices.AddToTail( newTriIndices[0] );
				m_aWalkableIndices.AddToTail( newTriIndices[1] );
				m_aWalkableIndices.AddToTail( newTriIndices[2] );
			}
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateBuildable( void )
{
	// Set the buildable tag.
	int nTriCount = GetTriCount();
	for ( int iTri = 0; iTri < nTriCount; ++iTri )
	{
		Vector v1, v2, v3;
		GetTriPos( iTri, v1, v2, v3 );

		Vector vecEdge1, vecEdge2;
		vecEdge1 = v2 - v1;
		vecEdge2 = v3 - v1;

		Vector vecTriNormal;
		CrossProduct( vecEdge2, vecEdge1, vecTriNormal );
		VectorNormalize( vecTriNormal );

		ResetTriTag( iTri, COREDISPTRI_TAG_BUILDABLE );
		if ( vecTriNormal.z >= BUILDABLE_NORMAL_VALUE )
		{
			SetTriTag( iTri, COREDISPTRI_TAG_BUILDABLE );
		}
	}

	// Create the buildable render list.
	m_aBuildableVerts.RemoveAll();
	m_aBuildableIndices.RemoveAll();
	m_aForcedBuildableIndices.RemoveAll();

	for ( int iTri = 0; iTri < nTriCount; ++iTri )
	{
		if ( !IsTriBuildable( iTri ) )
		{
			unsigned short triIndices[3];
			unsigned short newTriIndices[3];
			GetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] );

			newTriIndices[0] = m_aBuildableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[0] ) );
			newTriIndices[1] = m_aBuildableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[1] ) );
			newTriIndices[2] = m_aBuildableVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[2] ) );

			if ( IsTriTag( iTri, COREDISPTRI_TAG_FORCE_BUILDABLE_BIT ) )
			{
				m_aForcedBuildableIndices.AddToTail( newTriIndices[0] );
				m_aForcedBuildableIndices.AddToTail( newTriIndices[1] );
				m_aForcedBuildableIndices.AddToTail( newTriIndices[2] );
			}
			else
			{
				m_aBuildableIndices.AddToTail( newTriIndices[0] );
				m_aBuildableIndices.AddToTail( newTriIndices[1] );
				m_aBuildableIndices.AddToTail( newTriIndices[2] );
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapDisp::UpdateTriRemove( void )
{
	// Create the remove render list.
	m_aRemoveVerts.RemoveAll();
	m_aRemoveIndices.RemoveAll();

	int nTriCount = GetTriCount();
	for ( int iTri = 0; iTri < nTriCount; ++iTri )
	{
		if ( IsTriRemove( iTri ) )
		{
			unsigned short triIndices[3];
			unsigned short newTriIndices[3];
			GetTriIndices( iTri, triIndices[0], triIndices[1], triIndices[2] );

			newTriIndices[0] = m_aRemoveVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[0] ) );
			newTriIndices[1] = m_aRemoveVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[1] ) );
			newTriIndices[2] = m_aRemoveVerts.AddToTail( m_CoreDispInfo.GetDispVert( triIndices[2] ) );

			m_aRemoveIndices.AddToTail( newTriIndices[0] );
			m_aRemoveIndices.AddToTail( newTriIndices[1] );
			m_aRemoveIndices.AddToTail( newTriIndices[2] );
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CMapDisp::CreateShoreOverlays( CMapFace *pFace, Shoreline_t *pShoreline )
{
	// Do the bounds volumes intersect?
	Vector vecDispMin, vecDispMax;
	GetBoundingBox( vecDispMin, vecDispMax );
	Vector vecSolidMin, vecSolidMax;
	CMapSolid *pSolid = static_cast<CMapSolid*>( pFace->GetParent() );
	pSolid->GetCullBox( vecSolidMin, vecSolidMax );
	if ( !IsBoxIntersectingBox( vecDispMin, vecDispMax, vecSolidMin, vecSolidMax ) )
		return;

	int nTriangleCount = TriangleCount();
	for ( int iTri = 0; iTri < nTriangleCount; ++iTri )
	{
		unsigned short i[3];
		GetTriIndices( iTri, i[0], i[1], i[2] );
		Vector v[3];
		GetVert( i[0], v[0] );
		GetVert( i[1], v[1] );
		GetVert( i[2], v[2] );
		
		Vector vU, vV;
		VectorSubtract( v[1], v[0], vU );
		VectorSubtract( v[2], v[0], vV );

		Vector2D vecIntersect[2];
		Vector4D plane;
		plane.Init( pFace->plane.normal.x, pFace->plane.normal.y, pFace->plane.normal.z, pFace->plane.dist );
		int nCount = IntersectTriangleWithPlaneBarycentric( v[0], vU, vV, plane, vecIntersect );
		if ( nCount != 2 )
			continue;

		// Find the normal pointing toward the shore.
		Vector vecPoints[2];
		vecPoints[0] = v[0] + ( vU * vecIntersect[0].x ) + ( vV * vecIntersect[0].y );
		vecPoints[1] = v[0] + ( vU * vecIntersect[1].x ) + ( vV * vecIntersect[1].y );

		// Create shore edge normal.
		Vector vecEdge, vecNormal;
		VectorSubtract( vecPoints[1], vecPoints[0], vecEdge );
		VectorNormalize( vecEdge );
		CrossProduct( vecEdge, pFace->plane.normal, vecNormal );
		float flEdgeDist = DotProduct( vecNormal, vecPoints[0] );

		for ( int iVert = 0; iVert < 3; ++iVert )
		{
			float flDist = DotProduct( vecNormal, v[iVert] ) - flEdgeDist;
			if ( flDist > 0.0f )
			{
				float flDist2 = DotProduct( pFace->plane.normal, v[iVert] ) - pFace->plane.dist;
				if ( flDist2 < 0.0f )
				{
					vecNormal.Negate();
					break;
				}
			}
		}

		if ( !VectorsAreEqual( vecPoints[0], vecPoints[1], 0.1f ) )
		{
			pShoreline->AddSegment( vecPoints[0], vecPoints[1], vecNormal, pFace->plane.dist, pFace, GetEditHandle() );
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void RenderDisplacementNormals( CCoreDispInfo& coreDispInfo, int numVerts )
{
	Vector points[4], normal;

	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh* pMesh = pRenderContext->GetDynamicMesh();

	meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts );

    for( int i = 0; i < numVerts; i++ )
    {
		coreDispInfo.GetVert( i, points[0] );
		coreDispInfo.GetNormal( i, normal );

	    meshBuilder.Color3f( 0.0f, 1.0f, 0.0f );
        meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] );
		meshBuilder.AdvanceVertex();

		meshBuilder.Color3f( 0.0f, 1.0f, 0.0f );
        meshBuilder.Position3f( points[0][0] + ( normal[0] * 10.0f ), 
								points[0][1] + ( normal[1] * 10.0f ), 
								points[0][2] + ( normal[2] * 10.0f ) );
		meshBuilder.AdvanceVertex();
    }
	meshBuilder.End();
	pMesh->Draw();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void RenderDisplacementTangentsS( CCoreDispInfo &coreDispInfo, int numVerts )
{
	Vector points[4], tangentS;

	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();

	meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts );

	for( int i = 0; i < numVerts; i++ )
	{
		coreDispInfo.GetVert( i, points[0] );
		coreDispInfo.GetTangentS( i, tangentS );

	    meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
        meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] );
		meshBuilder.AdvanceVertex();

		meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
        meshBuilder.Position3f( points[0][0] + ( tangentS[0] * 10.0f ), 
			                    points[0][1] + ( tangentS[1] * 10.0f ), 
					            points[0][2] + ( tangentS[2] * 10.0f ) );
		meshBuilder.AdvanceVertex();
	}

	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void RenderDisplacementTangentsT( CCoreDispInfo &coreDispInfo, int numVerts )
{
	Vector points[4], tangentT;

	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();

	meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts );

	for( int i = 0; i < numVerts; i++ )
	{
		coreDispInfo.GetVert( i, points[0] );
		coreDispInfo.GetTangentT( i, tangentT );

	    meshBuilder.Color3f( 0.0f, 0.0f, 1.0f );
        meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] );
		meshBuilder.AdvanceVertex();

		meshBuilder.Color3f( 0.0f, 0.0f, 1.0f );
        meshBuilder.Position3f( points[0][0] + ( tangentT[0] * 10.0f ), 
			                    points[0][1] + ( tangentT[1] * 10.0f ), 
					            points[0][2] + ( tangentT[2] * 10.0f ) );
		meshBuilder.AdvanceVertex();
	}

	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void RenderFaceVertexNormals( CCoreDispInfo& coreDispInfo )
{
	Vector points[4], normal;

	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();
	meshBuilder.Begin( pMesh, MATERIAL_LINES, 4 );

	CCoreDispSurface *pSurf = coreDispInfo.GetSurface();
    for( int i = 0; i < 4; i++ )
    {
		pSurf->GetPoint( i, points[0] );
		pSurf->GetPointNormal( i, normal );

		meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
        meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] );
		meshBuilder.AdvanceVertex();

		meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
        meshBuilder.Position3f( points[0][0] + ( normal[0] * 25.0f ), 
								points[0][1] + ( normal[1] * 25.0f ), 
								points[0][2] + ( normal[2] * 25.0f ) );
		meshBuilder.AdvanceVertex();
    }
	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void RenderDisplacementVectorField( CCoreDispInfo& coreDispInfo, int numVerts )
{
	Vector points[4], normal;

	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();
	meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts );

	for( int i = 0; i < numVerts; i++ )
	{
		coreDispInfo.GetVert( i, points[0] );
		coreDispInfo.GetFieldVector( i, normal );

		meshBuilder.Color3f( 1.0f, 1.0f, 0.0f );
		meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] );
		meshBuilder.AdvanceVertex();

		meshBuilder.Color3f( 1.0f, 1.0f, 0.0f );
		meshBuilder.Position3f( points[0][0] + ( normal[0] * 50.0f ),
								points[0][1] + ( normal[1] * 50.0f ),
								points[0][2] + ( normal[2] * 50.0f ) );
		meshBuilder.AdvanceVertex();
	}
	meshBuilder.End();
	pMesh->Draw();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void RenderSubdivPositions( CCoreDispInfo& coreDispInfo, int numVerts )
{
	Vector pt, normal;

	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();
	meshBuilder.Begin( pMesh, MATERIAL_LINES, numVerts );

	for( int i = 0;  i < numVerts; i++ )
	{
		coreDispInfo.GetFlatVert( i, pt );
		coreDispInfo.GetSubdivPosition( i, normal );

		meshBuilder.Position3f( pt[0], pt[1], pt[2] );
		meshBuilder.Color3f( 1.0f, 0.0f, 1.0f );
		meshBuilder.AdvanceVertex();

		meshBuilder.Position3f( pt[0] + normal[0], pt[1] + normal[1], pt[2] + normal[2] );
		meshBuilder.Color3f( 1.0f, 0.0f, 1.0f );
		meshBuilder.AdvanceVertex();
	}
	meshBuilder.End();
	pMesh->Draw();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
static void RenderDisplacementEdges( CCoreDispInfo& coreDispInfo )
{
 	Vector points[4];

	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();
	meshBuilder.Begin( pMesh, MATERIAL_LINES, 4 );

	CCoreDispSurface *pSurf = coreDispInfo.GetSurface();
	pSurf->GetPoint( 0, points[0] );
	pSurf->GetPoint( 1, points[1] );
	pSurf->GetPoint( 2, points[2] );
	pSurf->GetPoint( 3, points[3] );
	
	meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
	meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] );
	meshBuilder.AdvanceVertex();
	meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
	meshBuilder.Position3f( points[1][0], points[1][1], points[1][2] );
	meshBuilder.AdvanceVertex();
	
	meshBuilder.Color3f( 0.0f, 1.0f, 0.0f );
	meshBuilder.Position3f( points[1][0], points[1][1], points[1][2] );
	meshBuilder.AdvanceVertex();
	meshBuilder.Color3f( 0.0f, 1.0f, 0.0f );
	meshBuilder.Position3f( points[2][0], points[2][1], points[2][2] );
	meshBuilder.AdvanceVertex();
    
	meshBuilder.Color3f( 0.0f, 0.0f, 1.0f );
	meshBuilder.Position3f( points[2][0], points[2][1], points[2][2] );
	meshBuilder.AdvanceVertex();
	meshBuilder.Color3f( 0.0f, 0.0f, 1.0f );
	meshBuilder.Position3f( points[3][0], points[3][1], points[3][2] );
	meshBuilder.AdvanceVertex();
	
	meshBuilder.Color3f( 1.0f, 0.0f, 1.0f );
	meshBuilder.Position3f( points[3][0], points[3][1], points[3][2] );
	meshBuilder.AdvanceVertex();
	meshBuilder.Color3f( 1.0f, 0.0f, 1.0f );
	meshBuilder.Position3f( points[0][0], points[0][1], points[0][2] );
	meshBuilder.AdvanceVertex();

	meshBuilder.End();
	pMesh->Draw();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::RenderDisAllowedVerts( CRender3D *pRender )
{
	CBitVec<MAX_DISPVERTS> &allowedVerts = m_CoreDispInfo.GetAllowedVerts();

	int nVertCount = GetSize();
	for ( int iVert = 0; iVert < nVertCount; ++iVert )
	{
		if ( allowedVerts.Get( iVert ) == 0 )
		{
			Vector vecPos;
			GetVert( iVert, vecPos );

			// Draw a box at this point!
			Vector vecPointMin, vecPointMax;
			for ( int iAxis = 0; iAxis < 3; ++iAxis )
			{
				vecPointMin[iAxis] = vecPos[iAxis] - 5.0f;
				vecPointMax[iAxis] = vecPos[iAxis] + 5.0f;
			}
			pRender->RenderBox( vecPointMin, vecPointMax, 255, 0, 255, SELECT_NONE );
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Render3DDebug( CRender3D *pRender, bool isSelected )
{
#if 0
	pRender->SetRenderMode( RENDER_MODE_WIREFRAME );
	RenderDisplacementNormals( m_CoreDispInfo, MAPDISP_MAX_VERTS );
	RenderDisplacementTangentsS( m_CoreDispInfo, MAPDISP_MAX_VERTS );
	RenderDisplacementTangentsT( m_CoreDispInfo, MAPDISP_MAX_VERTS );

//	RenderFaceVertexNormals( m_CoreDispInfo );
	RenderDisplacementVectorField( m_CoreDispInfo, MAPDISP_MAX_VERTS );
	RenderSubdivPositions( m_CoreDispInfo, GetSize() );
//	RenderDisplacementEdges( m_CoreDispInfo );
#endif
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::CalcColor( CRender3D *pRender, bool bIsSelected, 
						  SelectionState_t faceSelectionState,
						  Color &pColor )
{
	// Get the current render mode.
	EditorRenderMode_t renderMode = pRender->GetCurrentRenderMode();
	
	switch ( renderMode )
	{
		case RENDER_MODE_TEXTURED:
		case RENDER_MODE_TEXTURED_SHADED:
		case RENDER_MODE_LIGHT_PREVIEW2:
		case RENDER_MODE_LIGHT_PREVIEW_RAYTRACED:
		{
			break;
		}
		case RENDER_MODE_SELECTION_OVERLAY:
		{
			if ( faceSelectionState == SELECT_MULTI_PARTIAL )
			{
				pColor[2] = 100;
				pColor[3] = 64;
			}
			else if ( ( faceSelectionState == SELECT_NORMAL ) || bIsSelected )
			{
				SelectFaceColor( pColor );
				pColor[3] = 64;
			}
			break;
		}
		case RENDER_MODE_LIGHTMAP_GRID:
		{
			CMapFace *pFace = ( CMapFace* )GetParent();
			if ( bIsSelected )
			{
				SelectFaceColor( pColor );
			}
			else if (pFace->texture.nLightmapScale > DEFAULT_LIGHTMAP_SCALE)
			{
				pColor[0] = 150;
			}
			else if (pFace->texture.nLightmapScale < DEFAULT_LIGHTMAP_SCALE)
			{
				pColor[2] = 100;
			}
			break;
		}
		case RENDER_MODE_TRANSLUCENT_FLAT:
		case RENDER_MODE_FLAT:
		{
			if ( bIsSelected )
			{
				SelectFaceColor( pColor );
			}
			
			break;
		}
		case RENDER_MODE_WIREFRAME:
		{
			if ( bIsSelected )
			{
				SelectEdgeColor( pColor );
			}
			break;
		}
		case RENDER_MODE_SMOOTHING_GROUP:
		{			
			// Render the non-smoothing group faces in white, yellow for the others.
			CMapDoc *pDoc = CMapDoc::GetActiveMapDoc();
			if ( pDoc )
			{
				CMapFace *pFace = ( CMapFace* )GetParent();
				int iGroup = pDoc->GetSmoothingGroupVisual();
				if ( pFace->InSmoothingGroup( iGroup ) )
				{
					pColor[2] = 0;
				}
			}
			
			break;
		}
		default:
		{
			assert( 0 );
			break;
		}
	}
}

void CMapDisp::Render2D(CRender2D *pRender, bool bIsSelected, SelectionState_t faceSelectionState )
{
	pRender->PushRenderMode( RENDER_MODE_WIREFRAME );

	pRender->DrawDisplacement( &m_CoreDispInfo );

	pRender->PopRenderMode();
}

void CMapDisp::AddShadowingTriangles( CUtlVector<Vector> &tri_list )
{
	// add lighting preview triangles
	CoreDispVert_t *pVert = m_CoreDispInfo.GetDispVertList();
	unsigned short *pIndex = m_CoreDispInfo.GetRenderIndexList();
	int numIndices = m_CoreDispInfo.GetRenderIndexCount();
	for ( int i = 0; i < numIndices; i += 3 )
	{
		for( int v = 0; v < 3; v++ )
			tri_list.AddToTail( pVert[pIndex[i+v]].m_Vert );
	}
}

//-----------------------------------------------------------------------------
// NOTE: most of the rendering mode is set in the parent face render call!!!
//-----------------------------------------------------------------------------
void CMapDisp::Render3D( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState )
{	
    // Get the current rendermode.
    EditorRenderMode_t renderMode = pRender->GetCurrentRenderMode();

	if ( renderMode == RENDER_MODE_SELECTION_OVERLAY )
	{
		RenderOverlaySurface( pRender, bIsSelected, faceSelectionState );
	}
	else
	{
		RenderSurface( pRender, bIsSelected, faceSelectionState );

		// Note: This will cause the wireframe to render twice in selection due to
		//       the multiplass operations at the solid and face levels (the render
		//       portion of the hammer code needs to be reworked there).
		if ( renderMode != RENDER_MODE_WIREFRAME && bIsSelected )
		{
			// This renders wireframe twice in selection!
			RenderWireframeSurface( pRender, bIsSelected, faceSelectionState );
		}
	}

	// Note: the rendermode == textured is so that this only gets rendered
	//       once per frame.
	bool bDispWalkableMode = CMapDoc::GetActiveMapDoc()->IsDispDrawWalkable();
	if ( bDispWalkableMode && RenderingModeIsTextured(renderMode))
	{
		RenderWalkableSurface( pRender, bIsSelected, faceSelectionState );
	}

	// Note: the rendermode == textured is so that this only gets rendered
	//       once per frame.
	bool bDispBuildableMode = CMapDoc::GetActiveMapDoc()->IsDispDrawBuildable();
	if ( bDispBuildableMode && RenderingModeIsTextured( renderMode ))
	{
		RenderBuildableSurface( pRender, bIsSelected, faceSelectionState );
	}

	// Note: the rendermode == textured is so that this only gets rendered
	//       once per frame.
	bool bDispRemoveMode = CMapDoc::GetActiveMapDoc()->IsDispDrawRemove();
	if ( bDispRemoveMode && RenderingModeIsTextured( renderMode ))
	{
		RenderRemoveSurface( pRender, bIsSelected, faceSelectionState );
	}

	bool bDispRemovedVertMode = CMapDoc::GetActiveMapDoc()->IsDispDrawRemovedVerts();
	if ( bDispRemovedVertMode && RenderingModeIsTextured( renderMode ) )
	{
		RenderDisAllowedVerts( pRender );
	}

	// Render debug information.
//	Render3DDebug( pRender, bIsSelected );
}

//-----------------------------------------------------------------------------
// Purpose: Render the displacement surface.
//-----------------------------------------------------------------------------
void CMapDisp::RenderOverlaySurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState )
{
	if ( HasSelectMask() )
		return;

	Color color( 255, 255, 255, 255 );
	CalcColor( pRender, bIsSelected, faceSelectionState, color );

	int nVertCount = m_CoreDispInfo.GetSize();
	int nIndexCount = m_CoreDispInfo.GetRenderIndexCount();
	
	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();
	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount );
	
	CoreDispVert_t *pVert = m_CoreDispInfo.GetDispVertList();
	for (int i = 0; i < nVertCount; ++i )
	{
		meshBuilder.Position3fv( pVert[i].m_Vert.Base() );
		meshBuilder.Color4ub( color[0], color[1], color[2], color[3] );
		meshBuilder.Normal3fv( pVert[i].m_Normal.Base() );
		meshBuilder.AdvanceVertex();
	}
	
	unsigned short *pIndex = m_CoreDispInfo.GetRenderIndexList();
	for ( int i = 0; i < nIndexCount; ++i )
	{
		meshBuilder.Index( pIndex[i] );
		meshBuilder.AdvanceIndex();
	}
	
	meshBuilder.End();
	pMesh->Draw();
}

//-----------------------------------------------------------------------------
// Purpose: Render the displacement surface with a vertex alpha (blending).
//-----------------------------------------------------------------------------
void CMapDisp::RenderSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState )
{
	Color color( 255, 255, 255, 255 );
	CalcColor( pRender, bIsSelected, faceSelectionState, color );

	int numVerts = m_CoreDispInfo.GetSize();
	int numIndices = m_CoreDispInfo.GetRenderIndexCount();

	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();
	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numVerts,	numIndices );
	
	CoreDispVert_t *pVert = m_CoreDispInfo.GetDispVertList();
	for (int i = 0; i < numVerts; ++i )
	{
		meshBuilder.Position3fv( pVert[i].m_Vert.Base() );
		meshBuilder.Color4ub( color[0], color[1], color[2], 255 - ( unsigned char )( pVert[i].m_Alpha ) );
		meshBuilder.Normal3fv( pVert[i].m_Normal.Base() );
		meshBuilder.TangentS3fv( pVert[i].m_TangentS.Base() );
		meshBuilder.TangentT3fv( pVert[i].m_TangentT.Base() );
		meshBuilder.TexCoord2fv( 0, pVert[i].m_TexCoord.Base() );
		meshBuilder.TexCoord2fv( 1, pVert[i].m_LuxelCoords[0].Base() );
		meshBuilder.AdvanceVertex();
	}
	
	unsigned short *pIndex = m_CoreDispInfo.GetRenderIndexList();
	int nTriCount = numIndices / 3;
	for ( int nTri = 0; nTri < nTriCount; ++nTri )
	{
		if ( !IsTriRemove( nTri ) )
		{
			int nIndex = nTri * 3;
			meshBuilder.Index( pIndex[nIndex] );
			meshBuilder.AdvanceIndex();
			meshBuilder.Index( pIndex[nIndex+1] );
			meshBuilder.AdvanceIndex();
			meshBuilder.Index( pIndex[nIndex+2] );
			meshBuilder.AdvanceIndex();
		}
	}

	meshBuilder.End();
	pMesh->Draw();
}

//-----------------------------------------------------------------------------
// Purpose: Render the displacement surface with walkable data.
//-----------------------------------------------------------------------------
void CMapDisp::RenderWalkableSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState )
{
	// Normal
	for ( int iPass = 0; iPass < 2; ++iPass )
	{
		Color color;
		if ( iPass == 0 )
		{
			pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT );
			color.SetColor( 255, 255, 0, 64 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		else
		{
			pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
			color.SetColor( 255, 255, 0, 255 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		
		int nVertCount = m_aWalkableVerts.Count();
		int nIndexCount = m_aWalkableIndices.Count();

		CMeshBuilder meshBuilder;
		CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
		IMesh *pMesh = pRenderContext->GetDynamicMesh();
		meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount );
		
		CoreDispVert_t **ppVerts = m_aWalkableVerts.Base();
		for (int i = 0; i < nVertCount; ++i )
		{
			CoreDispVert_t *pVert = ppVerts[i];
			
			meshBuilder.Position3fv( pVert->m_Vert.Base() );
			meshBuilder.Color4ub( color[0], color[1], color[2], color[3] );
			meshBuilder.Normal3fv( pVert->m_Normal.Base() );
			meshBuilder.AdvanceVertex();
		}
		
		unsigned short *pIndex = m_aWalkableIndices.Base();
		for ( int i = 0; i < nIndexCount; ++i )
		{
			meshBuilder.Index( pIndex[i] );
			meshBuilder.AdvanceIndex();
		}
		
		meshBuilder.End();
		pMesh->Draw();

		pRender->PopRenderMode();
	}

	// Forced
	for ( int iPass = 0; iPass < 2; ++iPass )
	{
		Color color;
		if ( iPass == 0 )
		{
			pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT );
			color.SetColor( 0, 255, 0, 64 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		else
		{
			pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
			color.SetColor( 0, 255, 0, 255 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		
		int nVertCount = m_aWalkableVerts.Count();
		int nIndexCount = m_aForcedWalkableIndices.Count();

		CMeshBuilder meshBuilder;
		CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
		IMesh *pMesh = pRenderContext->GetDynamicMesh();
		meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount );
		
		CoreDispVert_t **ppVerts = m_aWalkableVerts.Base();
		for (int i = 0; i < nVertCount; ++i )
		{
			CoreDispVert_t *pVert = ppVerts[i];
			
			meshBuilder.Position3fv( pVert->m_Vert.Base() );
			meshBuilder.Color4ub( color[0], color[1], color[2], color[3] );
			meshBuilder.Normal3fv( pVert->m_Normal.Base() );
			meshBuilder.AdvanceVertex();
		}
		
		unsigned short *pIndex = m_aForcedWalkableIndices.Base();
		for ( int i = 0; i < nIndexCount; ++i )
		{
			meshBuilder.Index( pIndex[i] );
			meshBuilder.AdvanceIndex();
		}
		
		meshBuilder.End();
		pMesh->Draw();

		pRender->PopRenderMode();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Render the displacement surface with buildable data.
//-----------------------------------------------------------------------------
void CMapDisp::RenderBuildableSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState )
{
    // Normal
	for ( int iPass = 0; iPass < 2; ++iPass )
	{
		Color color;
		if ( iPass == 0 )
		{
			pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT );
			color.SetColor( 255, 100, 25, 64 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		else
		{
			pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
			color.SetColor( 255, 255, 0, 255 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		
		int nVertCount = m_aBuildableVerts.Count();
		int nIndexCount = m_aBuildableIndices.Count();

		CMeshBuilder meshBuilder;
		CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
		IMesh *pMesh = pRenderContext->GetDynamicMesh();
		meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount );
		
		CoreDispVert_t **ppVerts = m_aBuildableVerts.Base();
		for (int i = 0; i < nVertCount; ++i )
		{
			CoreDispVert_t *pVert = ppVerts[i];
			
			meshBuilder.Position3fv( pVert->m_Vert.Base() );
			meshBuilder.Color4ub( color[0], color[1], color[2], color[3] );
			meshBuilder.Normal3fv( pVert->m_Normal.Base() );
			meshBuilder.AdvanceVertex();
		}
		
		unsigned short *pIndex = m_aBuildableIndices.Base();
		for ( int i = 0; i < nIndexCount; ++i )
		{
			meshBuilder.Index( pIndex[i] );
			meshBuilder.AdvanceIndex();
		}
		
		meshBuilder.End();
		pMesh->Draw();

		pRender->PopRenderMode();
	}

	// Forced
	for ( int iPass = 0; iPass < 2; ++iPass )
	{
		Color color;
		if ( iPass == 0 )
		{
			pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT );
			color.SetColor( 0, 0, 255, 64 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		else
		{
			pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
			color.SetColor( 0, 0, 255, 255 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		
		int nVertCount = m_aBuildableVerts.Count();
		int nIndexCount = m_aForcedBuildableIndices.Count();

		CMeshBuilder meshBuilder;
		CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
		IMesh *pMesh = pRenderContext->GetDynamicMesh();
		meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount );
		
		CoreDispVert_t **ppVerts = m_aBuildableVerts.Base();
		for (int i = 0; i < nVertCount; ++i )
		{
			CoreDispVert_t *pVert = ppVerts[i];
			
			meshBuilder.Position3fv( pVert->m_Vert.Base() );
			meshBuilder.Color4ub( color[0], color[1], color[2], color[3] );
			meshBuilder.Normal3fv( pVert->m_Normal.Base() );
			meshBuilder.AdvanceVertex();
		}
		
		unsigned short *pIndex = m_aForcedBuildableIndices.Base();
		for ( int i = 0; i < nIndexCount; ++i )
		{
			meshBuilder.Index( pIndex[i] );
			meshBuilder.AdvanceIndex();
		}
		
		meshBuilder.End();
		pMesh->Draw();

		pRender->PopRenderMode();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Render the displacement surface removed faces.
//-----------------------------------------------------------------------------
void CMapDisp::RenderRemoveSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState )
{
	// Remove Faces
	for ( int iPass = 0; iPass < 2; ++iPass )
	{
		Color color;
		if ( iPass == 0 )
		{
			pRender->PushRenderMode( RENDER_MODE_TRANSLUCENT_FLAT );
			color.SetColor( 0, 0, 255, 64 );
			CalcColor( pRender, false, faceSelectionState, color );
		}
		else
		{
			pRender->PushRenderMode( RENDER_MODE_WIREFRAME );
			color.SetColor( 0, 0, 255, 255 );
			CalcColor( pRender, false, faceSelectionState, color );
		}

		int nVertCount = m_aRemoveVerts.Count();
		int nIndexCount = m_aRemoveIndices.Count();

		CMeshBuilder meshBuilder;
		CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
		IMesh *pMesh = pRenderContext->GetDynamicMesh();
		meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, nVertCount, nIndexCount );

		CoreDispVert_t **ppVerts = m_aRemoveVerts.Base();
		for (int i = 0; i < nVertCount; ++i )
		{
			CoreDispVert_t *pVert = ppVerts[i];

			meshBuilder.Position3fv( pVert->m_Vert.Base() );
			meshBuilder.Color4ub( color[0], color[1], color[2], color[3] );
			meshBuilder.Normal3fv( pVert->m_Normal.Base() );
			meshBuilder.AdvanceVertex();
		}

		unsigned short *pIndex = m_aRemoveIndices.Base();
		for ( int i = 0; i < nIndexCount; ++i )
		{
			meshBuilder.Index( pIndex[i] );
			meshBuilder.AdvanceIndex();
		}

		meshBuilder.End();
		pMesh->Draw();

		pRender->PopRenderMode();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Render the white wireframe overlay.
//-----------------------------------------------------------------------------
void CMapDisp::RenderWireframeSurface( CRender3D *pRender, bool bIsSelected, SelectionState_t faceSelectionState )
{
	if ( HasGridMask() )
		return;

    pRender->PushRenderMode( RENDER_MODE_WIREFRAME );

	Color color( 255, 255, 255, 255 );
	CalcColor( pRender, bIsSelected, faceSelectionState, color );
	
	int numVerts = m_CoreDispInfo.GetSize();
	int numIndices = m_CoreDispInfo.GetRenderIndexCount();
	
	CMeshBuilder meshBuilder;
	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	IMesh *pMesh = pRenderContext->GetDynamicMesh();
	meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numVerts,	numIndices );
	
	CoreDispVert_t *pVert = m_CoreDispInfo.GetDispVertList();
	for (int i = 0; i < numVerts; ++i )
	{
		meshBuilder.Position3fv( pVert[i].m_Vert.Base() );
		meshBuilder.Color3ub( 255, 255, 255 );
		meshBuilder.TexCoord2fv( 0, pVert[i].m_TexCoord.Base() );
		meshBuilder.TexCoord2fv( 1, pVert[i].m_LuxelCoords[0].Base() );
		meshBuilder.AdvanceVertex();
	}
	
	unsigned short *pIndex = m_CoreDispInfo.GetRenderIndexList();
	for ( int i = 0; i < numIndices; ++i )
	{
		meshBuilder.Index( pIndex[i] );
		meshBuilder.AdvanceIndex();
	}
	
	meshBuilder.End();
	pMesh->Draw();

	// Reset the render mode.
	pRender->PopRenderMode();
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateNeighborDependencies( bool bDestroy )
{
	if( !bDestroy )
	{
		// reset and find new neighbors
		ResetNeighbors();
		FindNeighbors();
	}
	else
	{
		//
		// update edge neighbors
		//
		for( int i = 0; i < 4; i++ )
		{
			EditDispHandle_t handle = GetEdgeNeighbor( i );
			if( handle == EDITDISPHANDLE_INVALID )
				continue;

			CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
			pNeighborDisp->UpdateNeighborDependencies( false );
		}
		
		//
		// update corner neighbors
		//
		for( int i = 0; i < 4; i++ )
		{
			int cornerCount = GetCornerNeighborCount( i );
			for( int j = 0; j < cornerCount; j++ )
			{
				EditDispHandle_t handle = GetCornerNeighbor( i, j );
				if( handle != EDITDISPHANDLE_INVALID )
				{
					CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
					pNeighborDisp->UpdateNeighborDependencies( false );
				}
			}
		}
	}
}

void CMapDisp::UpdateNeighborsOfDispsIntersectingBox( const Vector &bbMin, const Vector &bbMax, float flPadding )
{
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( !pDispMgr )
		return;

	Vector bbPaddedMin = bbMin - Vector( flPadding, flPadding, flPadding );
	Vector bbPaddedMax = bbMax + Vector( flPadding, flPadding, flPadding );

	int count = pDispMgr->WorldCount();
	for ( int i=0; i < count; i++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromWorld( i );
		
		// Do the bbox test.
		Vector testbbmin, testbbmax;
		pDisp->GetBoundingBox( testbbmin, testbbmax );
		if ( QuickBoxIntersectTest( testbbmin, testbbmax, bbPaddedMin, bbPaddedMax ) )
		{
			pDisp->ResetNeighbors();
			pDispMgr->FindWorldNeighbors( pDisp->GetEditHandle() );
		}
	}	
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::FindNeighbors( void )
{
	//
	// find the current neighbors to "this" displacement
	//
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( !pDispMgr )
		return;

	pDispMgr->FindWorldNeighbors( m_EditHandle );

	//
	// generate the vector field for neighboring surfaces (edges and corners)
	//
	for( int i = 0; i < NUM_EDGES_CORNERS; i++ )
	{
		EditDispHandle_t handle = m_EdgeNeighbors[i];
		if( handle != EDITDISPHANDLE_INVALID )
		{
			CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
			pNeighborDisp->ResetNeighbors();
			pDispMgr->FindWorldNeighbors( pNeighborDisp->GetEditHandle() );
		}

		int cornerCount = m_CornerNeighborCounts[i];
		if( cornerCount != 0 )
		{
			for( int j = 0; j < cornerCount; j++ )
			{
				handle = m_CornerNeighbors[i][j];
				if( handle != EDITDISPHANDLE_INVALID )
				{
					CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
					pNeighborDisp->ResetNeighbors();
					pDispMgr->FindWorldNeighbors( pNeighborDisp->GetEditHandle() );
				}
			}
		}
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::UpdateBoundingBox( void )
{
	Vector v;

	VectorFill( m_BBox[0], COORD_NOTINIT );
	VectorFill( m_BBox[1], -COORD_NOTINIT );

	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		m_CoreDispInfo.GetVert( i, v );
		
		if( v[0] < m_BBox[0][0] ) { m_BBox[0][0] = v[0]; }
		if( v[1] < m_BBox[0][1] ) { m_BBox[0][1] = v[1]; }
		if( v[2] < m_BBox[0][2] ) { m_BBox[0][2] = v[2]; }

		if( v[0] > m_BBox[1][0] ) { m_BBox[1][0] = v[0]; }
		if( v[1] > m_BBox[1][1] ) { m_BBox[1][1] = v[1]; }
		if( v[2] > m_BBox[1][2] ) { m_BBox[1][2] = v[2]; }
	}
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Scale( float scale )
{
	// check for a change in scale
	if( scale == m_Scale )
		return;

	int size = GetSize();

	// scale the surface back to its original state and re-scale with the new
	// value
	if( m_Scale != 1.0f )
	{
		float adj = 1.0f / m_Scale;

		for( int i = 0; i < size; i++ )
		{
			// scale the vector field distance
			float dist = GetFieldDistance( i );
			dist *= adj;
			SetFieldDistance( i, dist );

			// scale the subdivision pos
			Vector vPos;
			GetSubdivPosition( i, vPos );
			vPos *= adj;
			SetSubdivPosition( i, vPos );
		}
	}

	for( int i = 0; i < size; i++ )
	{
		// scale the vector field distance
		float dist = GetFieldDistance( i );
		dist *= scale;
		SetFieldDistance( i, dist );
		
		// scale the subdivision pos
		Vector vPos;
		GetSubdivPosition( i, vPos );
		vPos *= scale;
		SetSubdivPosition( i, vPos );
	}

	m_Scale = scale;

	UpdateData();
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::ApplyNoise( float min, float max, float rockiness )
{
	if( min == max )
		return;

	// initialize the paint data
	Paint_Init( DISPPAINT_CHANNEL_POSITION );

	//
	// clamp rockiness value between 0.0 and 1.0
	//
	if( rockiness < 0.0f ) { rockiness = 0.0f; }
	if( rockiness > 1.0f ) { rockiness = 1.0f; }

	float delta = max - min;
	float deltaBy2 = delta / 2.0f;

	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		//
		// get a noise value based on the points position
		//
		Vector v;
		GetVert( i, v );

		float noiseX = v.x + v.z;
		float noiseY = v.y + v.z;
		float noise = PerlinNoise2D( noiseX, noiseY, rockiness );

		//
		// clamp noise (can go a little higher and lower due to precision)
		//
		if( noise < -1.0f ) { noise = -1.0f; }
		if( noise > 1.0f ) { noise = 1.0f; }

		noise *= deltaBy2;
		noise += ( deltaBy2 + min );

		// apply noise to the subdivision normal direction
		Vector vNoise;
		
		GetFieldVector( i, vNoise );
		if( ( vNoise.x == 0 ) && ( vNoise.y == 0 ) && ( vNoise.z == 0 ) )
		{
			GetSubdivNormal( i, vNoise );
		}
		vNoise *= noise;
		vNoise += v;

		// set the paint value
		Paint_SetValue( i, vNoise );
	}

	Paint_Update( false );
}


//=============================================================================
//
// Load/Save Functions
//


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::PostLoad( void )
{
	Vector v;

	//
	// check the subdivision normals -- clean them up (old files)
	//
	bool bUpdateSubdivNormals = false;

	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		GetSubdivNormal( i, v );
		if( ( v.x == 0.0f ) && ( v.y == 0.0f ) && ( v.z == 0.0f ) )
		{
			bUpdateSubdivNormals = true;
			break;
		}
	}

	if( bUpdateSubdivNormals )
	{
		Vector vNormal;
		GetSurfNormal( vNormal );

		for( int i = 0; i < size; i++ )
		{
			SetSubdivNormal( i, vNormal );
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFile - 
//			*pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispDistancesCallback(CChunkFile *pFile, CMapDisp *pDisp)
{
	return(pFile->ReadChunk((KeyHandler_t)LoadDispDistancesKeyCallback, pDisp));
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : szKey - 
//			szValue - 
//			pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispDistancesKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
	float dispDistance;

	if (!strnicmp(szKey, "row", 3))
	{
		char szBuf[MAX_KEYVALUE_LEN];
		strcpy(szBuf, szValue);

		int nCols = (1 << pDisp->GetPower()) + 1;
		int nRow = atoi(&szKey[3]);

		char *pszNext = strtok(szBuf, " ");
		int nIndex = nRow * nCols;

		while (pszNext != NULL)
		{
			dispDistance = (float)atof(pszNext);
			pDisp->m_CoreDispInfo.SetFieldDistance( nIndex, dispDistance );
			pszNext = strtok(NULL, " ");
			nIndex++;
		}
	}

	return(ChunkFile_Ok);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFile - 
//			*pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispOffsetsCallback(CChunkFile *pFile, CMapDisp *pDisp)
{
	return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetsKeyCallback, pDisp));
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : szKey - 
//			szValue - 
//			pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispOffsetsKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
	Vector subdivVector;

	if( !strnicmp( szKey, "row", 3 ) )
	{
		char szBuf[MAX_KEYVALUE_LEN];
		strcpy( szBuf, szValue );

		int nCols = ( 1 << pDisp->GetPower() ) + 1;
		int nRow = atoi( &szKey[3] );

		char *pszNext0 = strtok( szBuf, " " );
		char *pszNext1 = strtok( NULL, " " );
		char *pszNext2 = strtok( NULL, " " );

		int nIndex = nRow * nCols;

		while( ( pszNext0 != NULL ) && ( pszNext1 != NULL ) && ( pszNext2 != NULL ) )
		{
			subdivVector[0] = ( float )atof( pszNext0 );
			subdivVector[1] = ( float )atof( pszNext1 );
			subdivVector[2] = ( float )atof( pszNext2 );

			pDisp->m_CoreDispInfo.SetSubdivPosition( nIndex, subdivVector );

			pszNext0 = strtok( NULL, " " );
			pszNext1 = strtok( NULL, " " );
			pszNext2 = strtok( NULL, " " );

			nIndex++;
		}
	}

	return( ChunkFile_Ok );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFile - 
//			*pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispOffsetNormalsCallback(CChunkFile *pFile, CMapDisp *pDisp)
{
	return(pFile->ReadChunk((KeyHandler_t)LoadDispOffsetNormalsKeyCallback, pDisp ));
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : szKey - 
//			szValue - 
//			pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispOffsetNormalsKeyCallback(const char *szKey, const char *szValue, 
															 CMapDisp *pDisp)
{
	Vector normalVector;

	if( !strnicmp( szKey, "row", 3 ) )
	{
		char szBuf[MAX_KEYVALUE_LEN];
		strcpy( szBuf, szValue );

		int nCols = ( 1 << pDisp->GetPower() ) + 1;
		int nRow = atoi( &szKey[3] );

		char *pszNext0 = strtok( szBuf, " " );
		char *pszNext1 = strtok( NULL, " " );
		char *pszNext2 = strtok( NULL, " " );

		int nIndex = nRow * nCols;

		while( ( pszNext0 != NULL ) && ( pszNext1 != NULL ) && ( pszNext2 != NULL ) )
		{
			normalVector[0] = ( float )atof( pszNext0 );
			normalVector[1] = ( float )atof( pszNext1 );
			normalVector[2] = ( float )atof( pszNext2 );

			pDisp->m_CoreDispInfo.SetSubdivNormal( nIndex, normalVector );

			pszNext0 = strtok( NULL, " " );
			pszNext1 = strtok( NULL, " " );
			pszNext2 = strtok( NULL, " " );

			nIndex++;
		}
	}

	return( ChunkFile_Ok );
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : szKey - 
//			szValue - 
//			pWorld - 
// Output : 
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
	if (!stricmp(szKey, "power"))
	{
		int	power;
		CChunkFile::ReadKeyValueInt( szValue, power );
		pDisp->SetPower( power );
	}
	else if (!stricmp(szKey, "uaxis"))
	{
		Vector mapAxis;
		CChunkFile::ReadKeyValueVector3( szValue, mapAxis );
		pDisp->SetHasMappingAxes( true );
		pDisp->m_MapAxes[0] = mapAxis;
	}
	else if (!stricmp(szKey, "vaxis"))
	{
		Vector mapAxis;
		CChunkFile::ReadKeyValueVector3( szValue, mapAxis );
		pDisp->SetHasMappingAxes( true );
		pDisp->m_MapAxes[1] = mapAxis;
	}
	else if( !stricmp( szKey, "startposition" ) )
	{
		Vector startPosition;
		CChunkFile::ReadKeyValueVector3( szValue, startPosition );
		CCoreDispSurface *pSurf = pDisp->m_CoreDispInfo.GetSurface();
		pSurf->SetPointStart( startPosition );
	}
	else if (!stricmp(szKey, "flags"))
	{
		int	nFlags;
		CChunkFile::ReadKeyValueInt( szValue, nFlags );
		pDisp->SetFlags( nFlags );
	}
#if 0
	else if (!stricmp(szKey, "mintess"))
	{
		int	minTess;
		CChunkFile::ReadKeyValueInt( szValue, minTess );
		pDisp->SetMinTess( minTess );
	}
	else if (!stricmp(szKey, "smooth"))
	{
		float smoothingAngle;
		CChunkFile::ReadKeyValueFloat( szValue, smoothingAngle );
		pDisp->SetSmoothingAngle( smoothingAngle );
	}
	else if( !stricmp( szKey, "alpha" ) )
	{
		Vector4D alphaValues;
		CChunkFile::ReadKeyValueVector4( szValue, alphaValues );

		for( int i = 0; i < 4; i++ )
		{
			pDisp->m_CoreDispInfo.SetSurfPointAlpha( i, alphaValues[i] );
		}
	}
#endif
	else if( !stricmp( szKey, "elevation" ) )
	{
		float elevation;
		CChunkFile::ReadKeyValueFloat( szValue, elevation );
		pDisp->SetElevation( elevation );
	}
	else if( !stricmp( szKey, "subdiv" ) )
	{
		int bSubdivided;
		CChunkFile::ReadKeyValueInt( szValue, bSubdivided );
		bool bSubdiv = ( bSubdivided != 0 );
		pDisp->SetSubdivided( bSubdiv );
	}

	return(ChunkFile_Ok);
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFile - 
//			*pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispAlphasCallback(CChunkFile *pFile, CMapDisp *pDisp)
{
	return(pFile->ReadChunk((KeyHandler_t)LoadDispAlphasKeyCallback, pDisp));
}


//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFile - 
//			*pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispAlphasKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
	float alpha;

	if (!strnicmp(szKey, "row", 3))
	{
		char szBuf[MAX_KEYVALUE_LEN];
		strcpy(szBuf, szValue);

		int nCols = (1 << pDisp->GetPower()) + 1;
		int nRow = atoi(&szKey[3]);

		char *pszNext = strtok(szBuf, " ");

		int nIndex = nRow * nCols;

		while (pszNext != NULL) 
		{
			alpha = (float)atof(pszNext);

			pDisp->m_CoreDispInfo.SetAlpha( nIndex, alpha );

			pszNext = strtok(NULL, " ");

			nIndex++;
		}
	}

	return(ChunkFile_Ok);
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispTriangleTagsCallback(CChunkFile *pFile, CMapDisp *pDisp)
{
	return(pFile->ReadChunk((KeyHandler_t)LoadDispTriangleTagsKeyCallback, pDisp));
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispTriangleTagsKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
	unsigned short nTriTag;

	if ( !strnicmp( szKey, "row", 3 ) )
	{
		char szBuf[MAX_KEYVALUE_LEN];
		strcpy( szBuf, szValue );

		int nCols = ( 1 << pDisp->GetPower() );
		int nRow = atoi( &szKey[3] );

		char *pszNext = strtok( szBuf, " " );

		int nIndex = nRow * nCols;
		int iTri = nIndex * 2;

		while ( pszNext != NULL ) 
		{
			nTriTag = ( unsigned int )atoi( pszNext );
			pDisp->m_CoreDispInfo.SetTriTagValue( iTri, nTriTag );
			pszNext = strtok( NULL, " " );
			iTri++;
		}
	}

	return( ChunkFile_Ok );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispAllowedVertsCallback( CChunkFile *pFile, CMapDisp *pDisp )
{
	return( pFile->ReadChunk( ( KeyHandler_t )LoadDispAllowedVertsKeyCallback, pDisp ) );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispAllowedVertsKeyCallback( const char *szKey, const char *szValue, CMapDisp *pDisp )
{
	if ( !strnicmp( szKey, "10", 2 ) )
	{
		char szBuf[MAX_KEYVALUE_LEN];
		strcpy( szBuf, szValue );
		
		int iValue = 0;

		char *pszNext = strtok( szBuf, " " );
		while ( pszNext != NULL ) 
		{
			unsigned int nValue = ( unsigned int )atoi( pszNext );
			unsigned long ulValue = ( unsigned long )nValue;
			pDisp->m_CoreDispInfo.AllowedVerts_SetDWord( iValue, ulValue );
			pszNext = strtok( NULL, " " );
			iValue++;
		}
	}

	return( ChunkFile_Ok );
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFile - 
//			*pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispNormalsCallback(CChunkFile *pFile, CMapDisp *pDisp)
{
	return(pFile->ReadChunk((KeyHandler_t)LoadDispNormalsKeyCallback, pDisp));
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *szKey - 
//			*szValue - 
//			*pDisp - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadDispNormalsKeyCallback(const char *szKey, const char *szValue, CMapDisp *pDisp)
{
	Vector vectorFieldVector;

	if (!strnicmp(szKey, "row", 3))
	{
		char szBuf[MAX_KEYVALUE_LEN];
		strcpy(szBuf, szValue);

		int nCols = (1 << pDisp->GetPower()) + 1;
		int nRow = atoi(&szKey[3]);

		char *pszNext0 = strtok(szBuf, " ");
		char *pszNext1 = strtok(NULL, " ");
		char *pszNext2 = strtok(NULL, " ");

		int nIndex = nRow * nCols;

		while ((pszNext0 != NULL) && (pszNext1 != NULL) && (pszNext2 != NULL))
		{
			vectorFieldVector[0] = (float)atof(pszNext0);
			vectorFieldVector[1] = (float)atof(pszNext1);
			vectorFieldVector[2] = (float)atof(pszNext2);

			pDisp->m_CoreDispInfo.SetFieldVector( nIndex, vectorFieldVector );

			pszNext0 = strtok(NULL, " ");
			pszNext1 = strtok(NULL, " ");
			pszNext2 = strtok(NULL, " ");

			nIndex++;
		}
	}

	return(ChunkFile_Ok);
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  : *pFile - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::LoadVMF(CChunkFile *pFile)
{
	//
	// Set up handlers for the subchunks that we are interested in.
	//
	CChunkHandlerMap Handlers;
	Handlers.AddHandler("normals", (ChunkHandler_t)LoadDispNormalsCallback, this);
	Handlers.AddHandler("distances", (ChunkHandler_t)LoadDispDistancesCallback, this);
	Handlers.AddHandler("offsets", (ChunkHandler_t)LoadDispOffsetsCallback, this);
	Handlers.AddHandler("offset_normals", (ChunkHandler_t)LoadDispOffsetNormalsCallback, this);
	Handlers.AddHandler("alphas", (ChunkHandler_t)LoadDispAlphasCallback, this);
	Handlers.AddHandler("triangle_tags", (ChunkHandler_t)LoadDispTriangleTagsCallback, this );
	Handlers.AddHandler("allowed_verts", (ChunkHandler_t)LoadDispAllowedVertsCallback, this );

	pFile->PushHandlers(&Handlers);
	ChunkFileResult_t eResult = pFile->ReadChunk((KeyHandler_t)LoadDispKeyCallback, this);
	pFile->PopHandlers();

	return(eResult);
}


//-----------------------------------------------------------------------------
// Purpose: Saves the displacement info into a special chunk in the MAP file.
// Input  : *pFile - 
// Output : ChunkFileResult_t
//-----------------------------------------------------------------------------
ChunkFileResult_t CMapDisp::SaveVMF(CChunkFile *pFile, CSaveInfo *pSaveInfo)
{
	ChunkFileResult_t eResult = pFile->BeginChunk("dispinfo");

	int power = GetPower();
	float elevation = GetElevation();
	int nFlags = GetFlags();

	Vector	startPosition;
	CCoreDispSurface *pSurf = m_CoreDispInfo.GetSurface();
	pSurf->GetPoint( 0, startPosition );

	int bSubdivided = ( int )IsSubdivided();

#if 0	// old
	Vector4D	alphaValues;
	for( int i = 0; i < 4; i++ )
	{
		alphaValues[i] = m_CoreDispInfo.GetSurfPointAlpha( i );
	}
#endif

	if (eResult == ChunkFile_Ok)
	{
		eResult = pFile->WriteKeyValueInt("power", power);
	}

#if 0	// old
	if (eResult == ChunkFile_Ok)
	{
		eResult = pFile->WriteKeyValueVector3("uaxis", m_BDSurf.uAxis);
	}

	if (eResult == ChunkFile_Ok)
	{
		eResult = pFile->WriteKeyValueVector3("vaxis", m_BDSurf.vAxis);
	}
#endif

	if( eResult == ChunkFile_Ok )
	{
		eResult = pFile->WriteKeyValueVector3( "startposition", startPosition );
	}

	if (eResult == ChunkFile_Ok)
	{
		eResult = pFile->WriteKeyValueInt("flags", nFlags );
	}

#if 0
	// old
	if (eResult == ChunkFile_Ok)
	{
		eResult = pFile->WriteKeyValueInt("mintess", minTess);
	}

	if (eResult == ChunkFile_Ok)
	{
		eResult = pFile->WriteKeyValueFloat("smooth", smoothingAngle);
	}
	//
	// save the corner alpha values
	//
	if( eResult == ChunkFile_Ok )
	{
		eResult = pFile->WriteKeyValueVector4( "alpha", alphaValues );
	}
#endif

	if( eResult == ChunkFile_Ok )
	{
		eResult = pFile->WriteKeyValueFloat( "elevation", elevation );
	}

	if (eResult == ChunkFile_Ok)
	{
		eResult = pFile->WriteKeyValueInt( "subdiv", bSubdivided );
	}

    //
    // Save displacement map normals.
    //
	if (eResult == ChunkFile_Ok)
	{
		Vector vectorFieldVector;

		eResult = pFile->BeginChunk("normals");
		if (eResult == ChunkFile_Ok)
		{
			char szBuf[MAX_KEYVALUE_LEN];
			char szTemp[80];

			int nRows = (1 << power) + 1;;
			int nCols = nRows;
			
			for (int nRow = 0; nRow < nRows; nRow++)
			{
				bool bFirst = true;
				szBuf[0] = '\0';

				for (int nCol = 0; nCol < nCols; nCol++)
				{
					int nIndex = nRow * nCols + nCol;

					if (!bFirst)
					{
						strcat(szBuf, " ");
					}

					bFirst = false;
					m_CoreDispInfo.GetFieldVector( nIndex, vectorFieldVector );
					sprintf(szTemp, "%g %g %g", (double)vectorFieldVector[0], (double)vectorFieldVector[1], (double)vectorFieldVector[2]);
					strcat(szBuf, szTemp);
				}

				char szKey[10];
				sprintf(szKey, "row%d", nRow);
				eResult = pFile->WriteKeyValue(szKey, szBuf);
			}
		}

		if (eResult == ChunkFile_Ok)
		{
			eResult = pFile->EndChunk();
		}
	}

    //
    // Save displacement map distances.
	//
	if (eResult == ChunkFile_Ok)
	{
		float dispDistance;

		eResult = pFile->BeginChunk("distances");
		if (eResult == ChunkFile_Ok)
		{
			char szBuf[MAX_KEYVALUE_LEN];
			char szTemp[80];

			int nRows = (1 << power) + 1;
			int nCols = nRows;

			for (int nRow = 0; nRow < nRows; nRow++)
			{
				bool bFirst = true;
				szBuf[0] = '\0';

				for (int nCol = 0; nCol < nCols; nCol++)
				{
					int nIndex = nRow * nCols + nCol;

					if (!bFirst)
					{
						strcat(szBuf, " ");
					}

					bFirst = false;
					dispDistance = m_CoreDispInfo.GetFieldDistance( nIndex ); 
					sprintf(szTemp, "%g", (double)dispDistance);
					strcat(szBuf, szTemp);
				}

				char szKey[10];
				sprintf(szKey, "row%d", nRow);
				eResult = pFile->WriteKeyValue(szKey, szBuf);
			}
		}

		if (eResult == ChunkFile_Ok)
		{
			eResult = pFile->EndChunk();
		}
	}

    //
    // Save displacement map offset.
	//
	if (eResult == ChunkFile_Ok)
	{
		Vector subdivPos;

		eResult = pFile->BeginChunk( "offsets" );
		if( eResult == ChunkFile_Ok )
		{
			char szBuf[MAX_KEYVALUE_LEN];
			char szTemp[80];

			int nRows = (1 << power) + 1;
			int nCols = nRows;

			for (int nRow = 0; nRow < nRows; nRow++)
			{
				bool bFirst = true;
				szBuf[0] = '\0';

				for (int nCol = 0; nCol < nCols; nCol++)
				{
					int nIndex = nRow * nCols + nCol;

					if (!bFirst)
					{
						strcat(szBuf, " ");
					}

					bFirst = false;
					m_CoreDispInfo.GetSubdivPosition( nIndex, subdivPos );
					sprintf(szTemp, "%g %g %g", (double)subdivPos[0], (double)subdivPos[1], (double)subdivPos[2]);
					strcat(szBuf, szTemp);
				}

				char szKey[10];
				sprintf(szKey, "row%d", nRow);
				eResult = pFile->WriteKeyValue(szKey, szBuf);
			}
		}

		if (eResult == ChunkFile_Ok)
		{
			eResult = pFile->EndChunk();
		}
	}

    //
    // Save displacement subdivision normals
	//
	if (eResult == ChunkFile_Ok)
	{
		Vector subdivNormal;

		eResult = pFile->BeginChunk( "offset_normals" );
		if( eResult == ChunkFile_Ok )
		{
			char szBuf[MAX_KEYVALUE_LEN];
			char szTemp[80];

			int nRows = (1 << power) + 1;
			int nCols = nRows;

			for (int nRow = 0; nRow < nRows; nRow++)
			{
				bool bFirst = true;
				szBuf[0] = '\0';

				for (int nCol = 0; nCol < nCols; nCol++)
				{
					int nIndex = nRow * nCols + nCol;

					if (!bFirst)
					{
						strcat(szBuf, " ");
					}

					bFirst = false;
					m_CoreDispInfo.GetSubdivNormal( nIndex, subdivNormal );
					sprintf(szTemp, "%g %g %g", (double)subdivNormal[0], (double)subdivNormal[1], (double)subdivNormal[2]);
					strcat(szBuf, szTemp);
				}

				char szKey[10];
				sprintf(szKey, "row%d", nRow);
				eResult = pFile->WriteKeyValue(szKey, szBuf);
			}
		}

		if (eResult == ChunkFile_Ok)
		{
			eResult = pFile->EndChunk();
		}
	}

    //
    // Save displacement alphas
	//
	if (eResult == ChunkFile_Ok)
	{
		float alpha;

		eResult = pFile->BeginChunk( "alphas" );
		if( eResult == ChunkFile_Ok )
		{
			char szBuf[MAX_KEYVALUE_LEN];
			char szTemp[80];

			int nRows = (1 << power) + 1;
			int nCols = nRows;

			for (int nRow = 0; nRow < nRows; nRow++)
			{
				bool bFirst = true;
				szBuf[0] = '\0';

				for (int nCol = 0; nCol < nCols; nCol++)
				{
					int nIndex = nRow * nCols + nCol;

					if (!bFirst)
					{
						strcat(szBuf, " ");
					}

					bFirst = false;
					alpha = m_CoreDispInfo.GetAlpha( nIndex );
					sprintf(szTemp, "%g", (double)alpha);
					strcat(szBuf, szTemp);
				}

				char szKey[10];
				sprintf(szKey, "row%d", nRow);
				eResult = pFile->WriteKeyValue(szKey, szBuf);
			}
		}

		if (eResult == ChunkFile_Ok)
		{
			eResult = pFile->EndChunk();
		}
	}

    // Save Triangle data.
	if (eResult == ChunkFile_Ok)
	{
		unsigned short nTriTag;

		eResult = pFile->BeginChunk( "triangle_tags" );
		if( eResult == ChunkFile_Ok )
		{
			char szBuf[MAX_KEYVALUE_LEN];
			char szTemp[80];

			int nRows = ( 1 << power );			// ( 1 << power ) + 1 - 1
			int nCols = nRows;

			for ( int iRow = 0; iRow < nRows; ++iRow )
			{
				bool bFirst = true;
				szBuf[0] = '\0';

				for ( int iCol = 0; iCol < nCols; ++iCol )
				{
					int nIndex = iRow * nCols + iCol;
					int iTri = nIndex * 2;

					if ( !bFirst )
					{
						strcat( szBuf, " " );
					}
					bFirst = false;

					nTriTag = m_CoreDispInfo.GetTriTagValue( iTri );
					sprintf( szTemp, "%d", (int)nTriTag );
					strcat( szBuf, szTemp );

					nTriTag = m_CoreDispInfo.GetTriTagValue( iTri + 1 );
					sprintf( szTemp, " %d", (int)nTriTag );
					strcat( szBuf, szTemp );
				}

				char szKey[10];
				sprintf( szKey, "row%d", iRow );
				eResult = pFile->WriteKeyValue( szKey, szBuf );
			}
		}

		if (eResult == ChunkFile_Ok)
		{
			eResult = pFile->EndChunk();
		}
	}

	// Save allowed vert data.
	if ( eResult == ChunkFile_Ok )
	{
		eResult = pFile->BeginChunk( "allowed_verts" );
		if ( eResult == ChunkFile_Ok )
		{
			char szBuf[MAX_KEYVALUE_LEN];
			char szTemp[80];

			szBuf[0] = '\0';

			int nCount = m_CoreDispInfo.AllowedVerts_GetNumDWords();
			for ( int iCount = 0; iCount < nCount; ++iCount )
			{
				if ( iCount != 0 )
				{
					strcat( szBuf, " " );
				}
				
				unsigned long ulValue = m_CoreDispInfo.AllowedVerts_GetDWord( iCount );
				sprintf( szTemp, "%d", ( int )ulValue );
				strcat( szBuf, szTemp );
			}
			
			char szKey[8];
			sprintf( szKey, "%d", nCount );
			eResult = pFile->WriteKeyValue( szKey, szBuf );
		}

		if (eResult == ChunkFile_Ok)
		{
			eResult = pFile->EndChunk();
		}
	}

	if (eResult == ChunkFile_Ok)
	{
		eResult = pFile->EndChunk();
	}

	return(eResult);
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::SerializedLoadMAP( std::fstream &file, CMapFace *pFace, UINT version )
{
	int		power;
	float	maxData = 1.0f;
	int		minTess;
	float	smoothingAngle;
	Vector	vectorFieldVector;
	float	distance;

	//
    // read off the first line -- burn it!!! and get the second
	//
    static char buf[256];
    file.getline( buf, 256 );
    file.getline( buf, 256 );

	if( version < 350 )
	{
		sscanf( buf, "%d [ %f %f %f ] [ %f %f %f ] %f %d %f",
				&power,
				&m_MapAxes[0][0], &m_MapAxes[0][1], &m_MapAxes[0][2],
				&m_MapAxes[1][0], &m_MapAxes[1][1], &m_MapAxes[1][2],
				&maxData,
				&minTess,
				&smoothingAngle );
	}
	else
	{
		sscanf( buf, "%d [ %f %f %f ] [ %f %f %f ] %d %f",
				&power,
				&m_MapAxes[0][0], &m_MapAxes[0][1], &m_MapAxes[0][2],
				&m_MapAxes[1][0], &m_MapAxes[1][1], &m_MapAxes[1][2],
				&minTess,
				&smoothingAngle );
	}

	m_CoreDispInfo.SetPower( power );

	m_bHasMappingAxes = true;

    //
    // displacement normals
    //
	int size = GetSize();
    for( int i = 0; i < size; i++ )
    {
        file >> vectorFieldVector[0];
        file >> vectorFieldVector[1];
        file >> vectorFieldVector[2];

		m_CoreDispInfo.SetFieldVector( i, vectorFieldVector );
    }
    file.getline( buf, 256 );

    //
    // displacement distances
    //
    for( int i = 0; i < size; i++ )
    {
		if( version < 350 )
		{
	        file >> distance;
			distance *= maxData;
		}
		else
		{
	        file >> distance;
		}

		m_CoreDispInfo.SetFieldDistance( i, distance );
    }
    file.getline( buf, 256 );

    // finish the last bit of the "chunk"
    file.getline( buf, 256 );

    // save the parent info
    SetParent( pFace );

    return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::SerializedLoadRMF( std::fstream &file, CMapFace *pFace, float version )
{
	int		power;
	int		minTess;
	float	smoothingAngle;
	Vector	vectorFieldVectors[MAPDISP_MAX_VERTS];
	float	distances[MAPDISP_MAX_VERTS];

    //
    // get displacement information
    //
	file.read( ( char* )&power, sizeof( int ) );
	file.read( ( char* )m_MapAxes[0].Base(), 3 * sizeof( float ) );
	file.read( ( char* )m_MapAxes[1].Base(), 3 * sizeof( float ) );
	file.read( ( char* )&minTess, sizeof( int ) );
	file.read( ( char* )&smoothingAngle, sizeof( float ) );

	m_CoreDispInfo.SetPower( power );

	m_bHasMappingAxes = true;

    //
    // get displacement map normals and distances
    //
    int size = GetSize();
	int i;
	for ( i = 0; i < size; ++i)
	{
		file.read( ( char* )&vectorFieldVectors[i], 3 * sizeof( float ) );
	}
    file.read( ( char* )distances, size * sizeof( float ) );

	for( i = 0; i < size; i++ )
	{
		m_CoreDispInfo.SetFieldVector( i, vectorFieldVectors[i] );
		m_CoreDispInfo.SetFieldDistance( i, distances[i] );
	}

    // set the parent
    SetParent( pFace );

    // displacement info loaded
    return true;
}


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


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

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::DoTransform(const VMatrix &matrix)
{
	// get the face
	CCoreDispSurface *pSurf = m_CoreDispInfo.GetSurface();	
	CMapFace *pFace = ( CMapFace* )GetParent();

	if( !pFace || !pSurf )
		return;

	Assert( pFace->GetPointCount() == 4 );

	bool bFlip = (matrix[0][0]*matrix[1][1]*matrix[2][2]) < 0;

	if ( bFlip )
	{
		// get the displacement starting point, relative to the newly "flipped" points
		// NOTE: this seems a bit hacky -- if flip goes NUTS later -- look here!!!
		
		int iStartIndex = pSurf->GetPointStartIndex(); 
		pSurf->SetPointStartIndex( 3-iStartIndex );
		Flip( FLIP_TRANSPOSE );
	} 

	Vector v;
	int size = GetSize();
	for( int i = 0; i < size; i++ )
	{
		GetFieldVector( i, v );
		TransformPoint( matrix, v );
 		SetFieldVector( i, v );

		GetSubdivPosition( i, v );
		TransformPoint( matrix, v );
		SetSubdivPosition( i, v );

		GetSubdivNormal( i, v );
		TransformPoint( matrix, v );
		SetSubdivNormal( i, v );
	}

	UpdateSurfData( pFace );
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool SphereTriEdgePlanesIntersection( Vector const &ptCenter, float radius, cplane_t *pPlanes )
{
	// check all planes
	for( int ndxPlane = 0; ndxPlane < 3; ndxPlane++ )
	{
		float dist = pPlanes[ndxPlane].normal.Dot( ptCenter ) - pPlanes[ndxPlane].dist;
		if( dist > radius )
			return false;
	}

	return true;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::PointSurfIntersection( Vector const &ptCenter, float radius, float &distMin,
							          Vector &ptMin )
{
	// initialize the min data
	distMin = radius;
	ptMin.Init();

	//
	// get the render list size -- created triangles
	//
	unsigned short *pTriList = m_CoreDispInfo.GetRenderIndexList();
	int listSize = m_CoreDispInfo.GetRenderIndexCount();
	for( int i = 0; i < listSize; i += 3 )
	{
		// get the triangle
		Vector v[3];
		GetVert( pTriList[i], v[0] );
		GetVert( pTriList[i+1], v[1] );
		GetVert( pTriList[i+2], v[2] );

		//
		// create a triangle plane
		//
		Vector seg0, seg1;
		seg0 = v[1] - v[0];
		seg1 = v[2] - v[0];
		cplane_t triPlane;
		triPlane.normal = seg1.Cross( seg0 );
		VectorNormalize( triPlane.normal );
		triPlane.dist = triPlane.normal.Dot( v[0] );

		//
		// plane sphere intersection
		//
		float dist = triPlane.normal.Dot( ptCenter ) - triPlane.dist;
		if( fabs( dist ) < distMin )
		{
			//
			// create edge plane data
			//
			cplane_t edgePlanes[3];
			Vector edges[3];
			edges[0] = v[1] - v[0];
			edges[1] = v[2] - v[1];
			edges[2] = v[0] - v[2];

			for( int j = 0; j < 3; j++ )
			{
				edgePlanes[j].normal = triPlane.normal.Cross( edges[j] );
				VectorNormalize( edgePlanes[j].normal );
				edgePlanes[j].dist = edgePlanes[j].normal.Dot( v[j] );

				// check normal facing
				float distPt = edgePlanes[j].normal.Dot( v[(j+2)%3] ) - edgePlanes[j].dist;
				if( distPt > 0.0f )
				{
					edgePlanes[j].normal.Negate();
					edgePlanes[j].dist = -edgePlanes[j].dist;
				}
			}

			// intersect sphere with triangle
			bool bSphereIntersect = SphereTriEdgePlanesIntersection( ptCenter, distMin, edgePlanes );

			//
			// check to see if the center lies behind all the edge planes
			//
			if( bSphereIntersect )
			{
				bool bPointInside = SphereTriEdgePlanesIntersection( ptCenter, 0.0f, edgePlanes );

				if( bPointInside )
				{
					distMin = fabs( dist );
					ptMin = ptCenter - ( triPlane.normal * dist );
				}
				else
				{
					// check distance to points
					for( int k = 0; k < 3; k++ )
					{
						Vector vTmp;
						vTmp = ptCenter - v[k];
						float distPt = ( float )sqrt( vTmp.Dot( vTmp ) );
						if( distPt < distMin )
						{
							distMin = distPt;
							ptMin = v[k];
						}
					}
				}
			}
		}
	}

	if( distMin != radius )
		return true;

	return false;
}


//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
EditDispHandle_t CMapDisp::GetHitDispMap( void )
{
	if( m_HitDispIndex == -1 )
	{
		CMapFace *pFace = ( CMapFace* )GetParent();
		return pFace->GetDisp();
	}

	if( m_HitDispIndex <= 3 )
	{
		return m_EdgeNeighbors[m_HitDispIndex];
	}

	return m_CornerNeighbors[m_HitDispIndex-4][2];
}


//-----------------------------------------------------------------------------
// Purpose: UNDO is messy to begin with, and now with handles it gets even 
//          more fun!!!  Call through here to setup undo!!
//-----------------------------------------------------------------------------
void EditDisp_ForUndo( EditDispHandle_t editHandle, char *pszPositionName,
					   bool bNeighborsUndo )
{
	// sanity check on handle
	if( editHandle == EDITDISPHANDLE_INVALID )
		return;

	// get the current displacement given the handle
	CMapDisp *pDisp = EditDispMgr()->GetDisp( editHandle );

	//
	// set the undo name if necessary
	//
	if( pszPositionName )
	{
		GetHistory()->MarkUndoPosition( NULL, pszPositionName );
	}

	//
	// get the solid (face) for the UNDO history
	//
	CMapFace *pFace = ( CMapFace* )pDisp->GetParent();
	CMapSolid *pSolid = ( CMapSolid* )pFace->GetParent();
	GetHistory()->Keep( ( CMapClass* )pSolid );
	
	//
	// neighbors in undo as well
	//
	if ( bNeighborsUndo )
	{
		for ( int ndxNeighbor = 0; ndxNeighbor < 4; ndxNeighbor++ )
		{
			// displacement pointer could have changed due to the undo/copyfrom above
			pDisp = EditDispMgr()->GetDisp( editHandle );

			//
			// edge neighbors
			//
			int neighborOrient;
			EditDispHandle_t neighborHandle;
			pDisp->GetEdgeNeighbor( ndxNeighbor, neighborHandle, neighborOrient );
			if( neighborHandle != EDITDISPHANDLE_INVALID )
			{
				CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( neighborHandle );
				CMapFace *pNeighborFace = ( CMapFace* )pNeighborDisp->GetParent();
				CMapSolid *pNeighborSolid = ( CMapSolid* )pNeighborFace->GetParent();
				GetHistory()->Keep( ( CMapClass* )pNeighborSolid );

				// displacement pointer could have changed due to the undo/copyfrom above
				pDisp = EditDispMgr()->GetDisp( editHandle );
			}

			//
			// corner neighbors
			//
			int cornerCount = pDisp->GetCornerNeighborCount( ndxNeighbor );
			if( cornerCount > 0 )
			{
				for( int ndxCorner = 0; ndxCorner < cornerCount; ndxCorner++ )
				{
					pDisp->GetCornerNeighbor( ndxNeighbor, ndxCorner, neighborHandle, neighborOrient );
					CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( neighborHandle );
					CMapFace *pNeighborFace = ( CMapFace* )pNeighborDisp->GetParent();
					CMapSolid *pNeighborSolid = ( CMapSolid* )pNeighborFace->GetParent();
					GetHistory()->Keep( ( CMapClass* )pNeighborSolid );

					// displacement pointer could have changed due to the undo/copyfrom above
					pDisp = EditDispMgr()->GetDisp( editHandle );
				}
			}
		}
	}
}


//=============================================================================
//
// Painting Functions
//

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Paint_Init( int nType )
{
	m_Canvas.m_nType = nType;
	m_Canvas.m_bDirty = false;

	int nVertCount = GetSize();
	for( int iVert = 0; iVert < nVertCount; iVert++ )
	{
		m_Canvas.m_Values[iVert].Init();
		m_Canvas.m_bValuesDirty[iVert] = false;
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Paint_InitSelfAndNeighbors( int nType )
{
	// Initialiuze self.
	Paint_Init( nType );

	// Initialize neighbors.
	for( int iEdge = 0; iEdge < 4; iEdge++ )
	{
		EditDispHandle_t handle = GetEdgeNeighbor( iEdge );
		if( handle != EDITDISPHANDLE_INVALID )
		{
			CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
			pNeighborDisp->Paint_Init( nType );
		}

		int nCornerCount = GetCornerNeighborCount( iEdge );
		if( nCornerCount > 0 )
		{
			for( int iCorner = 0; iCorner < nCornerCount; iCorner++ )
			{
				handle = GetCornerNeighbor( iEdge, iCorner );
				if( handle != EDITDISPHANDLE_INVALID )
				{
					CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
					pNeighborDisp->Paint_Init( nType );
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Paint_SetValue( int iVert, Vector const &vPaint )
{
	Assert( iVert >= 0 );
	Assert( iVert < MAPDISP_MAX_VERTS );

	VectorCopy( vPaint, m_Canvas.m_Values[iVert] );
	m_Canvas.m_bValuesDirty[iVert] = true;
	m_Canvas.m_bDirty = true;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::PaintAlpha_Update( int iVert )
{
	SetAlpha( iVert, m_Canvas.m_Values[iVert].x );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::PaintPosition_Update( int iVert )
{
	Vector vSPos, vFlat;
	GetFlatVert( iVert, vFlat );
	GetSubdivPosition( iVert, vSPos );
				
	Vector vSeg;
	vSeg = m_Canvas.m_Values[iVert] - vFlat;
	vSeg -= vSPos;
				
	// Subtract out the elevation.
	float elev = GetElevation();
	if( elev != 0.0 )
	{
		Vector vNormal;
		GetSurfNormal( vNormal );
		vNormal *= elev;
		
		vSeg -= vNormal;
	}
				
	float flDistance = VectorNormalize( vSeg );
	
	SetFieldVector( iVert, vSeg );
	SetFieldDistance( iVert, flDistance );
}

void CMapDisp::UpdateVertPositionForSubdiv( int iVert, const Vector &vecNewSubdivPos )
{
	Vector vecSubdivPos, vecFlatPos, vecPos;
	GetFlatVert( iVert, vecFlatPos );
	GetSubdivPosition( iVert, vecSubdivPos );
	GetVert( iVert, vecPos );

	Vector vecSegment1;
	vecPos -= vecSubdivPos;
	vecSegment1 = vecPos - vecFlatPos;

	// Subtract out the elevation.
	float flElevation = GetElevation();
	Vector vecFaceNormal( 0.0f, 0.0f, 0.0f );
	if( flElevation != 0.0 )
	{
		GetSurfNormal( vecFaceNormal );
		vecFaceNormal *= flElevation;
		vecSegment1 -= vecFaceNormal;
	}

	float flDistance = VectorNormalize( vecSegment1 );

	SetFieldVector( iVert, vecSegment1 );
	SetFieldDistance( iVert, flDistance );

	SetSubdivPosition( iVert, vecNewSubdivPos );

	// Have to update in place.
	Vector vecNewPos = vecFlatPos;
	vecNewPos += ( vecFaceNormal * flElevation );
	vecNewPos += vecNewSubdivPos;
	vecNewPos += ( vecSegment1 * flDistance );
	SetVert( iVert, vecNewPos );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Paint_Update( bool bSplit )
{
	// Check for changes to the canvas.
	if ( !m_Canvas.m_bDirty )
		return;

	int nVertCount = GetSize();
	for ( int iVert = 0; iVert < nVertCount; iVert++ )
	{
		// Check for changes at the vertex.
		if ( m_Canvas.m_bValuesDirty[iVert] )
		{
			if ( m_Canvas.m_nType == DISPPAINT_CHANNEL_POSITION )
			{
				PaintPosition_Update( iVert );
			}
			else if ( m_Canvas.m_nType == DISPPAINT_CHANNEL_ALPHA )
			{
				PaintAlpha_Update( iVert );
			}
		}
	}

	// Update the displacement surface.
	UpdateData();

	if ( !bSplit )
	{
		CheckAndUpdateOverlays( false );
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::Paint_UpdateSelfAndNeighbors( bool bSplit )
{
	// Update self.
	Paint_Update( bSplit );

	// Update neighbors.
	for( int iEdge = 0; iEdge < 4; iEdge++ )
	{
		EditDispHandle_t handle = GetEdgeNeighbor( iEdge );
		if( handle != EDITDISPHANDLE_INVALID )
		{
			CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
			pNeighborDisp->Paint_Update( bSplit );
		}

		int nCornerCount = GetCornerNeighborCount( iEdge );
		if( nCornerCount > 0 )
		{
			for( int iCorner = 0; iCorner < nCornerCount; iCorner++ )
			{
				handle = GetCornerNeighbor( iEdge, iCorner );
				if( handle != EDITDISPHANDLE_INVALID )
				{
					CMapDisp *pNeighborDisp = EditDispMgr()->GetDisp( handle );
					pNeighborDisp->Paint_Update( bSplit );
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::SetSelectMask( bool bSelectMask ) 
{ 
	m_bSelectMask = bSelectMask; 
}
	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::HasSelectMask( void ) 
{ 
	return m_bSelectMask; 
}	

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CMapDisp::SetGridMask( bool bGridMask ) 
{ 
	m_bGridMask = bGridMask; 
}
	
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
bool CMapDisp::HasGridMask( void ) 
{ 
	return m_bGridMask; 
}

//-----------------------------------------------------------------------------
// Purpose: Do the slow thing first and optimize later??
//-----------------------------------------------------------------------------
int CMapDisp::CollideWithDispTri( const Vector &rayStart, const Vector &rayEnd, float &flFraction, bool OneSided )
{
	int iTriangle = -1;
	flFraction = 1.0f;

	int nTriCount = GetTriCount();
	for ( int iTri = 0; iTri < nTriCount; ++iTri )
	{
		unsigned short v1, v2, v3;
		GetTriIndices( iTri, v1, v2, v3 );
		Vector vec1, vec2, vec3;
		GetVert( v1, vec1 );
		GetVert( v2, vec2 );
		GetVert( v3, vec3 );

		Ray_t ray;
		ray.Init( rayStart, rayEnd, Vector( 0.0f, 0.0f, 0.0f ), Vector ( 0.0f, 0.0f, 0.0f ) );

		float flFrac = IntersectRayWithTriangle( ray, vec1, vec2, vec3, OneSided );
		if ( flFrac == -1.0f )
			continue;

		if ( flFrac < flFraction )
		{
			flFraction = flFrac;
			iTriangle = iTri;
		}
	}

	return iTriangle;
}

bool CMapDisp::SaveDXF(ExportDXFInfo_s *pInfo)
{
	char szName[128];
	sprintf(szName, "OBJECT%03d", pInfo->nObject);

	// count number of triangulated faces
	int nVertCount = GetSize();
	int nTriFaces = TriangleCount();

	fprintf(pInfo->fp,"0\nPOLYLINE\n8\n%s\n66\n1\n70\n64\n71\n%u\n72\n%u\n", 
		szName, nVertCount, nTriFaces);
	fprintf(pInfo->fp,"62\n50\n");

	// Write out vertices...
	int i;
	for (i = 0; i < nVertCount; i++)
	{
		Vector pos;
		GetVert( i, pos );
		fprintf(pInfo->fp,	"0\nVERTEX\n8\n%s\n10\n%.6f\n20\n%.6f\n30\n%.6f\n70\n192\n", szName, pos[0], pos[1], pos[2]);
	}

	// triangulate each face and write
	int nWidth = GetWidth();
	int nHeight = GetHeight();
	for (i = 0; i < nHeight - 1; ++i)
	{
		for (int j = 0; j < nWidth - 1; ++j)
		{
			// DXF files are 1 based, not 0 based. That's what the extra 1 is for
			int idx = i * nHeight + j + 1;

			fprintf(pInfo->fp, "0\nVERTEX\n8\n%s\n10\n0\n20\n0\n30\n"
				"0\n70\n128\n71\n%d\n72\n%d\n73\n%d\n", szName,
				idx, idx + nHeight, idx + nHeight + 1 );

			fprintf(pInfo->fp, "0\nVERTEX\n8\n%s\n10\n0\n20\n0\n30\n"
				"0\n70\n128\n71\n%d\n72\n%d\n73\n%d\n", szName,
				idx, idx + nHeight + 1, idx + 1 );
		}
	}

	fprintf(pInfo->fp, "0\nSEQEND\n8\n%s\n", szName);

	return true;
}