//========= Copyright Valve Corporation, All rights reserved. ============//
// SculptOptions.cpp : implementation file
//

#include <stdafx.h>
#include "hammer.h"
#include "CollisionUtils.h"
#include "resource.h"
#include "ToolDisplace.h"
#include "MainFrm.h"
#include "FaceEditSheet.h"
#include "GlobalFunctions.h"
#include "MapAtom.h"
#include "MapSolid.h"
#include "MapView3D.h"
#include "History.h"
#include "Camera.h"
#include "MapDoc.h"
#include "ChunkFile.h"
#include "ToolManager.h"
#include "bitmap/tgaloader.h"
#include "tier1/utlbuffer.h"
#include "Material.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialsystem.h"
#include "materialsystem/MaterialSystemUtil.h"
#include "materialsystem/itexture.h"
#include "../materialsystem/itextureinternal.h"
#include "pixelwriter.h"
#include "TextureSystem.h"
#include "SculptOptions.h"

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


extern CToolDisplace* GetDisplacementTool();
extern void FaceListSewEdges( void );


CUtlMap<EditDispHandle_t, CMapDisp *>		CSculptTool::m_OrigMapDisp( 3, 3, CSculptTool::MapDispLessFunc );


//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CSculptTool::CSculptTool()
{
	m_PaintOwner = NULL;

	m_MousePoint.Init();
	m_StartingCollisionNormal.Init();

	m_OriginalCollisionPoint.Init();

	m_bAltDown = m_bCtrlDown = m_bShiftDown = false;

	m_bLMBDown = m_bRMBDown = false;
	m_ValidPaintingSpot = false;
	m_BrushSize = 50;

	m_StartingProjectedRadius = m_OriginalProjectedRadius = 10.0f;

	m_OriginalCollisionValid = m_CurrentCollisionValid = false;
}


//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CSculptTool::~CSculptTool()
{
	FOR_EACH_MAP( m_OrigMapDisp, pos )
	{
		delete m_OrigMapDisp.Element( pos );
	}
	m_OrigMapDisp.Purge();
}


