diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/sculptoptions.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'hammer/sculptoptions.cpp')
| -rw-r--r-- | hammer/sculptoptions.cpp | 3933 |
1 files changed, 3933 insertions, 0 deletions
diff --git a/hammer/sculptoptions.cpp b/hammer/sculptoptions.cpp new file mode 100644 index 0000000..6aa0a72 --- /dev/null +++ b/hammer/sculptoptions.cpp @@ -0,0 +1,3933 @@ +//========= 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> |