//-----------------------------------------------------------------------------
// Purpose: setup for starting to paint on the displacement
// Input  : pView - the 3d view
//			vPoint - the initial click point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::BeginPaint( CMapView3D *pView, const Vector2D &vPoint )
{
	DuplicateSelectedDisp();

	GetStartingSpot( pView, vPoint );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: main routine called when mouse move has happened to start painting
// Input  : pView - the 3d view
//			vPoint - the mouse point
//			SpatialData - the spatial data ( mostly ignored )
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::Paint( CMapView3D *pView, const Vector2D &vPoint, SpatialPaintData_t &SpatialData )
{
	m_SpatialData = SpatialData;

	// Successful paint operation.
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: determines if any of the special keys ( control, shift, alt ) are pressed
//-----------------------------------------------------------------------------
void CSculptTool::DetermineKeysDown()
{
	m_bCtrlDown = ( ( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) != 0 );
	m_bShiftDown = ( ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) != 0 );
	m_bAltDown = ( ( GetAsyncKeyState( VK_MENU ) & 0x8000 ) != 0 );
}


//-----------------------------------------------------------------------------
// Purpose: handles the left mouse button up in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::OnLMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	DetermineKeysDown();

	// left button up
	m_bLMBDown = false;
	m_MousePoint = vPoint;

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the left mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::OnLMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	DetermineKeysDown();

	// left button down
	m_bLMBDown = true;
	m_MousePoint = vPoint;

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the right mouse button up in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::OnRMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	DetermineKeysDown();

	// right button up
	m_bRMBDown = false;
	m_MousePoint = vPoint;

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the right mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::OnRMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	DetermineKeysDown();

	// right button down
	m_bRMBDown = true;
	m_MousePoint = vPoint;

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the mouse move in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::OnMouseMove3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	DetermineKeysDown();

	m_MousePoint = vPoint;

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: called just before painting begins to gather reference information
// Input  : pView - the 3d view
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::PrePaint( CMapView3D *pView, const Vector2D &vPoint )
{
	Vector2D	RadiusPoint = vPoint;
	Vector		vecStart, vecEnd;

	RadiusPoint.x += m_BrushSize;
	pView->GetCamera()->BuildRay( RadiusPoint, vecStart, vecEnd );

	m_OriginalCollisionValid = FindCollisionIntercept( pView->GetCamera(), vPoint, true, m_OriginalCollisionPoint, m_OriginalCollisionNormal, m_OriginalCollisionIntercept );
	if ( m_OriginalCollisionValid )
	{
		m_OriginalProjectedRadius = CalcDistanceToLine( m_OriginalCollisionPoint, vecStart, vecEnd );
	}

	m_CurrentCollisionValid = FindCollisionIntercept( pView->GetCamera(), vPoint, false, m_CurrentCollisionPoint, m_CurrentCollisionNormal, m_CurrentCollisionIntercept );
	if ( m_CurrentCollisionValid )
	{
		m_CurrentProjectedRadius = CalcDistanceToLine( m_CurrentCollisionPoint, vecStart, vecEnd );
	}

	m_SpatialData.m_flRadius = 128.0f;
	m_SpatialData.m_flRadius2 = ( m_SpatialData.m_flRadius * m_SpatialData.m_flRadius );
	m_SpatialData.m_flOORadius2 = 1.0f / m_SpatialData.m_flRadius2;

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: called after painting finishes to finalize things
// Input  : bAutoSew - should we sew the edges
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::PostPaint( bool bAutoSew )
{
	// Get the displacement manager from the active map document.
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( !pDispMgr )
		return false;

	// Update the modified displacements.
	int nDispCount = pDispMgr->SelectCount();
	for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
		if ( pDisp )
		{
			pDisp->Paint_Update( false );
		}
	}

	// Auto "sew" if necessary.
	if ( bAutoSew )
	{
		FaceListSewEdges();
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: called to dispatch the painting routine across all selected displacements
// Input  : pView - the 3d view
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptTool::DoPaint( CMapView3D *pView, const Vector2D &vPoint )
{
	// Get the displacement manager from the active map document.
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( !pDispMgr )
		return false;

	// For each displacement surface is the selection list attempt to paint on it.
	int nDispCount = pDispMgr->SelectCount();
	for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
		if ( pDisp )
		{
			CMapDisp	*OrigDisp = NULL;
			int			index = m_OrigMapDisp.Find( pDisp->GetEditHandle() );
			
			if ( index != m_OrigMapDisp.InvalidIndex() )
			{
				OrigDisp = m_OrigMapDisp[ index ];
			}
			DoPaintOperation( pView, vPoint, pDisp, OrigDisp );
		}
	}

	// Successful paint.
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: checks to see if a given displacement vert lies within the 2d screenspace of the circle
// Input  : pView - the 3d view
//			pDisp - the displacement the vert belongs to
//			pOrigDisp - the displacement prior to any moving
//			nVertIndex - the vert index
//			bUseOrigDisplacement - should we use the vert from the original displacement
//			bUseCurrentPosition - should we use the current collision test point
// Output : returns true if the point is within the circle
//-----------------------------------------------------------------------------
bool CSculptTool::IsPointInScreenCircle( CMapView3D *pView, CMapDisp *pDisp, CMapDisp *pOrigDisp, int nVertIndex, bool bUseOrigDisplacement, bool bUseCurrentPosition, float *pflLengthPercent )
{
	Vector	vVert, vTestVert;

	pDisp->GetVert( nVertIndex, vVert );

	if ( pOrigDisp && bUseOrigDisplacement )
	{
		pOrigDisp->GetVert( nVertIndex, vTestVert );
	}
	else
	{
		vTestVert = vVert;
	}

#if 0
	Vector2D ViewVert;
	pView->GetCamera()->WorldToView( vTestVert, ViewVert );

	Vector2D	Offset = ViewVert - m_MousePoint;
	float		Length = Offset.Length();

	return ( Length <= m_BrushSize );
#else
	if ( bUseCurrentPosition )
	{
		if ( !m_CurrentCollisionValid )
		{
			return false;
		}

		Vector	Offset = m_CurrentCollisionPoint - vTestVert;
		float	Length = Offset.Length();

		if ( pflLengthPercent )
		{
			*pflLengthPercent = Length / m_CurrentProjectedRadius;
		}

		return ( Length <= m_CurrentProjectedRadius );
	}
	else
	{
		if ( !m_OriginalCollisionValid )
		{
			return false;
		}

		Vector	Offset = m_OriginalCollisionPoint - vTestVert;
		float	Length = Offset.Length();

		if ( pflLengthPercent )
		{
			*pflLengthPercent = Length / m_OriginalProjectedRadius;
		}

#if 0
		if ( Length <= m_OriginalProjectedRadius || vertIndex == 66 )
		{
			Msg( "%d: ( %g %g %g ) from %g <= %g at ( %g %g %g )\n", vertIndex, vTestVert.x, vTestVert.y, vTestVert.z, Length, m_OriginalProjectedRadius, m_OriginalCollisionPoint.x, m_OriginalCollisionPoint.y, m_OriginalCollisionPoint.z );
		}
#endif
		return ( Length <= m_OriginalProjectedRadius );
	}
#endif
}


//-----------------------------------------------------------------------------
// Purpose: Adds a displacement to the undo manager
// Input  : pDisp - the displacement
//-----------------------------------------------------------------------------
void CSculptTool::AddToUndo( CMapDisp **pDisp )
{
	CMapDisp *pUndoDisp = *pDisp;
	if ( pUndoDisp->Paint_IsDirty() )
		return;

	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( pDispMgr )
	{
		EditDispHandle_t handle = pUndoDisp->GetEditHandle();
		pDispMgr->Undo( handle, false );
		*pDisp = EditDispMgr()->GetDisp( handle );
	}
}


#if 0
//-----------------------------------------------------------------------------
// Purpose: 
// Input  : 
// Output :
//-----------------------------------------------------------------------------
void CSculptTool::DoPaintEqual( SpatialPaintData_t &spatialData, CMapDisp *pDisp )
{
	Vector vPaintPos, vVert, vFlatVert;
	float flDistance2;

	int nVertCount = pDisp->GetSize();
	for ( int iVert = 0; iVert < nVertCount; iVert++ )
	{
		// Get the current vert.
		pDisp->GetVert( iVert, vVert );

		if ( IsInSphereRadius( spatialData.m_vCenter, spatialData.m_flRadius2, vVert, flDistance2 ) )
		{
			// Get the base vert.
			pDisp->GetFlatVert( iVert, vFlatVert );

			// Build the new position (paint value) and set it.
			DoPaintOne( spatialData, vFlatVert, vPaintPos );
			AddToUndo( &pDisp );
			pDisp->Paint_SetValue( iVert, vPaintPos );
		}
	}
}
#endif


//-----------------------------------------------------------------------------
// Purpose: this routine does the smoothing operation
// Input  : pView - the 3d view
//			vPoint - the mouse point
//			pDisp - the displacement to smooth
//			pOrigDisp - the displacement prior to the paint operation
// Output :
//-----------------------------------------------------------------------------
void CSculptTool::DoPaintSmooth( CMapView3D *pView, const Vector2D &vPoint, CMapDisp *pDisp, CMapDisp *pOrigDisp )
{
	Vector	vPaintPos, vVert;

	pDisp->GetSurfNormal( m_SpatialData.m_vPaintAxis );

	int nVertCount = pDisp->GetSize();
	for ( int iVert = 0; iVert < nVertCount; iVert++ )
	{
		if ( IsPointInScreenCircle( pView, pDisp, pOrigDisp, iVert, false, true ) )
		{
//			Msg( "Checking Vert %d\n", iVert );
			// Get the current vert.
			pDisp->GetVert( iVert, vVert );

			// Build the new smoothed position and set it.
			if ( DoPaintSmoothOneOverExp( vVert, vPaintPos ) )
			{
				AddToUndo( &pDisp );
				pDisp->Paint_SetValue( iVert, vPaintPos );
//				Msg( "Vert %d Updated: from %g %g %g to %g %g %g\n", iVert, vVert.x, vVert.y, vVert.z, vPaintPos.x, vPaintPos.y, vPaintPos.z );
			}
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: checks to see if the paint sphere is within the bounding box
// Input  : vCenter - center of the sphere
//			flRadius - sphere radius
//			vBBoxMin - bounding box mins
//			vBBoxMax - bounding box maxs
// Output : returns two if the two intersect
//-----------------------------------------------------------------------------
bool CSculptTool::PaintSphereDispBBoxOverlap( const Vector &vCenter, float flRadius, const Vector &vBBoxMin, const Vector &vBBoxMax )
{
	return IsBoxIntersectingSphere( vBBoxMin, vBBoxMax, vCenter, flRadius );
}


//-----------------------------------------------------------------------------
// Purpose: checkes to see if the two spheres intersect
// Input  : vCenter - center of the sphere
//			flRadius2 - sphere radius squared
//			vPos - point to test
//			flDistance2 - radius of point
// Output : returns true if the two spheres intersect
//-----------------------------------------------------------------------------
bool CSculptTool::IsInSphereRadius( const Vector &vCenter, float flRadius2, const Vector &vPos, float &flDistance2 )
{
	Vector vTmp;
	VectorSubtract( vPos, vCenter, vTmp );
	flDistance2 = ( vTmp.x * vTmp.x ) + ( vTmp.y * vTmp.y ) + ( vTmp.z * vTmp.z );
	return ( flDistance2 < flRadius2 );
}


//-----------------------------------------------------------------------------
// Purpose: calculates the smoothing radius squared
// Input  : vPoint - the point to be smoothed
// Output : returns the smoothing radius squared
//-----------------------------------------------------------------------------
float CSculptTool::CalcSmoothRadius2( const Vector &vPoint )
{
	Vector vTmp;
	VectorSubtract( m_SpatialData.m_vCenter, vPoint, vTmp );
	float flDistance2 = ( vTmp.x * vTmp.x ) + ( vTmp.y * vTmp.y ) + ( vTmp.z * vTmp.z );

	float flRatio = flDistance2 / m_SpatialData.m_flRadius2;
	flRatio = 1.0f - flRatio;

	float flRadius = flRatio * m_SpatialData.m_flRadius;
	return ( flRadius * flRadius );
}


//-----------------------------------------------------------------------------
// Purpose: smooths all displacements 
// Input  : vNewCenter - calculate the smoothing center
// Output : returns true if successful
//			vPaintPos - the new smoothing position
//-----------------------------------------------------------------------------
bool CSculptTool::DoPaintSmoothOneOverExp( const Vector &vNewCenter, Vector &vPaintPos )
{
	// Get the displacement manager from the active map document.
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( !pDispMgr )
		return false;

	// Calculate the smoothing radius.
	float flNewRadius2 = CalcSmoothRadius2( vNewCenter );
	flNewRadius2 *= 2.0f;
	float flNewRadius = ( float )sqrt( flNewRadius2 );


	// Test all selected surfaces for smoothing.
	float flWeight = 0.0f;
	float flSmoothDist = 0.0f;

	// Calculate the plane dist.
	float flPaintDist = m_SpatialData.m_vPaintAxis.Dot( vNewCenter );

	int nDispCount = pDispMgr->SelectCount();
	for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
		if ( pDisp )
		{
			// Test paint sphere displacement bbox for overlap.
			Vector vBBoxMin, vBBoxMax;
			pDisp->GetBoundingBox( vBBoxMin, vBBoxMax );
			if ( PaintSphereDispBBoxOverlap( vNewCenter, flNewRadius, vBBoxMin, vBBoxMax ) )
			{
				Vector vVert;
				int nVertCount = pDisp->GetSize();
				for ( int iVert = 0; iVert < nVertCount; iVert++ )
				{
					// Get the current vert.
					pDisp->GetVert( iVert, vVert );

					float flDistance2 = 0.0f;
					if ( IsInSphereRadius( vNewCenter, flNewRadius2, vVert, flDistance2 ) )
					{
						float flRatio = flDistance2 / flNewRadius2;
						float flFactor = 1.0f / exp( flRatio );
						if ( flFactor != 1.0f )
						{
							flFactor *= 1.0f / ( m_SpatialData.m_flScalar * 2.0f );
						}

						Vector vProjectVert;
						float flProjectDist = DotProduct( vVert, m_SpatialData.m_vPaintAxis ) - flPaintDist;
						flSmoothDist += ( flProjectDist * flFactor );
						flWeight += flFactor;
//						Msg( "Factoring %d: %g %g %g at %g\n", iVert, vVert.x, vVert.y, vVert.z, flNewRadius2 );
					}
				}
			}
		}
	}

	if ( flWeight == 0.0f )
	{
		return false;
	}

	// Re-normalize the smoothing position.
	flSmoothDist /= flWeight;
	vPaintPos = vNewCenter + ( m_SpatialData.m_vPaintAxis * flSmoothDist );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: gets the starting position when the paint operation begins
// Input  : pView - the 3d view
//			vPoint - the mouse point
// Output : returns the starting position
//-----------------------------------------------------------------------------
bool CSculptTool::GetStartingSpot( CMapView3D *pView, const Vector2D &vPoint )
{
	m_ValidPaintingSpot = FindCollisionIntercept( pView->GetCamera(), vPoint, false, m_StartingCollisionPoint, m_StartingCollisionNormal, m_StartingCollisionIntercept );

	if ( m_ValidPaintingSpot )
	{
		Vector2D	RadiusPoint = vPoint;
		Vector		vecStart, vecEnd;

		RadiusPoint.x += m_BrushSize;
		pView->GetCamera()->BuildRay( RadiusPoint, vecStart, vecEnd );
		m_StartingProjectedRadius = CalcDistanceToLine( m_StartingCollisionPoint, vecStart, vecEnd );

	}

	return m_ValidPaintingSpot;
}


//-----------------------------------------------------------------------------
// Purpose: Draws a 2d line to represent the direction
// Input  : pRender - the renderer
//			Direction - direction / normal
//			Towards - the color to be used if the direction is towards the viewer
//			Away - the color to be used if the direction is away from the view
//-----------------------------------------------------------------------------
void CSculptTool::DrawDirection( CRender3D *pRender, Vector Direction, Color Towards, Color Away )
{
	Vector		ViewPoint, ViewDir;
	Vector2D	ViewVert;

	VMatrix  Matrix;
	pRender->GetCamera()->GetViewProjMatrix( Matrix );
	Matrix.SetTranslation( Vector( 0.0f, 0.0f, 0.0f ) );
	Vector3DMultiply( Matrix, Direction, ViewDir );
	VectorNormalize( ViewDir );

	ViewVert = m_MousePoint + ( Vector2D( ViewDir.x, -ViewDir.y ) * m_BrushSize );

	if ( ViewDir.z > 0.0f )
	{
		pRender->SetDrawColor( Away.r(), Away.g(), Away.b() );
	}
	else
	{
		pRender->SetDrawColor( Towards.r(), Towards.g(), Towards.b() );
	}

	bool bPopMode = pRender->BeginClientSpace();
	pRender->DrawLine( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( ViewVert.x, ViewVert.y, 0.0f ) );
	if ( bPopMode )
	{
		pRender->EndClientSpace();
	}
}


//-----------------------------------------------------------------------------
// Purpose: this function will copy all the selected displacements
//-----------------------------------------------------------------------------
void CSculptTool::DuplicateSelectedDisp( )
{
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( !pDispMgr )
	{
		return;
	}

	FOR_EACH_MAP( m_OrigMapDisp, pos )
	{
		delete m_OrigMapDisp.Element( pos );
	}
	m_OrigMapDisp.Purge();

	int nDispCount = pDispMgr->SelectCount();

	for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
		if ( pDisp )
		{
			CMapDisp *pCopy = new CMapDisp();

			pCopy->CopyFrom( pDisp, false );
			m_OrigMapDisp.Insert( pDisp->GetEditHandle(), pCopy );
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: this function will initialize all selected displacements for updating
//-----------------------------------------------------------------------------
void CSculptTool::PrepareDispForPainting( )
{
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( !pDispMgr )
	{
		return;
	}

	int nDispCount = pDispMgr->SelectCount();

	for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
		if ( pDisp )
		{
			pDisp->Paint_Init( DISPPAINT_CHANNEL_POSITION );
		}
	}

}


//-----------------------------------------------------------------------------
// Purpose: this function will find the collision location within the selected displacements
// Input  : pCamera - the camera
//			vPoint - the 2d point on screen
//			bUseOrigPosition - should we use the original displacements prior to updating
// Output : returns true if the point intercepted one of the selected displacements
//			vCollisionPoint the 3d interception point
//			vCollisionNormal - the normal of the tri hit
//			flCollisionIntercept - the intercept
//-----------------------------------------------------------------------------
bool CSculptTool::FindCollisionIntercept( CCamera *pCamera, const Vector2D &vPoint, bool bUseOrigPosition, Vector &vCollisionPoint, Vector &vCollisionNormal, float &flCollisionIntercept )
{
	Vector	vecStart, vecEnd;
	float	flFraction, flLeastFraction;

	flLeastFraction = -1.0f;

	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( !pDispMgr )
	{
		return false;
	}

	int nDispCount = pDispMgr->SelectCount();
	pCamera->BuildRay( vPoint, vecStart, vecEnd );

	for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
		if ( pDisp )
		{
			if ( bUseOrigPosition )
			{
				CMapDisp	*OrigDisp = NULL;
				int			index = m_OrigMapDisp.Find( pDisp->GetEditHandle() );

				if ( index != m_OrigMapDisp.InvalidIndex() )
				{
					OrigDisp = m_OrigMapDisp[ index ];
				}

				if ( OrigDisp )
				{
					pDisp = OrigDisp;
				}
			}

			int iTri = pDisp->CollideWithDispTri( vecStart, vecEnd, flFraction, false );
			if ( iTri != -1 && ( flLeastFraction == -1.0f || flFraction < flLeastFraction ) )
			{
				flLeastFraction = flFraction;
				vCollisionPoint = vecStart + ( ( vecEnd - vecStart ) * flFraction );

				unsigned short v1, v2, v3;
				Vector vec1, vec2, vec3;

				pDisp->GetTriIndices( iTri, v1, v2, v3 );
				pDisp->GetVert( v1, vec1 );
				pDisp->GetVert( v2, vec2 );
				pDisp->GetVert( v3, vec3 );

				ComputeTrianglePlane( vec1, vec2, vec3, vCollisionNormal, flCollisionIntercept );
			}
		}
	}

	return ( flLeastFraction != -1.0f );
}






//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CSculptPainter::CSculptPainter() :
	CSculptTool()
{
	m_InSizingMode = m_InPaintingMode = false;
	m_OrigBrushSize = m_BrushSize;
}


//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CSculptPainter::~CSculptPainter( )
{

}


//-----------------------------------------------------------------------------
// Purpose: setup for starting to paint on the displacement
// Input  : pView - the 3d view
//			vPoint - the initial click point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPainter::BeginPaint( CMapView3D *pView, const Vector2D &vPoint )
{
	CSculptTool::BeginPaint( pView, vPoint );

	PrepareDispForPainting();

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: main routine called when mouse move has happened to start painting
// Input  : pView - the 3d view
//			vPoint - the mouse point
//			SpatialData - the spatial data ( mostly ignored )
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPainter::Paint( CMapView3D *pView, const Vector2D &vPoint, SpatialPaintData_t &SpatialData )
{
	__super::Paint( pView, vPoint, SpatialData );

	if ( m_bRMBDown )
	{
		if ( !m_bAltDown )
		{
			DoSizing( vPoint );
		}
	}
	else if ( m_bLMBDown )
	{
		if ( !m_ValidPaintingSpot )
		{
			if ( !GetStartingSpot( pView, vPoint ) )
			{
				return false;
			}
		}

		// Setup painting.
		if ( !PrePaint( pView, vPoint ) )
		{
			return false;
		}

		// Handle painting.
		if ( !DoPaint( pView, vPoint ) )
		{
			return false;
		}

		// Finish painting.
		if ( !PostPaint( m_PaintOwner->GetAutoSew() ) )
		{
			return false;
		}
	}

	// Successful paint operation.
	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the left mouse button up in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPainter::OnLMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnLMouseUp3D( pView, nFlags, vPoint );

	m_InPaintingMode = false;

	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( pDispMgr )
	{
		pDispMgr->PostUndo();
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the left mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPainter::OnLMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnLMouseDown3D( pView, nFlags, vPoint );

	m_InPaintingMode = true;

	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( pDispMgr )
	{
		pDispMgr->PreUndo( "Displacement Modifier" );
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the right mouse button up in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPainter::OnRMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnRMouseUp3D( pView, nFlags, vPoint );

	m_InSizingMode = false;

	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( pDispMgr )
	{
		pDispMgr->PostUndo();
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the right mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPainter::OnRMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnRMouseDown3D( pView, nFlags, vPoint );

	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( pDispMgr )
	{
		pDispMgr->PreUndo( "Displacement Modifier" );
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the mouse move in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPainter::OnMouseMove3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	return CSculptTool::OnMouseMove3D( pView, nFlags, vPoint );
}


//-----------------------------------------------------------------------------
// Purpose: toggles the sizing mode
// Input  : vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPainter::DoSizing( const Vector2D &vPoint )
{
	if ( !m_InSizingMode )
	{
		m_InSizingMode = true;
		m_StartSizingPoint = vPoint;
		m_OrigBrushSize = m_BrushSize;
	}
	else
	{
		m_BrushSize = m_OrigBrushSize + ( vPoint.x - m_StartSizingPoint.x );
		if ( m_BrushSize < 1.0f )
		{
			m_BrushSize = 1.0f;
		}
	}

	return true;
}








// CSculptPushOptions dialog

IMPLEMENT_DYNAMIC(CSculptPushOptions, CDialog)


//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CSculptPushOptions::CSculptPushOptions(CWnd* pParent /*=NULL*/) : 
	CDialog(CSculptPushOptions::IDD, pParent),
	CSculptPainter()
{
	m_OffsetMode = OFFSET_MODE_ABSOLUTE;
	m_NormalMode = NORMAL_MODE_Z;
	m_DensityMode = DENSITY_MODE_ADDITIVE;
	m_OffsetDistance = 10.0f;
	m_OffsetAmount = 1.0f;
	m_SmoothAmount = 0.2f;
	m_Direction = 1.0f;
	m_SelectedNormal.Init( 0.0f, 0.0f, 0.0f );

	m_flFalloffSpot = 0.5f;
	m_flFalloffEndingValue = 0.0f;
}


//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CSculptPushOptions::~CSculptPushOptions()
{
}


//-----------------------------------------------------------------------------
// Purpose: initializes the dialog
// Output : returns true if successful
//-----------------------------------------------------------------------------
BOOL CSculptPushOptions::OnInitDialog( void )
{
	char	temp[ 1024 ];

	CDialog::OnInitDialog();

	m_OffsetModeControl.InsertString( -1, "Adaptive" );
	m_OffsetModeControl.InsertString( -1, "Absolute" );
	m_OffsetModeControl.SetCurSel( m_OffsetMode );

	m_OffsetDistanceControl.EnableWindow( ( m_OffsetMode == OFFSET_MODE_ABSOLUTE ) );
	m_OffsetAmountControl.EnableWindow( ( m_OffsetMode == OFFSET_MODE_ADAPTIVE ) ); 

	sprintf( temp, "%g", m_OffsetDistance );
	m_OffsetDistanceControl.SetWindowText( temp );

	sprintf( temp, "%g%%", m_OffsetAmount * 100.0f );
	m_OffsetAmountControl.SetWindowText( temp );

	sprintf( temp, "%g%%", m_SmoothAmount * 100.0f );
	m_SmoothAmountControl.SetWindowText( temp );

	sprintf( temp, "%g%%", m_flFalloffSpot * 100.0f );
	m_FalloffPositionControl.SetWindowText( temp );

	sprintf( temp, "%g%%", m_flFalloffEndingValue * 100.0f );
	m_FalloffFinalControl.SetWindowText( temp );

	m_NormalModeControl.InsertString( -1, "Brush Center" );
	m_NormalModeControl.InsertString( -1, "Screen" );
	m_NormalModeControl.InsertString( -1, "Screen XY" );
	m_NormalModeControl.InsertString( -1, "X" );
	m_NormalModeControl.InsertString( -1, "Y" );
	m_NormalModeControl.InsertString( -1, "Z" );
	m_NormalModeControl.InsertString( -1, "Selected" );
	m_NormalModeControl.SetCurSel( m_NormalMode );

	m_DensityModeControl.InsertString( -1, "Additive" );
	m_DensityModeControl.InsertString( -1, "Attenuated" );
	m_DensityModeControl.SetCurSel( m_DensityMode );

	return TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: prevent the dialog from closing
//-----------------------------------------------------------------------------
void CSculptPushOptions::OnOK()
{
}


//-----------------------------------------------------------------------------
// Purpose: prevent the dialog from closing
//-----------------------------------------------------------------------------
void CSculptPushOptions::OnCancel()
{
}


//-----------------------------------------------------------------------------
// Purpose: set up the data exchange for the variables
// Input  : pDX - the data exchange object
//-----------------------------------------------------------------------------
void CSculptPushOptions::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_OFFSET_MODE, m_OffsetModeControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_OFFSET_DISTANCE, m_OffsetDistanceControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_OFFSET_AMOUNT, m_OffsetAmountControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_SMOOTH_AMOUNT, m_SmoothAmountControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_DENSITY_MODE, m_DensityModeControl);
	DDX_Control(pDX, IDC_IDC_SCULPT_PUSH_OPTION_NORMAL_MODE, m_NormalModeControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_FALLOFF_POSITION, m_FalloffPositionControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_FALLOFF_FINAL, m_FalloffFinalControl);
}


BEGIN_MESSAGE_MAP(CSculptPushOptions, CDialog)
	ON_CBN_SELCHANGE(IDC_IDC_SCULPT_PUSH_OPTION_NORMAL_MODE, &CSculptPushOptions::OnCbnSelchangeIdcSculptPushOptionNormalMode)
	ON_CBN_SELCHANGE(IDC_SCULPT_PUSH_OPTION_OFFSET_MODE, &CSculptPushOptions::OnCbnSelchangeSculptPushOptionOffsetMode)
	ON_EN_CHANGE(IDC_SCULPT_PUSH_OPTION_OFFSET_DISTANCE, &CSculptPushOptions::OnEnChangeSculptPushOptionOffsetDistance)
	ON_CBN_SELCHANGE(IDC_SCULPT_PUSH_OPTION_DENSITY_MODE, &CSculptPushOptions::OnCbnSelchangeSculptPushOptionDensityMode)
	ON_EN_KILLFOCUS(IDC_SCULPT_PUSH_OPTION_SMOOTH_AMOUNT, &CSculptPushOptions::OnEnKillfocusSculptPushOptionSmoothAmount)
	ON_EN_KILLFOCUS(IDC_SCULPT_PUSH_OPTION_OFFSET_AMOUNT, &CSculptPushOptions::OnEnKillfocusSculptPushOptionOffsetAmount)
	ON_EN_KILLFOCUS(IDC_SCULPT_PUSH_OPTION_FALLOFF_POSITION, &CSculptPushOptions::OnEnKillfocusSculptPushOptionFalloffPosition)
	ON_EN_KILLFOCUS(IDC_SCULPT_PUSH_OPTION_FALLOFF_FINAL, &CSculptPushOptions::OnEnKillfocusSculptPushOptionFalloffFinal)
END_MESSAGE_MAP()


//-----------------------------------------------------------------------------
// Purpose: sets the normal mode of the sculpt operation
//-----------------------------------------------------------------------------
void CSculptPushOptions::OnCbnSelchangeIdcSculptPushOptionNormalMode()
{
	m_NormalMode = ( NormalMode )m_NormalModeControl.GetCurSel();
}


//-----------------------------------------------------------------------------
// Purpose: sets the offset mode of the sculpt operation
//-----------------------------------------------------------------------------
void CSculptPushOptions::OnCbnSelchangeSculptPushOptionOffsetMode()
{
	m_OffsetMode = ( OffsetMode )m_OffsetModeControl.GetCurSel();

	m_OffsetDistanceControl.EnableWindow( ( m_OffsetMode == OFFSET_MODE_ABSOLUTE ) );
	m_OffsetAmountControl.EnableWindow( ( m_OffsetMode == OFFSET_MODE_ADAPTIVE ) ); 
}


//-----------------------------------------------------------------------------
// Purpose: setup for starting to paint on the displacement
// Input  : pView - the 3d view
//			vPoint - the initial click point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPushOptions::BeginPaint( CMapView3D *pView, const Vector2D &vPoint )
{
	__super::BeginPaint( pView, vPoint );

	if ( m_bCtrlDown )
	{
		m_Direction = -1.0f;
	}
	else
	{
		m_Direction = 1.0f;
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: draws the tool in the 3d view
// Input  : pRender - the 3d renderer
//-----------------------------------------------------------------------------
void CSculptPushOptions::RenderTool3D( CRender3D *pRender )
{
//	pRender->DrawText( "mouse", m_MousePoint.x, m_MousePoint.y, 0 );
//	Msg( "%g %g\n", m_MousePoint.x, m_MousePoint.y );

	pRender->PushRenderMode( RENDER_MODE_WIREFRAME );

	if ( m_InSizingMode )
	{	// yellow for sizing mode
		pRender->BeginClientSpace();
		pRender->SetDrawColor( 255, 255, 0 );
		pRender->DrawCircle( Vector( m_StartSizingPoint.x, m_StartSizingPoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize, 32 );
		if ( m_flFalloffSpot > 0.0f )
		{
			pRender->SetDrawColor( 192, 192, 0 );
			pRender->DrawCircle( Vector( m_StartSizingPoint.x, m_StartSizingPoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize * m_flFalloffSpot, 32 );
		}
		pRender->EndClientSpace();
	}
	else if ( m_bShiftDown )
	{	// purple for smoothing
		pRender->SetDrawColor( 255, 0, 255 );
		pRender->BeginClientSpace();
		pRender->DrawCircle( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize, 32 );
		pRender->EndClientSpace();
	}
	else if ( m_bCtrlDown )
	{	// red for negative sculpting
		pRender->BeginClientSpace();
		pRender->SetDrawColor( 255, 0, 0 );
		pRender->DrawCircle( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize, 32 );
		if ( m_flFalloffSpot > 0.0f )
		{
			pRender->SetDrawColor( 192, 0, 0 );
			pRender->DrawCircle( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize * m_flFalloffSpot, 32 );
		}
		pRender->EndClientSpace();

		Vector	vPaintAxis;
		GetPaintAxis( pRender->GetCamera(), m_MousePoint, vPaintAxis );
		DrawDirection( pRender, -vPaintAxis, Color( 255, 255, 255 ), Color( 255, 128, 128 ) );
	}
	else
	{	// green for positive sculpting
		pRender->BeginClientSpace();
		pRender->SetDrawColor( 0, 255, 0 );
		pRender->DrawCircle( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize, 32 );
		if ( m_flFalloffSpot > 0.0f )
		{
			pRender->SetDrawColor( 0, 192, 0 );
			pRender->DrawCircle( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize * m_flFalloffSpot, 32 );
		}
		pRender->EndClientSpace();

		Vector	vPaintAxis;
		GetPaintAxis( pRender->GetCamera(), m_MousePoint, vPaintAxis );
		DrawDirection( pRender, vPaintAxis, Color( 255, 255, 255 ), Color( 255, 128, 128 ) );
	}

#if 0
	FindColissionIntercept( pRender->GetCamera(), m_MousePoint, true, m_CurrentCollisionPoint, m_CurrentCollisionNormal, m_CurrentCollisionIntercept );

	Vector2D	RadiusPoint = m_MousePoint;
	Vector		vecStart, vecEnd;

	RadiusPoint.x += m_BrushSize;
	pRender->GetCamera()->BuildRay( RadiusPoint, vecStart, vecEnd );

	m_CurrentProjectedRadius = CalcDistanceToLine( m_CurrentCollisionPoint, vecStart, vecEnd );

	pRender->RenderWireframeSphere( m_CurrentCollisionPoint, m_CurrentProjectedRadius, 12, 12, 0, 255, 255 );
#endif

#if 0

	// Get the displacement manager from the active map document.
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();

	// For each displacement surface is the selection list attempt to paint on it.
	int nDispCount = pDispMgr->SelectCount();
	for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
		if ( pDisp )
		{
			CMapDisp	*OrigDisp = NULL;
			int			index = m_OrigMapDisp.Find( pDisp->GetEditHandle() );

			if ( index != m_OrigMapDisp.InvalidIndex() )
			{
				OrigDisp = m_OrigMapDisp[ index ];
			}
			Vector	vPaintPos, vVert;

			int nVertCount = pDisp->GetSize();
			for ( int iVert = 0; iVert < nVertCount; iVert++ )
			{
				if ( IsPointInScreenCircle( pView, pDisp, pOrigDisp, iVert, false ) )
				{
					// Get the current vert.
					pDisp->GetVert( iVert, vVert );
				}
			}
		}
	}
#endif


	pRender->PopRenderMode();

#if 0
	if ( !FindColissionIntercept( pRender->GetCamera(), m_MousePoint, true, m_CurrentCollisionPoint, m_CurrentCollisionNormal, m_CurrentCollisionIntercept ) )
	{
		return;
	}

	Vector2D	RadiusPoint = m_MousePoint;
	Vector		vecStart, vecEnd;

	RadiusPoint.x += m_BrushSize;
	pRender->GetCamera()->BuildRay( RadiusPoint, vecStart, vecEnd );
	m_CurrentProjectedRadius = CalcDistanceToLine( m_CurrentCollisionPoint, vecStart, vecEnd );

	Msg( "Dist = %g at %g,%g,%g\n", m_CurrentProjectedRadius, m_CurrentCollisionPoint.x, m_CurrentCollisionPoint.y, m_CurrentCollisionPoint.z );
#endif
}


//-----------------------------------------------------------------------------
// Purpose: handles the right mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptPushOptions::OnRMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnRMouseDown3D( pView, nFlags, vPoint );

	if ( m_bAltDown )
	{
		m_NormalMode = NORMAL_MODE_Z;
		m_NormalModeControl.SetCurSel( m_NormalMode );

#if 0

		//
		// check for closest solid object
		//
		ULONG		ulFace;
		CMapClass	*pObject;

		if( ( ( pObject = pView->NearestObjectAt( vPoint, ulFace ) ) != NULL ) )
		{
			if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) )
			{
				// get the solid
				CMapSolid *pSolid = ( CMapSolid* )pObject;
				if( !pSolid )
				{
					return true;
				}

				// trace a line and get the normal -- will get a displacement normal
				// if one exists
				CMapFace *pFace = pSolid->GetFace( ulFace );
				if( !pFace )
				{
					return true;
				}

				Vector vRayStart, vRayEnd;
				pView->GetCamera()->BuildRay( vPoint, vRayStart, vRayEnd );

				Vector vHitPos, vHitNormal;
				if( pFace->TraceLine( vHitPos, vHitNormal, vRayStart, vRayEnd ) )
				{
					// set the paint direction
					m_SelectedNormal = vHitNormal;

					m_NormalMode = NORMAL_MODE_SELECTED;
					m_NormalModeControl.SetCurSel( m_NormalMode );
				}
			}
		}
#else
		Vector	CollisionPoint, CollisionNormal;
		float	CollisionIntercept;

		if ( FindCollisionIntercept( pView->GetCamera(), vPoint, false, CollisionPoint, CollisionNormal, CollisionIntercept ) )
		{
			// set the paint direction
			m_SelectedNormal = -CollisionNormal;

			m_NormalMode = NORMAL_MODE_SELECTED;
			m_NormalModeControl.SetCurSel( m_NormalMode );
		}
#endif
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: returns the painting direction
// Input  : pCamera - the 3d camera
//			vPoint - the 2d mouse point
// Output : vPaintAxis - the direction the painting should go
//-----------------------------------------------------------------------------
void CSculptPushOptions::GetPaintAxis( CCamera *pCamera, const Vector2D &vPoint, Vector &vPaintAxis )
{
	switch( m_NormalMode )
	{
		case NORMAL_MODE_SCREEN:
			pCamera->GetViewForward( vPaintAxis );
			vPaintAxis = -vPaintAxis;
			break;

		case NORMAL_MODE_SCREEN_XY:
			pCamera->GetViewForward( vPaintAxis );
			vPaintAxis = -vPaintAxis;
			vPaintAxis.z = 0.f;
			break;

		case NORMAL_MODE_BRUSH_CENTER:
			if ( !m_InPaintingMode )
			{
				Vector	CollisionPoint, CollisionNormal;
				float	CollisionIntercept;

				FindCollisionIntercept( pCamera, vPoint, false, CollisionPoint, CollisionNormal, CollisionIntercept );

				vPaintAxis = -CollisionNormal;
			}
			else
			{
				vPaintAxis = -m_StartingCollisionNormal;
			}
			break;

		case NORMAL_MODE_X:
			vPaintAxis.Init( 1.0f, 0.0f, 0.0f );
			break;

		case NORMAL_MODE_Y:
			vPaintAxis.Init( 0.0f, 1.0f, 0.0f );
			break;

		case NORMAL_MODE_Z:
			vPaintAxis.Init( 0.0f, 0.0f, 1.0f );
			break;

		case NORMAL_MODE_SELECTED:
			vPaintAxis = m_SelectedNormal;
			break;

		default:
			vPaintAxis.Init( 0.0f, 0.0f, 1.0f );
	}
}


//-----------------------------------------------------------------------------
// Purpose: applies the specific push operation onto the displacement
// Input  : pView - the 3d view
//			vPoint - the mouse point
//			pDisp - the displacement to apply the push to
//			pOrigDisp - the original displacement prior to any adjustments
//-----------------------------------------------------------------------------
void CSculptPushOptions::DoPaintOperation( CMapView3D *pView, const Vector2D &vPoint, CMapDisp *pDisp, CMapDisp *pOrigDisp )
{
	Vector	vPaintPos, vVert, vDirection;
	float	flMaxDistance = 0.0f;
	float	flDistance;
	float	flLengthPercent;
	Vector	vPaintAxis;

	if ( m_bShiftDown )
	{
//		DoSmoothOperation( pView, vPoint, pDisp, pOrigDisp );
//		m_SpatialData.m_flRadius = 256.0f;
//		m_SpatialData.m_flScalar = 5.0f / m_SmoothAmount;

//		m_SpatialData.m_flRadius = m_StartingProjectedRadius * 1.5f;
		m_SpatialData.m_flRadius = m_CurrentProjectedRadius * 2.0f;
		m_SpatialData.m_flRadius2 = ( m_SpatialData.m_flRadius * m_SpatialData.m_flRadius );
		m_SpatialData.m_flOORadius2 = 1.0f / m_SpatialData.m_flRadius2;
		m_SpatialData.m_flScalar = 10.0f / m_SmoothAmount;
		m_SpatialData.m_vCenter = m_CurrentCollisionPoint;

		DoPaintSmooth( pView, vPoint, pDisp, pOrigDisp );
		return;
	}

	GetPaintAxis( pView->GetCamera(), vPoint, vPaintAxis );

	vDirection = vPaintAxis * m_Direction;

	switch( m_OffsetMode )
	{
		case OFFSET_MODE_ADAPTIVE:
			flMaxDistance = m_StartingProjectedRadius * m_OffsetAmount;
			break;
		case OFFSET_MODE_ABSOLUTE:
			flMaxDistance = m_OffsetDistance;
			break;
	}

	int nVertCount = pDisp->GetSize();
	for ( int iVert = 0; iVert < nVertCount; iVert++ )
	{
		if ( IsPointInScreenCircle( pView, pDisp, pOrigDisp, iVert, true, false, &flLengthPercent ) )
		{
			pDisp->GetVert( iVert, vVert );

			if ( flLengthPercent > m_flFalloffSpot )
			{
				flLengthPercent = ( flLengthPercent - m_flFalloffSpot ) / ( 1.0f - m_flFalloffSpot );
				flLengthPercent = 1.0 - flLengthPercent;
				flDistance = ( ( 1.0f - m_flFalloffEndingValue ) * flLengthPercent * flMaxDistance ) + ( m_flFalloffEndingValue * flMaxDistance );
			}
			else
			{
				flDistance = flMaxDistance;
			}

			if ( flDistance == 0.0f )
			{
				continue;
			}

			switch( m_DensityMode )
			{
				case DENSITY_MODE_ADDITIVE:
					VectorScale( vDirection, flDistance, vPaintPos );
					VectorAdd( vPaintPos, vVert, vPaintPos );
					break;

				case DENSITY_MODE_ATTENUATED:
					VectorScale( vDirection, flDistance, vPaintPos );
					VectorAdd( vPaintPos, vVert, vPaintPos );

					if ( pOrigDisp )
					{
						Vector	vOrigVert, vDiff;
						float	Length;

						pOrigDisp->GetVert( iVert, vOrigVert );
						vDiff = ( vPaintPos - vOrigVert );
						Length = vDiff.Length() / flMaxDistance;
						if ( Length > 1.0f )
						{
							Length = 1.0f;
						}

						vPaintPos = vOrigVert + ( Length * vDirection * flMaxDistance );
					}
					break;
			}

			AddToUndo( &pDisp );
			pDisp->Paint_SetValue( iVert, vPaintPos );
		}
	}
}


#if 0

typedef enum
{
	DISP_DIR_LEFT_TO_RIGHT = 0,		// adjoining displacement is to the left
	DISP_DIR_TOP_TO_BOTTOM = 1,		// adjoining displacement is to the top
	DISP_DIR_RIGHT_TO_LEFT = 2,		// adjoining displacement is to the right
	DISP_DIR_BOTTOM_TO_TOP = 3,		// adjoining displacement is to the bottom
} DispDirections;

typedef enum
{
	MOVE_DIR_RIGHT = 0,
	MOVE_DIR_UP,
	MOVE_DIR_LEFT,
	MOVE_DIR_DOWN,

	MOVE_DIR_MAX
} MoveDirections;


class CDispGrid
{
public:
	CDispGrid( CMapDisp	*pDisp, bool DoPopulate = false, int GridExpand = 2 );
	~CDispGrid( );

	void Populate( CMapDisp	*pDisp );
	bool GetPosition( int x, int y, int OffsetX, int OffsetY, Vector &Position );
	bool GetFlatPosition( int x, int y, int OffsetX, int OffsetY, Vector &FlatPosition );

	void SetPosition( int x, int y, Vector &NewPosition );
	void UpdatePositions( void );

	void CalcSpringForce( int x, int y, int OffsetX, int OffsetY, float Ks, Vector &SpringForce );


private:
	typedef struct SDispPoint
	{
		bool	m_IsSet;
		int		m_DispPos;
		Vector	m_Position, m_UpdatePosition;
		Vector	m_FlatPosition;
	} TDispPoint;

	int			m_Width, m_Height;
	int			m_GridWidth, m_GridHeight;
	int			m_GridExpand;
	TDispPoint	*m_Grid;

	void PopulateUp( CMapDisp *pDisp );
	void PopulateDown( CMapDisp *pDisp );
	void PopulateRight( CMapDisp *pDisp );
	void PopulateLeft( CMapDisp *pDisp );
};

CDispGrid::CDispGrid( CMapDisp *pDisp, bool DoPopulate, int GridExpand )
{
	m_GridExpand = GridExpand;
	m_Width = pDisp->GetWidth();
	m_Height = pDisp->GetHeight();
	m_GridWidth = m_Width + ( GridExpand * 2 );
	m_GridHeight = m_Height + ( GridExpand * 2 );

	m_Grid = new TDispPoint[ m_GridWidth * m_GridHeight ];
	for( int i = 0; i < m_GridWidth * m_GridHeight; i++ )
	{
		m_Grid[ i ].m_IsSet = false;
	}

	if ( DoPopulate )
	{
		Populate( pDisp );
	}
}

CDispGrid::~CDispGrid( )
{
	delete [] m_Grid;
}

void CDispGrid::PopulateUp( CMapDisp *pDisp )
{
	EditDispHandle_t handle;
	int orient;

	pDisp->GetEdgeNeighbor( DISP_DIR_TOP_TO_BOTTOM, handle, orient );
	if ( handle == EDITDISPHANDLE_INVALID )
	{
		return;
	}
	pDisp = EditDispMgr()->GetDisp( handle );

	if ( pDisp->GetWidth() != m_Width || pDisp->GetHeight() != m_Height )
	{	// don't support ones which aren't of the same subdivision
		return;
	}

	if ( orient != MOVE_DIR_DOWN )
	{	// don't support rotation for now
		return;
	}


	for( int x = 0; x < m_Width; x++ )
	{
		for( int y = 0; y < m_GridExpand; y++ )
		{
			int GridPos = ( ( m_GridHeight - y - 1 ) * m_GridWidth ) + ( x + m_GridExpand );

			m_Grid[ GridPos ].m_DispPos = ( ( m_GridExpand - y ) * m_Width ) + x;		// don't do inner row, as that is sewed
			pDisp->GetVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_Position );
			m_Grid[ GridPos ].m_UpdatePosition = m_Grid[ GridPos ].m_Position;
			pDisp->GetFlatVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_FlatPosition );
			m_Grid[ GridPos ].m_IsSet = true;
		}
	}
}

void CDispGrid::PopulateDown( CMapDisp *pDisp )
{
	EditDispHandle_t handle;
	int orient;

	pDisp->GetEdgeNeighbor( DISP_DIR_BOTTOM_TO_TOP, handle, orient );
	if ( handle == EDITDISPHANDLE_INVALID )
	{
		return;
	}
	pDisp = EditDispMgr()->GetDisp( handle );

	if ( pDisp->GetWidth() != m_Width || pDisp->GetHeight() != m_Height )
	{	// don't support ones which aren't of the same subdivision
		return;
	}

	if ( orient != MOVE_DIR_UP )
	{	// don't support rotation for now
		return;
	}


	for( int x = 0; x < m_Width; x++ )
	{
		for( int y = 0; y < m_GridExpand; y++ )
		{
			int GridPos = ( ( y ) * m_GridWidth ) + ( x + m_GridExpand );

			m_Grid[ GridPos ].m_DispPos = ( ( m_Height - m_GridExpand + y - 1 ) * m_Width ) + x;		// don't do inner row, as that is sewed
			pDisp->GetVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_Position );
			m_Grid[ GridPos ].m_UpdatePosition = m_Grid[ GridPos ].m_Position;
			pDisp->GetFlatVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_FlatPosition );
			m_Grid[ GridPos ].m_IsSet = true;
		}
	}
}

void CDispGrid::PopulateRight( CMapDisp *pDisp )
{
	EditDispHandle_t handle;
	int orient;

	pDisp->GetEdgeNeighbor( DISP_DIR_RIGHT_TO_LEFT, handle, orient );
	if ( handle == EDITDISPHANDLE_INVALID )
	{
		return;
	}
	pDisp = EditDispMgr()->GetDisp( handle );

	if ( pDisp->GetWidth() != m_Width || pDisp->GetHeight() != m_Height )
	{	// don't support ones which aren't of the same subdivision
		return;
	}

	if ( orient != MOVE_DIR_RIGHT )
	{	// don't support rotation for now
		return;
	}


	for( int x = 0; x < m_GridExpand; x++ )
	{
		for( int y = 0; y < m_Height; y++ )
		{
			int GridPos = ( ( y + m_GridExpand ) * m_GridWidth ) + ( x + m_GridExpand + m_Width );

			m_Grid[ GridPos ].m_DispPos = ( ( y ) * m_Width ) + x + 1;		// don't do inner row, as that is sewed
			pDisp->GetVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_Position );
			m_Grid[ GridPos ].m_UpdatePosition = m_Grid[ GridPos ].m_Position;
			pDisp->GetFlatVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_FlatPosition );
			m_Grid[ GridPos ].m_IsSet = true;
		}
	}
}

void CDispGrid::PopulateLeft( CMapDisp *pDisp )
{
	EditDispHandle_t handle;
	int orient;

	pDisp->GetEdgeNeighbor( DISP_DIR_LEFT_TO_RIGHT, handle, orient );
	if ( handle == EDITDISPHANDLE_INVALID )
	{
		return;
	}
	pDisp = EditDispMgr()->GetDisp( handle );

	if ( pDisp->GetWidth() != m_Width || pDisp->GetHeight() != m_Height )
	{	// don't support ones which aren't of the same subdivision
		return;
	}

	if ( orient != MOVE_DIR_LEFT )
	{	// don't support rotation for now
		return;
	}


	for( int x = 0; x < m_GridExpand; x++ )
	{
		for( int y = 0; y < m_Height; y++ )
		{
			int GridPos = ( ( y + m_GridExpand ) * m_GridWidth ) + ( x );

			m_Grid[ GridPos ].m_DispPos = ( ( y ) * m_Width ) + ( m_Width - m_GridExpand + x - 1 );		// don't do inner row, as that is sewed
			pDisp->GetVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_Position );
			m_Grid[ GridPos ].m_UpdatePosition = m_Grid[ GridPos ].m_Position;
			pDisp->GetFlatVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_FlatPosition );
			m_Grid[ GridPos ].m_IsSet = true;
		}
	}
}

void CDispGrid::Populate( CMapDisp *pDisp )
{
	for( int x = 0; x < m_Width; x++ )
	{
		for( int y = 0; y < m_Height; y++ )
		{
			int GridPos = ( ( y + m_GridExpand ) * m_GridWidth ) + ( x + m_GridExpand );

			m_Grid[ GridPos ].m_DispPos = ( y * m_Width ) + x;
			pDisp->GetVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_Position );
			m_Grid[ GridPos ].m_UpdatePosition = m_Grid[ GridPos ].m_Position;
			pDisp->GetFlatVert( m_Grid[ GridPos ].m_DispPos, m_Grid[ GridPos ].m_FlatPosition );
			m_Grid[ GridPos ].m_IsSet = true;
		}
	}

	PopulateUp( pDisp );
	PopulateDown( pDisp );
	PopulateRight( pDisp );
	PopulateLeft( pDisp );
}

bool CDispGrid::GetPosition( int x, int y, int OffsetX, int OffsetY, Vector &Position )
{
	x += OffsetX;
	y += OffsetY;

	int GridPos = ( ( y + m_GridExpand ) * m_GridWidth ) + ( x + m_GridExpand );

	if ( !m_Grid[ GridPos ].m_IsSet )
	{
		return false;
	}

	Position = m_Grid[ GridPos ].m_Position;

	return true;
}

bool CDispGrid::GetFlatPosition( int x, int y, int OffsetX, int OffsetY, Vector &FlatPosition )
{
	x += OffsetX;
	y += OffsetY;

	int GridPos = ( ( y + m_GridExpand ) * m_GridWidth ) + ( x + m_GridExpand );

	if ( !m_Grid[ GridPos ].m_IsSet )
	{
		return false;
	}

	FlatPosition = m_Grid[ GridPos ].m_FlatPosition;

	return true;
}

void CDispGrid::SetPosition( int x, int y, Vector &NewPosition )
{
	int GridPos = ( ( y + m_GridExpand ) * m_GridWidth ) + ( x + m_GridExpand );

	if ( !m_Grid[ GridPos ].m_IsSet )
	{
		return;
	}

	m_Grid[ GridPos ].m_UpdatePosition = NewPosition;
}

void CDispGrid::UpdatePositions( void )
{
	for( int i = 0; i < m_GridWidth * m_GridHeight; i++ )
	{
		m_Grid[ i ].m_Position = m_Grid[ i ].m_UpdatePosition ;
	}
}

void CDispGrid::CalcSpringForce( int x, int y, int OffsetX, int OffsetY, float Ks, Vector &SpringForce )
{
	Vector	currentP1, currentP2;
	Vector	restP1, restP2;
	Vector	currentDelta, restDelta;
	float	currentDistance, restDistance;

	SpringForce.Init();

	if ( !GetPosition( x, y, 0, 0, currentP1 ) )
	{
		return;
	}
	if ( !GetPosition( x, y, OffsetX, OffsetY, currentP2 ) )
	{
		return;
	}
	if ( !GetFlatPosition( x, y, 0, 0, restP1 ) )
	{
		return;
	}
	if ( !GetFlatPosition( x, y, OffsetX, OffsetY, restP2 ) )
	{
		return;
	}

	currentDelta = currentP1 - currentP2;
	currentDistance = currentDelta.Length();

	if ( currentDistance == 0.0f )
	{
		return;
	}

	restDelta = restP1 - restP2;
	restDistance = restDelta.Length();

	float Hterm = (currentDistance - restDistance) * Ks;

	// VectorDifference(&p1->v,&p2->v,&deltaV);		// Delta Velocity Vector
	// Dterm = (DotProduct(&deltaV,&deltaP) * spring->Kd) / dist; // Damping Term
	float	Dterm = 0.0f;


	SpringForce = currentDelta * ( 1.0f / currentDistance );
	SpringForce = SpringForce * -(Hterm + Dterm);


	//VectorSum(&p1->f,&springForce,&p1->f);			// Apply to Particle 1
	//VectorDifference(&p2->f,&springForce,&p2->f);	// - Force on Particle 2
}


void CSculptPushOptions::DoSmoothOperation( CMapView3D *pView, const Vector2D &vPoint, CMapDisp *pDisp, CMapDisp *pOrigDisp )
{
	Vector	SpringForce;
	int width = pDisp->GetWidth();
	int height = pDisp->GetHeight();
	Vector	*Forces = ( Vector * )_alloca( sizeof( *Forces ) * width * height );
	bool	*DoCalc = ( bool * )_alloca( sizeof( *DoCalc ) * width * height );

	const float SPRING_CONSTANT	= 0.02f;
	const float SPRING_CONSTANT_TO_NORMAL = 0.4f;

	Vector	SurfaceNormal;

	pDisp->GetSurfNormal( SurfaceNormal );


	for( int x = 0; x < width; x++ )
	{
		for( int y = 0; y < height; y++ )
		{
			int		pVert = ( x * width ) + y;
			Vector	pos, vTestVert;

			pDisp->GetVert( pVert, pos );

			if ( pOrigDisp && 0 )
			{
				pOrigDisp->GetVert( pVert, vTestVert );
			}
			else
			{
				vTestVert = pos;
			}

			Vector2D ViewVert;
			pView->GetCamera()->WorldToView( vTestVert, ViewVert );

			Vector2D	Offset = ViewVert - m_MousePoint;
			float		Length = Offset.Length();
			if ( Length <= m_BrushSize || 0 )
			{
				DoCalc[ pVert ] = true;
			}
			else
			{
				DoCalc[ pVert ] = false;
			}
		}
	}

#if 0
	EditDispHandle_t handle;
	int orient;
	for( int i = 0; i < 4; i++ )
	{
		pDisp->GetEdgeNeighbor( i, handle, orient );
		if ( handle != EDITDISPHANDLE_INVALID )
		{
			Msg( "Handle at %d orient %d\n", i, orient );
		}
	}

	int x = 0;
	int y = 0;
	CMapDisp *pNextDisp = pDisp;
	Vector Vert;
	Vector FlatVert;

	while( 1 )
	{
		if ( !GetAdjoiningPoint( x, y, MOVE_DIR_UP, 1, pNextDisp, Vert, FlatVert ) || pDisp != pNextDisp )
		{
			break;
		}

		y++;
	}

	return;
#endif

	CDispGrid	DispGrid( pDisp, true );

	const float	StepAmount = 1.0f;

	float	CurrentSmooth = m_SmoothAmount;
	while( CurrentSmooth > 0.0f )
	{
		float SpringAmount;
		float SpringToNormalAmount;
		if ( CurrentSmooth > StepAmount )
		{
			SpringAmount = SPRING_CONSTANT * StepAmount;
			SpringToNormalAmount = SPRING_CONSTANT_TO_NORMAL * StepAmount;
		}
		else
		{
			SpringAmount = SPRING_CONSTANT * CurrentSmooth;
			SpringToNormalAmount = SPRING_CONSTANT_TO_NORMAL * CurrentSmooth;
		}
		CurrentSmooth -= StepAmount;

		for( int x = 0; x < width; x++ )
		{
			for( int y = 0; y < height; y++ )
			{
				int pVert = ( y * width ) + x;

				if ( !DoCalc[ pVert ] )
				{
					continue;
				}

				Forces[ pVert ].Init();

				// structural springs
				DispGrid.CalcSpringForce( x, y, 1, 0, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, -1, 0, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, 0, 1, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, 0, -1, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;


				// shear springs
				DispGrid.CalcSpringForce( x, y, 1, 1, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, -1, 1, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, 1, -1, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, -1, -1, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				// bend springs
				DispGrid.CalcSpringForce( x, y, 2, 0, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, -2, 0, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, 0, 2, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				DispGrid.CalcSpringForce( x, y, 0, -2, SpringAmount, SpringForce );
				Forces[ pVert ] += SpringForce;

				Vector	Vert, FlatVert, FlatVertExtended, ClosestPoint;

				DispGrid.GetPosition( x, y, 0, 0, Vert );
				DispGrid.GetFlatPosition( x, y, 0, 0, FlatVert );

				FlatVertExtended = FlatVert + ( SurfaceNormal * 10.0f );
				CalcClosestPointOnLine( Vert, FlatVert, FlatVertExtended, ClosestPoint );
				Vector	Difference = ( Vert - ClosestPoint );
				float Distance = Difference.Length();

				if ( Distance > 0.0f )
				{
					float Hterm = Distance * SpringToNormalAmount;
					float	Dterm = 0.0f;

					SpringForce = ( Difference ) * ( 1.0f / Distance );
					SpringForce = SpringForce * -(Hterm + Dterm);
					Forces[ pVert ] += SpringForce;
				}

				Vector	pos;

				DispGrid.GetPosition( x, y, 0, 0, pos );
				pos += Forces[ pVert ];

				AddToUndo( &pDisp );
				pDisp->Paint_SetValue( pVert, pos );

				DispGrid.SetPosition( x, y, pos );
			}
		}
		DispGrid.UpdatePositions();
	}
}
#endif


//-----------------------------------------------------------------------------
// Purpose: sets the offset distance
//-----------------------------------------------------------------------------
void CSculptPushOptions::OnEnChangeSculptPushOptionOffsetDistance()
{
	char	temp[ 1024 ];

	m_OffsetDistanceControl.GetWindowText( temp, sizeof( temp ) );
	m_OffsetDistance = atof( temp );
}


//-----------------------------------------------------------------------------
// Purpose: sets the density mode
//-----------------------------------------------------------------------------
void CSculptPushOptions::OnCbnSelchangeSculptPushOptionDensityMode()
{
	m_DensityMode = ( DensityMode )m_DensityModeControl.GetCurSel();
}


//-----------------------------------------------------------------------------
// Purpose: sets the smooth amount
//-----------------------------------------------------------------------------
void CSculptPushOptions::OnEnKillfocusSculptPushOptionSmoothAmount()
{
	char	temp[ 1024 ], t2[ 1024 ];

	m_SmoothAmountControl.GetWindowText( temp, sizeof( temp ) );
	sscanf( temp, "%f%%", &m_SmoothAmount );
	m_SmoothAmount /= 100.0f;

	if ( m_SmoothAmount <= 0.0f )
	{
		m_SmoothAmount = 0.2f;
	}

	sprintf( t2, "%g%%", m_SmoothAmount * 100.0f );

	if ( strcmpi( temp, t2 ) != 0 )
	{
		m_SmoothAmountControl.SetWindowText( t2 );
	}
}


//-----------------------------------------------------------------------------
// Purpose: sets the offset amount
//-----------------------------------------------------------------------------
void CSculptPushOptions::OnEnKillfocusSculptPushOptionOffsetAmount()
{
	char	temp[ 1024 ], t2[ 1024 ];

	m_OffsetAmountControl.GetWindowText( temp, sizeof( temp ) );
	sscanf( temp, "%f%%", &m_OffsetAmount );
	m_OffsetAmount /= 100.0f;

	if ( m_OffsetAmount <= 0.0f )
	{
		m_OffsetAmount = 1.0f;
	}

	sprintf( t2, "%g%%", m_OffsetAmount * 100.0f );

	if ( strcmpi( temp, t2 ) != 0 )
	{
		m_OffsetAmountControl.SetWindowText( t2 );
	}
}

void CSculptPushOptions::OnEnKillfocusSculptPushOptionFalloffPosition()
{
	char	temp[ 1024 ], t2[ 1024 ];

	m_FalloffPositionControl.GetWindowText( temp, sizeof( temp ) );
	sscanf( temp, "%f%%", &m_flFalloffSpot );
	m_flFalloffSpot /= 100.0f;

	if ( m_flFalloffSpot <= 0.0f )
	{
		m_flFalloffSpot = 0.0f;
	}

	if ( m_flFalloffSpot > 1.0f )
	{
		m_flFalloffSpot = 1.0f;
	}

	sprintf( t2, "%g%%", m_flFalloffSpot * 100.0f );

	if ( strcmpi( temp, t2 ) != 0 )
	{
		m_FalloffPositionControl.SetWindowText( t2 );
	}
}

void CSculptPushOptions::OnEnKillfocusSculptPushOptionFalloffFinal()
{
	char	temp[ 1024 ], t2[ 1024 ];

	m_FalloffFinalControl.GetWindowText( temp, sizeof( temp ) );
	sscanf( temp, "%f%%", &m_flFalloffEndingValue);
	m_flFalloffEndingValue /= 100.0f;

	if ( m_flFalloffEndingValue <= 0.0f )
	{
		m_flFalloffEndingValue = 0.0f;
	}

	if ( m_flFalloffEndingValue > 1.0f )
	{
		m_flFalloffEndingValue = 1.0f;
	}

	sprintf( t2, "%g%%", m_flFalloffEndingValue * 100.0f );

	if ( strcmpi( temp, t2 ) != 0 )
	{
		m_FalloffFinalControl.SetWindowText( t2 );
	}
}






// CSculptCarveOptions dialog

IMPLEMENT_DYNAMIC(CSculptCarveOptions, CDialog)


//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CSculptCarveOptions::CSculptCarveOptions(CWnd* pParent /*=NULL*/) : 
	CDialog(CSculptCarveOptions::IDD, pParent),
	CSculptPainter()
{
	m_OffsetMode = OFFSET_MODE_ABSOLUTE;
	m_NormalMode = NORMAL_MODE_Z;
	m_DensityMode = DENSITY_MODE_ADDITIVE;
	m_OffsetDistance = 10.0f;
	m_OffsetAmount = 1.0f;
	m_SmoothAmount = 0.2f;
	m_Direction = 1.0f;
	m_SelectedNormal.Init( 0.0f, 0.0f, 0.0f );
	m_BrushLocation = -1;
	m_StartLine.Init( -1.0f, -1.0f );
	m_EndLine.Init( -1.0f, -1.0f );

	for( int i = 0; i < MAX_SCULPT_SIZE; i++ )
	{
		m_BrushPoints[ i ] = ( i / ( float )MAX_SCULPT_SIZE ); // 0.0f;
	}
}


//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CSculptCarveOptions::~CSculptCarveOptions()
{
}


//-----------------------------------------------------------------------------
// Purpose: initializes the dialog
// Output : returns true if successful
//-----------------------------------------------------------------------------
BOOL CSculptCarveOptions::OnInitDialog( )
{
	char	temp[ 1024 ];

	CDialog::OnInitDialog();

	m_OffsetModeControl.InsertString( -1, "Adaptive" );
	m_OffsetModeControl.InsertString( -1, "Absolute" );
	m_OffsetModeControl.SetCurSel( m_OffsetMode );

	m_OffsetDistanceControl.EnableWindow( ( m_OffsetMode == OFFSET_MODE_ABSOLUTE ) );
	m_OffsetAmountControl.EnableWindow( ( m_OffsetMode == OFFSET_MODE_ADAPTIVE ) ); 

	sprintf( temp, "%g", m_OffsetDistance );
	m_OffsetDistanceControl.SetWindowText( temp );

	sprintf( temp, "%g%%", m_OffsetAmount * 100.0f );
	m_OffsetAmountControl.SetWindowText( temp );

	sprintf( temp, "%g%%", m_SmoothAmount * 100.0f );
	m_SmoothAmountControl.SetWindowText( temp );

	m_NormalModeControl.InsertString( -1, "Brush Center" );
	m_NormalModeControl.InsertString( -1, "Screen" );
	m_NormalModeControl.InsertString( -1, "Screen XY" );
	m_NormalModeControl.InsertString( -1, "X" );
	m_NormalModeControl.InsertString( -1, "Y" );
	m_NormalModeControl.InsertString( -1, "Z" );
	m_NormalModeControl.InsertString( -1, "Selected" );
	m_NormalModeControl.SetCurSel( m_NormalMode );

	m_DensityModeControl.InsertString( -1, "Additive" );
	m_DensityModeControl.InsertString( -1, "Attenuated" );
	m_DensityModeControl.SetCurSel( m_DensityMode );

	return TRUE;
}


//-----------------------------------------------------------------------------
// Purpose: prevent the dialog from closing
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnOK( )
{
}


//-----------------------------------------------------------------------------
// Purpose: prevent the dialog from closing
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnCancel( )
{
}


//-----------------------------------------------------------------------------
// Purpose: set up the data exchange for the variables
// Input  : pDX - the data exchange object
//-----------------------------------------------------------------------------
void CSculptCarveOptions::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_OFFSET_MODE, m_OffsetModeControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_OFFSET_DISTANCE, m_OffsetDistanceControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_OFFSET_AMOUNT, m_OffsetAmountControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_SMOOTH_AMOUNT, m_SmoothAmountControl);
	DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_DENSITY_MODE, m_DensityModeControl);
	DDX_Control(pDX, IDC_IDC_SCULPT_PUSH_OPTION_NORMAL_MODE, m_NormalModeControl);
	DDX_Control(pDX, IDC_CARVE_BRUSH, m_CarveBrushControl);
}


BEGIN_MESSAGE_MAP(CSculptCarveOptions, CDialog)
	ON_CBN_SELCHANGE(IDC_IDC_SCULPT_PUSH_OPTION_NORMAL_MODE, &CSculptCarveOptions::OnCbnSelchangeIdcSculptPushOptionNormalMode)
	ON_CBN_SELCHANGE(IDC_SCULPT_PUSH_OPTION_OFFSET_MODE, &CSculptCarveOptions::OnCbnSelchangeSculptPushOptionOffsetMode)
	ON_EN_CHANGE(IDC_SCULPT_PUSH_OPTION_OFFSET_DISTANCE, &CSculptCarveOptions::OnEnChangeSculptPushOptionOffsetDistance)
	ON_CBN_SELCHANGE(IDC_SCULPT_PUSH_OPTION_DENSITY_MODE, &CSculptCarveOptions::OnCbnSelchangeSculptPushOptionDensityMode)
	ON_EN_KILLFOCUS(IDC_SCULPT_PUSH_OPTION_SMOOTH_AMOUNT, &CSculptCarveOptions::OnEnKillfocusSculptPushOptionSmoothAmount)
	ON_EN_KILLFOCUS(IDC_SCULPT_PUSH_OPTION_OFFSET_AMOUNT, &CSculptCarveOptions::OnEnKillfocusSculptPushOptionOffsetAmount)

	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_LBUTTONUP()
	ON_WM_MOUSEMOVE()
END_MESSAGE_MAP()


//-----------------------------------------------------------------------------
// Purpose: sets the normal mode
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnCbnSelchangeIdcSculptPushOptionNormalMode()
{
	m_NormalMode = ( NormalMode )m_NormalModeControl.GetCurSel();
}


//-----------------------------------------------------------------------------
// Purpose: sets the offset mode
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnCbnSelchangeSculptPushOptionOffsetMode()
{
	m_OffsetMode = ( OffsetMode )m_OffsetModeControl.GetCurSel();

	m_OffsetDistanceControl.EnableWindow( ( m_OffsetMode == OFFSET_MODE_ABSOLUTE ) );
	m_OffsetAmountControl.EnableWindow( ( m_OffsetMode == OFFSET_MODE_ADAPTIVE ) ); 
}


//-----------------------------------------------------------------------------
// Purpose: setup for starting to paint on the displacement
// Input  : pView - the 3d view
//			vPoint - the initial click point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptCarveOptions::BeginPaint( CMapView3D *pView, const Vector2D &vPoint )
{
	__super::BeginPaint( pView, vPoint );

	if ( m_bCtrlDown )
	{
		m_Direction = -1.0f;
	}
	else
	{
		m_Direction = 1.0f;
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: calculates the normal / direction of the drawing line
// Input  : nPointIndex - which point to factor from
// Output : returns true if we found a valid normal
//			vNormal - the normal we found
//-----------------------------------------------------------------------------
#if 0
bool CSculptCarveOptions::CalculatePointNormal( int nPointIndex, Vector2D &vNormal )
{
	float		count = 0.0;
	Vector2D	vAverage( 0.0f, 0.0f );
	const int	max_backsize = 3;

	// keep going back from the current point until you get a total distance
	for( int j = 0; j < max_backsize; j++ )
	{
		int index = ( nPointIndex - max_backsize + j );
		if ( index < 0 )
		{
			continue;
		}
		int index2 = nPointIndex;

		Vector2D	vDiff( m_DrawPoints[ index2 ].x - m_DrawPoints[ index ].x, m_DrawPoints[ index2 ].y - m_DrawPoints[ index ].y );
		float		Length = Vector2DNormalize( vDiff );

		if ( Length == 0.0f )
		{
			continue;
		}

		float factor = ( ( j + 1 ) * 100 ); // * Length; //  * 8 * Length;
		vAverage += ( vDiff * factor );
		count += factor;
	}

	if ( count > 0.0f )
	{
		vAverage /= count;
		Vector2DNormalize( vAverage );

		vNormal = vAverage;
		return true;
	}

	return false;
}
#endif


//-----------------------------------------------------------------------------
// Purpose: calculates the normal / direction of the drawing line
// Input  : nPointIndex - which point to factor from
// Output : returns true if we found a valid normal
//			vNormal - the normal we found
//-----------------------------------------------------------------------------
bool CSculptCarveOptions::CalculateQueuePoint( Vector2D &vPoint, Vector2D &vNormal )
{
	float		count = 0.0;
	Vector2D	vAverage( 0.0f, 0.0f );
	const float fMaxLength = 40.0f;
	float		fTotalLength = 0.0f;
	Vector2D	vInitialDir;
	bool		bInitialDirSet = false;

	int PointIndex = m_PointQueue.Count() - 1;
	if ( PointIndex <= 1 )
	{
		return false;
	}

	vPoint = m_PointQueue[ PointIndex ];

	// keep going back from the current point until you get a total distance
	for( int j = PointIndex - 1; j >= 0; j-- )
	{
		int index = j;
		int index2 = PointIndex;

		Vector2D	vDiff( m_PointQueue[ index2 ].x - m_PointQueue[ index ].x, m_PointQueue[ index2 ].y - m_PointQueue[ index ].y );
		float		Length = Vector2DNormalize( vDiff );

		if ( Length == 0.0f )
		{
			continue;
		}

		if ( bInitialDirSet == false )
		{
			vInitialDir = vDiff;
			bInitialDirSet = true;
		}

		if ( DotProduct2D( vInitialDir, vDiff ) <= 0.5f )
		{
			break;
		}

		fTotalLength += Length;

		float factor;

#if 0
		factor = 1.0f - ( fTotalLength / fMaxLength );
		if ( factor <= 0.0f )
		{
			factor = 0.01;
		}
		factor *= 20.0f;
#endif
		factor = Length;

		//= Length; // ( ( j + 1 ) * 100 ); // * Length; //  * 8 * Length;
		vAverage += ( vDiff * factor );
		count += factor;

		if ( fTotalLength >= fMaxLength )
		{
			break;
		}
	}

	if ( count > 0.0f )
	{
		vAverage /= count;
		Vector2DNormalize( vAverage );

		vNormal = vAverage;
		return true;
	}

	return false;
}


//-----------------------------------------------------------------------------
// Purpose: adds the point and normal to the queue
// Input  : vPoint - the point to be added
//			bDrawIt - if we should add this point to the draw / normal lists
//-----------------------------------------------------------------------------
void CSculptCarveOptions::AddQueuePoint( const Vector2D &vPoint, bool bDrawIt )
{
	m_PointQueue.AddToTail( vPoint );
	if ( m_PointQueue.Count() > MAX_QUEUE_SIZE )
	{
		m_PointQueue.Remove( 0 );
	}

	Vector2D	vNewPoint, vNewNormal;

	if ( bDrawIt && CalculateQueuePoint( vNewPoint, vNewNormal ) )
	{
		m_DrawPoints.AddToTail( vNewPoint );
		m_DrawNormal.AddToTail( vNewNormal );
	}
}


//-----------------------------------------------------------------------------
// Purpose: draws the tool in the 3d view
// Input  : pRender - the 3d renderer
//-----------------------------------------------------------------------------
void CSculptCarveOptions::RenderTool3D( CRender3D *pRender )
{
//	pRender->DrawText( "mouse", m_MousePoint.x, m_MousePoint.y, 0 );
//	Msg( "%g %g\n", m_MousePoint.x, m_MousePoint.y );

	pRender->PushRenderMode( RENDER_MODE_WIREFRAME );

	pRender->BeginClientSpace();

	Vector2D	vMousePoint, vMouseNormal;

	if ( CalculateQueuePoint( vMousePoint, vMouseNormal ) )
	{
		Vector2D	vRight( -vMouseNormal.y, vMouseNormal.x );

		pRender->SetDrawColor( 255, 255, 0 );
		pRender->DrawLine( Vector( vMousePoint.x, vMousePoint.y, 0.0f ), Vector( vMousePoint.x + vRight.x * m_BrushSize, vMousePoint.y + vRight.y * m_BrushSize, 0.0f ) );
		pRender->DrawLine( Vector( vMousePoint.x, vMousePoint.y, 0.0f ), Vector( vMousePoint.x - ( vRight.x * m_BrushSize ), vMousePoint.y - ( vRight.y * m_BrushSize ), 0.0f ) );
	}

#if 0
	for( int i = 2; i < m_DrawPoints.Count(); i++ )
	{
		Vector2D	vPoint = m_DrawPoints[ i ];
		Vector2D	vPreviousPoint = m_DrawPoints[ i - 1];
		Vector2D	vNormal = m_DrawNormal[ i ];
		Vector2D	vRight( -m_DrawNormal[ i ].y, m_DrawNormal[ i ].x );
		Vector2D	vDelta = vPoint - vPreviousPoint;
		float		Length = Vector2DLength( vDelta );

		pRender->SetDrawColor( 255, 255, 0 );
		pRender->DrawLine( Vector( vPreviousPoint.x, vPreviousPoint.y, 0.0f ), Vector( vPoint.x, vPoint.y, 0.0f ) );

		pRender->SetDrawColor( 255, 0, 0 );
		pRender->DrawLine( Vector( vPoint.x, vPoint.y, 0.0f ), Vector( vPoint.x + vRight.x * m_BrushSize, vPoint.y + vRight.y * m_BrushSize, 0.0f ) );
//		pRender->DrawLine( Vector( vPoint.x, vPoint.y, 0.0f ), Vector( vPoint.x - ( vRight.x * m_BrushSize ), vPoint.y - ( vRight.y * m_BrushSize ), 0.0f ) );

		vNormal *= Length;
		pRender->SetDrawColor( 0, 255, 0 );
		pRender->DrawLine( Vector( vPoint.x - vNormal.x, vPoint.y - vNormal.y, 0.0f ), Vector( vPoint.x, vPoint.y, 0.0f ) );
	}

	pRender->SetDrawColor( 255, 0, 255 );
	pRender->SetHandleStyle( 6, CRender::HANDLE_SQUARE );
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( pDispMgr )
	{
		int nDispCount = pDispMgr->SelectCount();
		for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
		{
			CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
			if ( pDisp )
			{
				int nVertCount = pDisp->GetSize();
				for ( int iVert = 0; iVert < nVertCount; iVert++ )
				{
					Vector		vVert;
					Vector2D	vViewVert;

					pDisp->GetVert( iVert, vVert );
					pRender->GetCamera()->WorldToView( vVert, vViewVert );

					for( int i = 2; i < m_DrawPoints.Count(); i++ )
					{
						float distance;
						
						float tolerance = DotProduct2D( m_DrawNormal[ i ], m_DrawNormal[ i - 1 ] );
						if ( tolerance <= 0.5f )
						{
							continue;
						}

						distance = DotProduct2D( m_DrawNormal[ i ], m_DrawPoints[ i ] );
						if ( DotProduct2D( m_DrawNormal[ i ], vViewVert ) > distance )
						{
							continue;
						}
						distance = DotProduct2D( m_DrawNormal[ i - 1 ], m_DrawPoints[ i - 1 ] );
						if ( DotProduct2D( m_DrawNormal[ i - 1 ], vViewVert ) < distance )
						{
							continue;
						}

						Vector2D	vRight( -m_DrawNormal[ i ].y, m_DrawNormal[ i ].x );
						Vector2D	vPoint;

						vPoint = m_DrawPoints[ i ] + ( vRight * m_BrushSize );
						distance = DotProduct2D( vRight, vPoint );
						if ( DotProduct2D( vRight, vViewVert ) > distance )
						{
							continue;
						}

						vPoint = m_DrawPoints[ i ] - ( vRight * m_BrushSize );
						distance = DotProduct2D( vRight, vPoint );
						if ( DotProduct2D( vRight, vViewVert ) < distance )
						{
							continue;
						}

//						pRender->DrawHandle( Vector( vViewVert.x, vViewVert.y, 0.0f ) );
						pRender->DrawHandle( vVert );
						break;
					}
				}
			}
		}
	}
#endif

	pRender->EndClientSpace();

#if 0
	if ( m_InSizingMode )
	{	// yellow for sizing mode
		pRender->SetDrawColor( 255, 255, 0 );
		pRender->BeginClientSpace();
		pRender->DrawCircle( Vector( m_StartSizingPoint.x, m_StartSizingPoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize, 32 );
		pRender->EndClientSpace();
	}
	else if ( m_bShiftDown )
	{	// purple for smoothing
		pRender->SetDrawColor( 255, 0, 255 );
		pRender->BeginClientSpace();
		pRender->DrawCircle( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize, 32 );
		pRender->EndClientSpace();
	}
	else if ( m_bCtrlDown )
	{	// red for negative sculpting
		pRender->SetDrawColor( 255, 0, 0 );
		pRender->BeginClientSpace();
		pRender->DrawCircle( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize, 32 );
		pRender->EndClientSpace();

		Vector	vPaintAxis;
		GetPaintAxis( pRender->GetCamera(), m_MousePoint, vPaintAxis );
		DrawDirection( pRender, -vPaintAxis, Color( 255, 255, 255 ), Color( 255, 128, 128 ) );
	}
	else
	{	// green for positive sculpting
		pRender->SetDrawColor( 0, 255, 0 );
		pRender->BeginClientSpace();
		pRender->DrawCircle( Vector( m_MousePoint.x, m_MousePoint.y, 0.0f ), Vector( 0.0f, 0.0f, 1.0f ), m_BrushSize, 32 );
		pRender->EndClientSpace();

		Vector	vPaintAxis;
		GetPaintAxis( pRender->GetCamera(), m_MousePoint, vPaintAxis );
		DrawDirection( pRender, vPaintAxis, Color( 255, 255, 255 ), Color( 255, 128, 128 ) );
	}
#endif

#if 0
	FindColissionIntercept( pRender->GetCamera(), m_MousePoint, true, m_CurrentCollisionPoint, m_CurrentCollisionNormal, m_CurrentCollisionIntercept );

	Vector2D	RadiusPoint = m_MousePoint;
	Vector		vecStart, vecEnd;

	RadiusPoint.x += m_BrushSize;
	pRender->GetCamera()->BuildRay( RadiusPoint, vecStart, vecEnd );

	m_CurrentProjectedRadius = CalcDistanceToLine( m_CurrentCollisionPoint, vecStart, vecEnd );

	pRender->RenderWireframeSphere( m_CurrentCollisionPoint, m_CurrentProjectedRadius, 12, 12, 0, 255, 255 );
#endif

#if 0

	// Get the displacement manager from the active map document.
	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();

	// For each displacement surface is the selection list attempt to paint on it.
	int nDispCount = pDispMgr->SelectCount();
	for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
	{
		CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
		if ( pDisp )
		{
			CMapDisp	*OrigDisp = NULL;
			int			index = m_OrigMapDisp.Find( pDisp->GetEditHandle() );

			if ( index != m_OrigMapDisp.InvalidIndex() )
			{
				OrigDisp = m_OrigMapDisp[ index ];
			}
			Vector	vPaintPos, vVert;

			int nVertCount = pDisp->GetSize();
			for ( int iVert = 0; iVert < nVertCount; iVert++ )
			{
				if ( IsPointInScreenCircle( pView, pDisp, pOrigDisp, iVert, false ) )
				{
					// Get the current vert.
					pDisp->GetVert( iVert, vVert );
				}
			}
		}
	}
#endif


	pRender->PopRenderMode();

#if 0
	if ( !FindColissionIntercept( pRender->GetCamera(), m_MousePoint, true, m_CurrentCollisionPoint, m_CurrentCollisionNormal, m_CurrentCollisionIntercept ) )
	{
		return;
	}

	Vector2D	RadiusPoint = m_MousePoint;
	Vector		vecStart, vecEnd;

	RadiusPoint.x += m_BrushSize;
	pRender->GetCamera()->BuildRay( RadiusPoint, vecStart, vecEnd );
	m_CurrentProjectedRadius = CalcDistanceToLine( m_CurrentCollisionPoint, vecStart, vecEnd );

	Msg( "Dist = %g at %g,%g,%g\n", m_CurrentProjectedRadius, m_CurrentCollisionPoint.x, m_CurrentCollisionPoint.y, m_CurrentCollisionPoint.z );
#endif
}


//-----------------------------------------------------------------------------
// Purpose: handles the left mouse button up in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptCarveOptions::OnLMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	__super::OnLMouseUp3D( pView, nFlags, vPoint );

	AddQueuePoint( vPoint, true );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the left mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptCarveOptions::OnLMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	__super::OnLMouseDown3D( pView, nFlags, vPoint );

	m_DrawPoints.Purge();
	m_DrawNormal.Purge();
	AddQueuePoint( vPoint, true );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the right mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptCarveOptions::OnRMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	__super::OnRMouseDown3D( pView, nFlags, vPoint );

	if ( m_bAltDown )
	{
		m_NormalMode = NORMAL_MODE_Z;
		m_NormalModeControl.SetCurSel( m_NormalMode );

#if 0

		//
		// check for closest solid object
		//
		ULONG		ulFace;
		CMapClass	*pObject;

		if( ( ( pObject = pView->NearestObjectAt( vPoint, ulFace ) ) != NULL ) )
		{
			if( pObject->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) )
			{
				// get the solid
				CMapSolid *pSolid = ( CMapSolid* )pObject;
				if( !pSolid )
				{
					return true;
				}

				// trace a line and get the normal -- will get a displacement normal
				// if one exists
				CMapFace *pFace = pSolid->GetFace( ulFace );
				if( !pFace )
				{
					return true;
				}

				Vector vRayStart, vRayEnd;
				pView->GetCamera()->BuildRay( vPoint, vRayStart, vRayEnd );

				Vector vHitPos, vHitNormal;
				if( pFace->TraceLine( vHitPos, vHitNormal, vRayStart, vRayEnd ) )
				{
					// set the paint direction
					m_SelectedNormal = vHitNormal;

					m_NormalMode = NORMAL_MODE_SELECTED;
					m_NormalModeControl.SetCurSel( m_NormalMode );
				}
			}
		}
#else
		Vector	CollisionPoint, CollisionNormal;
		float	CollisionIntercept;

		if ( FindCollisionIntercept( pView->GetCamera(), vPoint, false, CollisionPoint, CollisionNormal, CollisionIntercept ) )
		{
			// set the paint direction
			m_SelectedNormal = -CollisionNormal;

			m_NormalMode = NORMAL_MODE_SELECTED;
			m_NormalModeControl.SetCurSel( m_NormalMode );
		}
#endif
	}

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: handles the mouse move in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptCarveOptions::OnMouseMove3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	__super::OnMouseMove3D( pView, nFlags, vPoint );

	AddQueuePoint( vPoint, m_bLMBDown );

	return true;
}


//-----------------------------------------------------------------------------
// Purpose: returns the painting direction
// Input  : pCamera - the 3d camera
//			vPoint - the 2d mouse point
// Output : vPaintAxis - the direction the painting should go
//-----------------------------------------------------------------------------
void CSculptCarveOptions::GetPaintAxis( CCamera *pCamera, const Vector2D &vPoint, Vector &vPaintAxis )
{
	switch( m_NormalMode )
	{
		case NORMAL_MODE_SCREEN:
			pCamera->GetViewForward( vPaintAxis );
			vPaintAxis = -vPaintAxis;
			break;

		case NORMAL_MODE_SCREEN_XY:
			pCamera->GetViewForward( vPaintAxis );
			vPaintAxis = -vPaintAxis;
			vPaintAxis.z = 0.f;
			break;

		case NORMAL_MODE_BRUSH_CENTER:
			if ( !m_InPaintingMode )
			{
				Vector	CollisionPoint, CollisionNormal;
				float	CollisionIntercept;

				FindCollisionIntercept( pCamera, vPoint, false, CollisionPoint, CollisionNormal, CollisionIntercept );

				vPaintAxis = -CollisionNormal;
			}
			else
			{
				vPaintAxis = -m_StartingCollisionNormal;
			}
			break;

		case NORMAL_MODE_X:
			vPaintAxis.Init( 1.0f, 0.0f, 0.0f );
			break;

		case NORMAL_MODE_Y:
			vPaintAxis.Init( 0.0f, 1.0f, 0.0f );
			break;

		case NORMAL_MODE_Z:
			vPaintAxis.Init( 0.0f, 0.0f, 1.0f );
			break;

		case NORMAL_MODE_SELECTED:
			vPaintAxis = m_SelectedNormal;
			break;

		default:
			vPaintAxis.Init( 0.0f, 0.0f, 1.0f );
	}
}


//-----------------------------------------------------------------------------
// Purpose: determines if a displacement point is affected by the carve
// Input  : pView - the 3d view
//			pDisp - the displacement
//			pOrigDisp - the displacement prior to any updates
//			nVertIndex - the vertex to look at
//			nBrushPoint - which list point to check against
//			bUseOrigDisplacement - should we use the vert from the original displacement
//			bUseCurrentPosition - should we use the current collision test point
// Output :	returns true if the point is affected
//			vViewVert - the 2d view vert location
//-----------------------------------------------------------------------------
bool CSculptCarveOptions::IsPointAffected( CMapView3D *pView, CMapDisp *pDisp, CMapDisp *pOrigDisp, int vertIndex, int nBrushPoint, Vector2D &vViewVert, bool bUseOrigDisplacement, bool bUseCurrentPosition )
{
	Vector	vVert, vTestVert;

	pDisp->GetVert( vertIndex, vVert );

	if ( pOrigDisp && bUseOrigDisplacement )
	{
		pOrigDisp->GetVert( vertIndex, vTestVert );
	}
	else
	{
		vTestVert = vVert;
	}

	pView->GetCamera()->WorldToView( vTestVert, vViewVert );

	float distance;

	float tolerance = DotProduct2D( m_DrawNormal[ nBrushPoint ], m_DrawNormal[ nBrushPoint - 1 ] );
	if ( tolerance <= 0.5f )
	{
		return false;
	}

	distance = DotProduct2D( m_DrawNormal[ nBrushPoint ], m_DrawPoints[ nBrushPoint ] );
	if ( DotProduct2D( m_DrawNormal[ nBrushPoint ], vViewVert ) > distance )
	{
		return false;
	}
	distance = DotProduct2D( m_DrawNormal[ nBrushPoint - 1 ], m_DrawPoints[ nBrushPoint - 1 ] );
	if ( DotProduct2D( m_DrawNormal[ nBrushPoint - 1 ], vViewVert ) < distance )
	{
		return false;
	}

	Vector2D	vRight( -m_DrawNormal[ nBrushPoint ].y, m_DrawNormal[ nBrushPoint ].x );
	Vector2D	vPoint;

	vPoint = m_DrawPoints[ nBrushPoint ] + ( vRight * m_BrushSize );
	distance = DotProduct2D( vRight, vPoint );
	if ( DotProduct2D( vRight, vViewVert ) > distance )
	{
		return false;
	}

	vPoint = m_DrawPoints[ nBrushPoint ] - ( vRight * m_BrushSize );
	distance = DotProduct2D( vRight, vPoint );
	if ( DotProduct2D( vRight, vViewVert ) < distance )
	{
		return false;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: applies the specific push operation onto the displacement
// Input  : pView - the 3d view
//			vPoint - the mouse point
//			pDisp - the displacement to apply the push to
//			pOrigDisp - the original displacement prior to any adjustments
//-----------------------------------------------------------------------------
void CSculptCarveOptions::DoPaintOperation( CMapView3D *pView, const Vector2D &vPoint, CMapDisp *pDisp, CMapDisp *pOrigDisp )
{
	Vector		vPaintPos, vVert, vDirection;
	Vector2D	vViewVert;
	float		flDistance = 0.0f;
	Vector		vPaintAxis;

	int nTestPoint = m_DrawPoints.Count() - 1;
	if ( nTestPoint < 2 )
	{
		return;
	}

	if ( m_bShiftDown )
	{
//		DoSmoothOperation( pView, vPoint, pDisp, pOrigDisp );
//		m_SpatialData.m_flRadius = 256.0f;
//		m_SpatialData.m_flScalar = 5.0f / m_SmoothAmount;

//		m_SpatialData.m_flRadius = m_StartingProjectedRadius * 1.5f;
		m_SpatialData.m_flRadius = m_CurrentProjectedRadius * 2.0f;
		m_SpatialData.m_flRadius2 = ( m_SpatialData.m_flRadius * m_SpatialData.m_flRadius );
		m_SpatialData.m_flOORadius2 = 1.0f / m_SpatialData.m_flRadius2;
		m_SpatialData.m_flScalar = 10.0f / m_SmoothAmount;
		m_SpatialData.m_vCenter = m_CurrentCollisionPoint;

		DoPaintSmooth( pView, vPoint, pDisp, pOrigDisp );
		return;
	}

	GetPaintAxis( pView->GetCamera(), vPoint, vPaintAxis );

	vDirection = vPaintAxis * m_Direction;

	switch( m_OffsetMode )
	{
		case OFFSET_MODE_ADAPTIVE:
			flDistance = m_StartingProjectedRadius * m_OffsetAmount;
			break;
		case OFFSET_MODE_ABSOLUTE:
			flDistance = m_OffsetDistance;
			break;
	}

	int nVertCount = pDisp->GetSize();
	for ( int iVert = 0; iVert < nVertCount; iVert++ )
	{
		if ( IsPointAffected( pView, pDisp, pOrigDisp, iVert, nTestPoint, vViewVert ) )
		{
			pDisp->GetVert( iVert, vVert );

			Vector2D	vRight( -m_DrawNormal[ nTestPoint ].y, m_DrawNormal[ nTestPoint ].x );
			float		fLineDistance = DotProduct2D( vRight, m_DrawPoints[ nTestPoint ] ) - DotProduct2D( vRight, vViewVert );
			
			fLineDistance = ( fLineDistance + m_BrushSize ) / ( m_BrushSize * 2.0f );
			int index = ( int )( fLineDistance * MAX_SCULPT_SIZE );

			index = clamp( index, 0, MAX_SCULPT_SIZE - 1 );
			index = MAX_SCULPT_SIZE - index - 1;

			float		fScaledDistance = m_BrushPoints[ index ] * flDistance;

			if ( fScaledDistance == 0.0f )
			{
				continue;
			}

			switch( m_DensityMode )
			{
				case DENSITY_MODE_ADDITIVE:
					VectorScale( vDirection, fScaledDistance, vPaintPos );
					VectorAdd( vPaintPos, vVert, vPaintPos );
					break;

				case DENSITY_MODE_ATTENUATED:
					VectorScale( vDirection, fScaledDistance, vPaintPos );
					VectorAdd( vPaintPos, vVert, vPaintPos );

					if ( pOrigDisp )
					{
						Vector	vOrigVert, vDiff;
						float	Length;

						pOrigDisp->GetVert( iVert, vOrigVert );
						vDiff = ( vPaintPos - vOrigVert );
						Length = vDiff.Length() / flDistance;
						if ( Length > 1.0f )
						{
							Length = 1.0f;
						}

						vPaintPos = vOrigVert + ( Length * vDirection * flDistance );
					}
					break;
			}

			AddToUndo( &pDisp );
			pDisp->Paint_SetValue( iVert, vPaintPos );
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: sets the offset distance
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnEnChangeSculptPushOptionOffsetDistance()
{
	char	temp[ 1024 ];

	m_OffsetDistanceControl.GetWindowText( temp, sizeof( temp ) );
	m_OffsetDistance = atof( temp );
}


//-----------------------------------------------------------------------------
// Purpose: sets the density mode
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnCbnSelchangeSculptPushOptionDensityMode()
{
	m_DensityMode = ( DensityMode )m_DensityModeControl.GetCurSel();
}


//-----------------------------------------------------------------------------
// Purpose: sets the smooth amount
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnEnKillfocusSculptPushOptionSmoothAmount()
{
	char	temp[ 1024 ], t2[ 1024 ];

	m_SmoothAmountControl.GetWindowText( temp, sizeof( temp ) );
	sscanf( temp, "%f%%", &m_SmoothAmount );
	m_SmoothAmount /= 100.0f;

	if ( m_SmoothAmount <= 0.0f )
	{
		m_SmoothAmount = 0.2f;
	}

	sprintf( t2, "%g%%", m_SmoothAmount * 100.0f );

	if ( strcmpi( temp, t2 ) != 0 )
	{
		m_SmoothAmountControl.SetWindowText( t2 );
	}
}


//-----------------------------------------------------------------------------
// Purpose: sets the offset amount
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnEnKillfocusSculptPushOptionOffsetAmount()
{
	char	temp[ 1024 ], t2[ 1024 ];

	m_OffsetAmountControl.GetWindowText( temp, sizeof( temp ) );
	sscanf( temp, "%f%%", &m_OffsetAmount );
	m_OffsetAmount /= 100.0f;

	if ( m_OffsetAmount <= 0.0f )
	{
		m_OffsetAmount = 1.0f;
	}

	sprintf( t2, "%g%%", m_OffsetAmount * 100.0f );

	if ( strcmpi( temp, t2 ) != 0 )
	{
		m_OffsetAmountControl.SetWindowText( t2 );
	}
}


//-----------------------------------------------------------------------------
// Purpose: paints the carve brush
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnPaint()
{
	CPaintDC dc(this); // device context for painting

	CBrush black( RGB( 0, 0, 0 ) );
	CBrush red( RGB( 255, 0, 0 ) );
	CBrush green( RGB( 0, 255, 0 ) );
	CBrush blue_red( RGB( 64, 0, 128 ) );
	CBrush blue_green( RGB( 0, 64, 128 ) );
	CBrush blue( RGB( 0, 0, 255 ) );

	CRect WindowRect;
	m_CarveBrushControl.GetWindowRect( &WindowRect );
	ScreenToClient( &WindowRect );
	dc.FillRect( WindowRect, &black );

	float center = ( WindowRect.bottom + WindowRect.top ) / 2;
	float height = ( WindowRect.bottom - WindowRect.top ) - 1;

	if ( m_BrushLocation != -1 )
	{
		CRect	rect;

		rect.left = ( m_BrushLocation * 2 ) + WindowRect.left;
		rect.right = rect.left + 2;
		rect.bottom = WindowRect.bottom;
		rect.top = WindowRect.top;
		dc.FillRect( rect, &blue );
	}

	for( int i = 0; i < MAX_SCULPT_SIZE; i++ )
	{
		float	size = height / 2.0f * m_BrushPoints[ i ];
		CRect	rect;
		CBrush	*pBrush;

		rect.left = ( i * 2 ) + WindowRect.left;
		rect.right = rect.left + 2;
		rect.bottom = center - size;
		rect.top = center;

		if ( m_BrushPoints[ i ] >= 0.0f )
		{
			if ( m_BrushLocation == i )
			{
				pBrush = &blue_green;
			}
			else
			{
				pBrush = &green;
			}
		}
		else
		{
			if ( m_BrushLocation == i )
			{
				pBrush = &blue_red;
			}
			else
			{
				pBrush = &red;
			}
		}
		dc.FillRect( rect, pBrush );
	}
}


//-----------------------------------------------------------------------------
// Purpose: adjusts the carve brush
// Input  : x - location to set the height to
//			y - offset into the brush
//-----------------------------------------------------------------------------
void CSculptCarveOptions::AdjustBrush( int x, int y )
{
	CRect	WindowRect;
	CPoint	MousePoint( x, y );

	m_CarveBrushControl.GetWindowRect( &WindowRect );
	ClientToScreen( &MousePoint );

	if ( MousePoint.x >= WindowRect.left && MousePoint.x < WindowRect.right &&
		 MousePoint.y >= WindowRect.top && MousePoint.y < WindowRect.bottom )
	{
		int		pos = ( MousePoint.x - WindowRect.left ) / 2;
		float	center = ( WindowRect.bottom + WindowRect.top ) / 2;
		float	value = ( center - MousePoint.y ) / ( WindowRect.bottom - WindowRect.top ) * 2.0f;

		value = clamp( value, -1.0f, 1.0f );
		if ( pos >= 0 && pos < MAX_SCULPT_SIZE )
		{
			m_BrushPoints[ pos ] = value;
			Invalidate();
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: sets the brush cursor location
// Input  : x - x location of mouse
//			y - y location of mouse
//-----------------------------------------------------------------------------
void CSculptCarveOptions::AdjustBrushCursor( int x, int y )
{
	CRect	WindowRect;
	int		OldBrushLocation = m_BrushLocation;
	CPoint	MousePoint( x, y );

	m_CarveBrushControl.GetWindowRect( &WindowRect );
	ClientToScreen( &MousePoint );

	if ( MousePoint.x >= WindowRect.left && MousePoint.x < WindowRect.right &&
		 MousePoint.y >= WindowRect.top && MousePoint.y < WindowRect.bottom )
	{
		m_BrushLocation = ( MousePoint.x - WindowRect.left ) / 2;
	}
	else
	{
		m_BrushLocation = -1;
	}

	if ( OldBrushLocation != m_BrushLocation )
	{
		Invalidate();
	}
}


//-----------------------------------------------------------------------------
// Purpose: handles adjusting the brush
// Input  : nFlags - mouse buttons
//			point - mouse point
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnLButtonDown(UINT nFlags, CPoint point)
{
	AdjustBrush( point.x, point.y );
	AdjustBrushCursor( point.x, point.y );

	__super::OnLButtonDown(nFlags, point);
}


//-----------------------------------------------------------------------------
// Purpose: handles adjusting the brush
// Input  : nFlags - mouse buttons
//			point - mouse point
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnLButtonUp(UINT nFlags, CPoint point)
{
	AdjustBrush( point.x, point.y );
	AdjustBrushCursor( point.x, point.y );

	__super::OnLButtonUp(nFlags, point);
}


//-----------------------------------------------------------------------------
// Purpose: handles adjusting the brush
// Input  : nFlags - mouse buttons
//			point - mouse point
//-----------------------------------------------------------------------------
void CSculptCarveOptions::OnMouseMove(UINT nFlags, CPoint point)
{
	if ( nFlags & MK_LBUTTON )
	{
		AdjustBrush( point.x, point.y );
	}
	AdjustBrushCursor( point.x, point.y );

	__super::OnMouseMove(nFlags, point);
}


//-----------------------------------------------------------------------------
// Purpose: we want to handle the messages for mouse events
//-----------------------------------------------------------------------------
BOOL CSculptCarveOptions::PreTranslateMessage( MSG* pMsg )
{
	if ( pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_MOUSEMOVE )
	{
		return FALSE;
	}

	return __super::PreTranslateMessage( pMsg );
}






#if 0

class CSculptRegenerator : public ITextureRegenerator
{
public:
	CSculptRegenerator( unsigned char *ImageData, int Width, int Height, enum ImageFormat Format ) :
	  m_ImageData( ImageData ), 
		  m_Width( Width ), 
		  m_Height( Height ),
		  m_Format( Format )
	  {
	  }

	  virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pSubRect )
	  {
		  for (int iFrame = 0; iFrame < pVTFTexture->FrameCount(); ++iFrame )
		  {
			  for (int iFace = 0; iFace < pVTFTexture->FaceCount(); ++iFace )
			  {
				  int nWidth = pVTFTexture->Width();
				  int nHeight = pVTFTexture->Height();
				  int nDepth = pVTFTexture->Depth();
				  for (int z = 0; z < nDepth; ++z)
				  {
					  // Fill mip 0 with a checkerboard
					  CPixelWriter pixelWriter;
					  pixelWriter.SetPixelMemory( pVTFTexture->Format(), pVTFTexture->ImageData( iFrame, iFace, 0, 0, 0, z ), pVTFTexture->RowSizeInBytes( 0 ) );

					  switch( m_Format )
					  {
					  case IMAGE_FORMAT_BGR888:
						  {
							  unsigned char *data = m_ImageData;

							  for (int y = 0; y < nHeight; ++y)
							  {
								  pixelWriter.Seek( 0, y );
								  for (int x = 0; x < nWidth; ++x)
								  {
									  pixelWriter.WritePixel( *( data + 2 ), *( data + 1 ), *( data ), 255 );
									  data += 3;
								  }
							  }
						  }
						  break;
					  }
				  }
			  }
		  }
	  }

	  virtual void Release()
	  {
		  delete this;
	  }

private:
	unsigned char		*m_ImageData;
	int					m_Width;
	int					m_Height;
	enum ImageFormat	m_Format;
};




// CSculptProjectOptions dialog

IMPLEMENT_DYNAMIC(CSculptProjectOptions, CDialog)

CSculptProjectOptions::CSculptProjectOptions(CWnd* pParent /*=NULL*/) : 
	CDialog(CSculptProjectOptions::IDD, pParent),
	CSculptTool()
{
	m_FileDialog = new CFileDialog(TRUE, NULL, NULL, OFN_LONGNAMES | OFN_NOCHANGEDIR | OFN_FILEMUSTEXIST, "Image Files (*.tga)|*.tga||");
	m_FileDialog->m_ofn.lpstrInitialDir = "";

	m_ImagePixels = NULL;
	m_pTexture = NULL;
	m_pMaterial = NULL;

	m_ProjectX = 100;
	m_ProjectY = 100;
	m_ProjectWidth = 100;
	m_ProjectHeight = 100;
	m_TileWidth = m_TileHeight = 1.0;
	m_OriginalTileWidth = m_TileWidth;
	m_OriginalTileHeight = m_TileHeight;

	m_ProjectLocation.Init( 100.0f, 100.0f, 0.0f );
	m_OriginalProjectLocation = m_ProjectLocation;
	m_ProjectSize.Init( 100.0f, 100.0f, 0.0f );
	m_OriginalProjectSize = m_ProjectSize;

	m_ToolMode = PROJECT_MODE_NONE;
}

CSculptProjectOptions::~CSculptProjectOptions()
{
	delete m_FileDialog;

	if ( m_ImagePixels )
	{
		delete [] m_ImagePixels;
	}

	if ( m_pTexture )
	{
		m_pTexture->DecrementReferenceCount();
		m_pTexture = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: set up the data exchange for the variables
// Input  : pDX - the data exchange object
//-----------------------------------------------------------------------------
void CSculptProjectOptions::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_PROJECT_SIZE, m_ProjectSizeControl);
	DDX_Control(pDX, IDC_PROJECT_SIZE_NUM, m_ProjectSizeNumControl);
}


BEGIN_MESSAGE_MAP(CSculptProjectOptions, CDialog)
	ON_BN_CLICKED(IDC_LOAD_IMAGE, &CSculptProjectOptions::OnBnClickedLoadImage)
	ON_NOTIFY(NM_CUSTOMDRAW, IDC_PROJECT_SIZE, &CSculptProjectOptions::OnNMCustomdrawProjectSize)
END_MESSAGE_MAP()

bool CSculptProjectOptions::Paint( CMapView3D *pView, const Vector2D &vPoint, SpatialPaintData_t &spatialData )
{
	CSculptTool::Paint( pView, vPoint, spatialData );

	switch( m_ToolMode )
	{
		case PROJECT_MODE_SIZE:
			DoSizing( vPoint );
			break;

		case PROJECT_MODE_POSITION:
			DoPosition( vPoint );
			break;

		case PROJECT_MODE_TILE:
			DoTiling( vPoint );
			break;
	}

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: draws the tool in the 3d view
// Input  : pRender - the 3d renderer
//-----------------------------------------------------------------------------
void CSculptProjectOptions::RenderTool3D(CRender3D *pRender)
{
	if ( !m_pMaterial )
	{
		return;
	}

	pRender->PushRenderMode( RENDER_MODE_TEXTURED );
	bool bPopMode = pRender->BeginClientSpace();

	CMatRenderContextPtr pRenderContext( MaterialSystemInterface() );
	pRender->BindMaterial( m_pMaterial );
	IMesh* pMesh = pRenderContext->GetDynamicMesh();

	CMeshBuilder meshBuilder;
	meshBuilder.Begin( pMesh, MATERIAL_QUADS, 4 );

	meshBuilder.Position3f( m_ProjectLocation.x, m_ProjectLocation.y, m_ProjectLocation.z );
	meshBuilder.TexCoord2f( 0, 0.0f, 0.0f );
	meshBuilder.Color4ub( 255, 255, 255, 128 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( m_ProjectLocation.x + m_ProjectSize.x, m_ProjectLocation.y, m_ProjectLocation.z );
	meshBuilder.TexCoord2f( 0, m_TileWidth, 0.0f );
	meshBuilder.Color4ub( 255, 255, 255, 128 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( m_ProjectLocation.x + m_ProjectSize.x, m_ProjectLocation.y + m_ProjectSize.y, m_ProjectLocation.z );
	meshBuilder.TexCoord2f( 0, m_TileWidth, m_TileHeight );
	meshBuilder.Color4ub( 255, 255, 255, 128 );
	meshBuilder.AdvanceVertex();

	meshBuilder.Position3f( m_ProjectLocation.x, m_ProjectLocation.y + m_ProjectSize.y, m_ProjectLocation.z );
	meshBuilder.TexCoord2f( 0, 0.0f, m_TileHeight );
	meshBuilder.Color4ub( 255, 255, 255, 128 );
	meshBuilder.AdvanceVertex();

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

	if ( bPopMode )
	{
		pRender->EndClientSpace();
	}
	pRender->PopRenderMode();
}

//-----------------------------------------------------------------------------
// Purpose: handles the left mouse button up in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptProjectOptions::OnLMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnLMouseUp3D( pView, nFlags, vPoint );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: handles the left mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptProjectOptions::OnLMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnLMouseDown3D( pView, nFlags, vPoint );

	IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
	if( pDispMgr )
	{
		pDispMgr->PreUndo( "Displacement Modifier" );
	}

	PrepareDispForPainting();

	// Handle painting.
	if ( !DoPaint( pView, vPoint ) )
	{
		return false;
	}

	// Finish painting.
	if ( !PostPaint( m_PaintOwner->GetAutoSew() ) )
	{
		return false;
	}

	if( pDispMgr )
	{
		pDispMgr->PostUndo();
	}

	return true;
}

bool CSculptProjectOptions::OnRMouseUp3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnRMouseUp3D( pView, nFlags, vPoint );

	m_ToolMode = PROJECT_MODE_NONE;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: handles the right mouse button down in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptProjectOptions::OnRMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnRMouseDown3D( pView, nFlags, vPoint );

	m_OriginalProjectSize = m_ProjectSize;
	m_OriginalProjectLocation = m_ProjectLocation;
	m_StartSizingPoint = vPoint;

	if ( m_bCtrlDown )
	{
		m_ToolMode = PROJECT_MODE_SIZE;
	}
	else if ( m_bShiftDown )
	{
		m_ToolMode = PROJECT_MODE_TILE;
	}
	else
	{
		m_ToolMode = PROJECT_MODE_POSITION;
	}

	m_StartSizingPoint = vPoint;

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: handles the mouse move in the 3d view
// Input  : pView - the 3d view
//			nFlags - the button flags
//			vPoint - the mouse point
// Output : returns true if successful
//-----------------------------------------------------------------------------
bool CSculptProjectOptions::OnMouseMove3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint )
{
	CSculptTool::OnMouseMove3D( pView, nFlags, vPoint );

	return true;
}

//-----------------------------------------------------------------------------
// Purpose: applies the specific push operation onto the displacement
// Input  : pView - the 3d view
//			vPoint - the mouse point
//			pDisp - the displacement to apply the push to
//			pOrigDisp - the original displacement prior to any adjustments
//-----------------------------------------------------------------------------
void CSculptProjectOptions::DoPaintOperation( CMapView3D *pView, const Vector2D &vPoint, CMapDisp *pDisp, CMapDisp *pOrigDisp )
{
	Vector	vPaintPos, vVert;
	Vector	vPaintAxis;

	pView->GetCamera()->GetViewForward( vPaintAxis );
	vPaintAxis = -vPaintAxis;

	vPaintAxis *= ( m_ProjectSizeControl.GetPos() * 16.0f );

	int nVertCount = pDisp->GetSize();
	for ( int iVert = 0; iVert < nVertCount; iVert++ )
	{
		Vector2D	ViewVert;
		Vector		vTestVert;

		pDisp->GetVert( iVert, vTestVert );
		pView->GetCamera()->WorldToView( vTestVert, ViewVert );

		if ( ViewVert.x >= m_ProjectLocation.x &&
			 ViewVert.y >= m_ProjectLocation.y &&
			 ViewVert.x <= m_ProjectLocation.x + m_ProjectSize.x &&
			 ViewVert.y <= m_ProjectLocation.y + m_ProjectSize.y )
		{
			pDisp->GetVert( iVert, vVert );

			float sCoord = ( ViewVert.x - m_ProjectLocation.x ) / m_ProjectSize.x;
			float tCoord = ( ViewVert.y - m_ProjectLocation.y ) / m_ProjectSize.y;
			
			sCoord *= m_TileWidth;
			tCoord *= m_TileHeight;

			sCoord -= ( int )sCoord;
			tCoord -= ( int )tCoord;

			int x = ( sCoord * m_Width );
			int y = ( tCoord * m_Height );

			unsigned char *pos = &m_ImagePixels[ ( y * m_Width * 3 ) + ( x * 3 ) ];
			float gray = ( 0.3f * pos[ 2 ] ) + ( 0.59f * pos[ 1 ] ) + ( 0.11f * pos[ 0 ] );
			gray /= 255.0f;

			vPaintPos = vVert + ( vPaintAxis * gray );

			AddToUndo( &pDisp );
			pDisp->Paint_SetValue( iVert, vPaintPos );
		}
	}
}

void CSculptProjectOptions::OnBnClickedLoadImage()
{
	if ( m_FileDialog->DoModal() == IDCANCEL )
	{
		return;
	}

	ReadImage( m_FileDialog->GetPathName() );
}

bool CSculptProjectOptions::ReadImage( CString &FileName )
{
	enum ImageFormat	imageFormat;
	float				sourceGamma;
	CUtlBuffer			buf;

	if ( !g_pFullFileSystem->ReadFile( FileName, NULL, buf ) )
	{
		return false;
	}

	if ( !TGALoader::GetInfo( buf, &m_Width, &m_Height, &imageFormat, &sourceGamma ) )
	{
		return false;
	}

	if ( m_ImagePixels )
	{
		delete [] m_ImagePixels;
	}

	int memRequired = ImageLoader::GetMemRequired( m_Width, m_Height, 1, imageFormat, false );
	m_ImagePixels = new unsigned char[ memRequired ];

	buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 );
	TGALoader::Load( m_ImagePixels, buf, m_Width, m_Height, imageFormat, sourceGamma, false );

	m_pTexture = dynamic_cast< ITextureInternal * >( g_pMaterialSystem->CreateProceduralTexture( "SculptProject", TEXTURE_GROUP_OTHER, m_Width, m_Height, imageFormat, 
		TEXTUREFLAGS_NOMIP | TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_PROCEDURAL ) );

	ITextureRegenerator *pRegen = new CSculptRegenerator( m_ImagePixels, m_Width, m_Height, imageFormat );
	m_pTexture->SetTextureRegenerator( pRegen );
	m_pTexture->Download();

	m_pMaterial = MaterialSystemInterface()->FindMaterial( "editor/sculpt", TEXTURE_GROUP_OTHER );

	return true;
}

bool CSculptProjectOptions::DoSizing( const Vector2D &vPoint )
{
	m_ProjectSize.x = m_OriginalProjectSize.x + ( vPoint.x - m_StartSizingPoint.x );
	if ( m_ProjectSize.x < 1.0f )
	{
		m_ProjectSize.x = 1.0f;
	}
	m_ProjectSize.y = m_OriginalProjectSize.y + ( vPoint.y - m_StartSizingPoint.y );
	if ( m_ProjectSize.y < 1.0f )
	{
		m_ProjectSize.y = 1.0f;
	}

	return true;
}

bool CSculptProjectOptions::DoPosition( const Vector2D &vPoint )
{
	m_ProjectLocation.x = m_OriginalProjectLocation.x + ( vPoint.x - m_StartSizingPoint.x );
	m_ProjectLocation.y = m_OriginalProjectLocation.y + ( vPoint.y - m_StartSizingPoint.y );

	return true;
}

bool CSculptProjectOptions::DoTiling( const Vector2D &vPoint )
{
	m_TileWidth += ( vPoint.x - m_StartSizingPoint.x ) / m_ProjectSize.x;
	m_TileHeight += ( vPoint.y - m_StartSizingPoint.y ) / m_ProjectSize.y;

	m_StartSizingPoint = vPoint;

	return true;
}

void CSculptProjectOptions::OnNMCustomdrawProjectSize(NMHDR *pNMHDR, LRESULT *pResult)
{
//	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);

	char	temp[ 128 ];
	sprintf( temp, "%d", m_ProjectSizeControl.GetPos() * 16 );

	m_ProjectSizeNumControl.SetWindowText( temp );

	*pResult = 0;
}

//-----------------------------------------------------------------------------
// Purpose: initializes the dialog
// Output : returns true if successful
//-----------------------------------------------------------------------------
BOOL CSculptProjectOptions::OnInitDialog()
{
	__super::OnInitDialog();

	m_ProjectSizeControl.SetRange( 1, 32 );
	m_ProjectSizeControl.SetTicFreq( 1 );
	m_ProjectSizeControl.SetPageSize( 4 );
	m_ProjectSizeControl.SetLineSize( 4 );

	return TRUE;  
}

#endif

// current mouse position updates location of rectangle
// then rmb = size
// +control = st adjust


#include <tier0/memdbgoff.h>