// SculptOptions.cpp : implementation file // #include #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" #include "tablet.h" #include "vstdlib/random.h" // memdbgon must be the last include file in a .cpp file!!! #include extern CToolDisplace* GetDisplacementTool(); extern void FaceListSewEdges( void ); CUtlMap 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, int *pnCollideDisplacement, int *pnCollideTri ) { 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 ); if ( pnCollideDisplacement != NULL ) { *pnCollideDisplacement = iDisp; } if ( pnCollideTri != NULL ) { *pnCollideTri = iTri; } } } } 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, "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_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 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_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, "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_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 flScaledDistance = m_BrushPoints[ index ] * flDistance; if ( flScaledDistance == 0.0f ) { continue; } switch( m_DensityMode ) { case DENSITY_MODE_ADDITIVE: VectorScale( vDirection, flScaledDistance, vPaintPos ); VectorAdd( vPaintPos, vVert, vPaintPos ); break; case DENSITY_MODE_ATTENUATED: VectorScale( vDirection, flScaledDistance, 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_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(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 CTextureButton::CTextureButton( ) : CButton() { m_pTexure = NULL; m_bSelected = false; } void CTextureButton::SetTexture( IEditorTexture *pTexture ) { m_pTexure = pTexture; } void CTextureButton::SetSelected( bool bSelected ) { m_bSelected = bSelected; Invalidate(); } BOOL CTextureButton::PreCreateWindow(CREATESTRUCT& cs) { cs.style |= BS_OWNERDRAW; return __super::PreCreateWindow( cs ); } void CTextureButton::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ) { #if 0 UINT uStyle = DFCS_BUTTONPUSH; // This code only works with buttons. ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON); // If drawing selected, add the pushed style to DrawFrameControl. if (lpDrawItemStruct->itemState & ODS_SELECTED) uStyle |= DFCS_PUSHED; // Draw the button frame. ::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, uStyle); // Get the button's text. CString strText; GetWindowText(strText); // Draw the button text using the text color red. COLORREF crOldColor = ::SetTextColor(lpDrawItemStruct->hDC, RGB(255,0,0)); ::DrawText(lpDrawItemStruct->hDC, strText, strText.GetLength(), &lpDrawItemStruct->rcItem, DT_SINGLELINE|DT_VCENTER|DT_CENTER); ::SetTextColor(lpDrawItemStruct->hDC, crOldColor); #endif UINT uStyle = DFCS_BUTTONPUSH; COLORREF dwForeColor = GetSysColor( COLOR_BTNTEXT ); // If drawing selected, add the pushed style to DrawFrameControl. if (lpDrawItemStruct->itemState & ODS_SELECTED) { dwForeColor = GetSysColor( COLOR_BTNTEXT ); uStyle |= DFCS_PUSHED; } if ( m_bSelected == true ) { dwForeColor = RGB( 200, 0, 0 ); } ::DrawFrameControl( lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, uStyle ); CDC dc; dc.Attach( lpDrawItemStruct->hDC ); dc.SaveDC(); RECT& r = lpDrawItemStruct->rcItem; int iFontHeight = dc.GetTextExtent( "J", 1 ).cy; dc.SetROP2( R2_COPYPEN ); CPalette *pOldPalette = NULL; if (m_pTexure != NULL) { m_pTexure->Load(); pOldPalette = dc.SelectPalette( m_pTexure->HasPalette() ? m_pTexure->GetPalette() : g_pGameConfig->Palette, FALSE ); dc.RealizePalette(); } if ( m_pTexure != NULL ) { char szName[ MAX_PATH ]; int iLen = m_pTexure->GetShortName( szName ); // crop to just the name without path const char *pszTextureName = V_UnqualifiedFileName( szName ); iLen = strlen( pszTextureName ); DrawTexData_t DrawTexData; DrawTexData.nFlags = 0; int nWidth = m_pTexure->GetPreviewImageWidth(); int nHeight = m_pTexure->GetPreviewImageHeight(); CRect r2(r); r2.InflateRect( -4, -4 ); if ( m_pTexure->IsLoaded() && nWidth > 0 && nHeight > 0 ) { // draw graphic int nDrawWidth = 64; int nDrawHeight = nDrawWidth * nHeight / nWidth; if ( nDrawHeight > r2.bottom - r2.top ) { nDrawHeight = r2.bottom - r2.top; nDrawWidth = nDrawHeight * nWidth / nHeight; } r2.right = r2.left + nDrawWidth; r2.bottom = r2.top + nDrawHeight; m_pTexure->Draw( &dc, r2, 0, 0, DrawTexData ); } else { int nDrawSize = r2.bottom - r2.top; r2.right = r2.left + nDrawSize; r2.bottom = r2.top + nDrawSize; } // draw name dc.SetTextColor( dwForeColor ); dc.SetBkMode( TRANSPARENT ); dc.TextOut( r2.right + 4, r2.top + 4, pszTextureName, iLen ); // draw size sprintf( szName, "%dx%d", m_pTexure->GetWidth(), m_pTexure->GetHeight() ); dc.TextOut( r2.right + 4, r2.top + 4 + iFontHeight, szName, strlen( szName ) ); } if (pOldPalette) { dc.SelectPalette( pOldPalette, FALSE ); } dc.RestoreDC( -1 ); dc.Detach(); } BEGIN_MESSAGE_MAP(CColorButton, CButton) END_MESSAGE_MAP() CColorButton::CColorButton( ) : CButton() { m_flRed = m_flGreen = m_flBlue = 1.0f; } void CColorButton::SetColor( float flRed, float flGreen, float flBlue ) { m_flRed = flRed; m_flGreen = flGreen; m_flBlue = flBlue; Invalidate(); } BOOL CColorButton::PreCreateWindow(CREATESTRUCT& cs) { cs.style |= BS_OWNERDRAW; return __super::PreCreateWindow( cs ); } void CColorButton::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct ) { UINT uStyle = DFCS_BUTTONPUSH; // This code only works with buttons. ASSERT(lpDrawItemStruct->CtlType == ODT_BUTTON); // If drawing selected, add the pushed style to DrawFrameControl. if (lpDrawItemStruct->itemState & ODS_SELECTED) uStyle |= DFCS_PUSHED; CDC dc; dc.Attach( lpDrawItemStruct->hDC ); dc.SaveDC(); COLORREF dwBackColor = RGB( m_flRed * 255, m_flGreen * 255, m_flBlue * 255 ); // Draw the button frame. ::DrawFrameControl(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem, DFC_BUTTON, uStyle); // draw background CBrush brush; CRect r2( lpDrawItemStruct->rcItem ); brush.CreateSolidBrush( dwBackColor) ; r2.InflateRect( -4, -4 ); dc.FillRect( &r2, &brush ); dc.RestoreDC( -1 ); dc.Detach(); } // CSculptBlendOptions dialog IMPLEMENT_DYNAMIC(CSculptBlendOptions, CDialog) //----------------------------------------------------------------------------- // Purpose: constructor //----------------------------------------------------------------------------- CSculptBlendOptions::CSculptBlendOptions(CWnd* pParent /*=NULL*/) : CDialog(CSculptBlendOptions::IDD, pParent), CSculptPainter() { m_flFalloffSpot = 0.5f; m_flFalloffEndingValue = 0.0f; m_Direction = 1.0f; m_nSelectedTexture = 0; for( int i = 0; i < MAX_MULTIBLEND_CHANNELS; i++ ) { m_ColorMode[ i ] = COLOR_MODE_SINGLE; m_vStartDrawColor[ i ].Init( 1.0f, 1.0f, 1.0f ); m_vEndDrawColor[ i ].Init( 1.0f, 1.0f, 1.0f ); } m_nDefaultFalloffPosition = 50; m_nDefaultFalloffFinal = 0; m_nDefaultBlendAmount = 50; m_nDefaultColorBlendAmount = 0; m_nDefaultAlphaBlendAmount = 0; m_b4WayBlendMode = false; } //----------------------------------------------------------------------------- // Purpose: destructor //----------------------------------------------------------------------------- CSculptBlendOptions::~CSculptBlendOptions() { } //----------------------------------------------------------------------------- // Purpose: initializes the dialog // Output : returns true if successful //----------------------------------------------------------------------------- BOOL CSculptBlendOptions::OnInitDialog( void ) { CDialog::OnInitDialog(); m_FalloffPositionControl.SetRange( 0, 100 ); m_FalloffPositionControl.SetTicFreq( 10 ); m_FalloffPositionControl.SetPos( m_nDefaultFalloffPosition ); m_FalloffFinalControl.SetRange( 0, 100 ); m_FalloffFinalControl.SetTicFreq( 10 ); m_FalloffFinalControl.SetPos( m_nDefaultFalloffFinal ); m_BlendAmountControl.SetRange( 0, 100 ); m_BlendAmountControl.SetTicFreq( 10 ); m_BlendAmountControl.SetPos( m_nDefaultBlendAmount ); m_ColorBlendAmountControl.SetRange( 0, 100 ); m_ColorBlendAmountControl.SetTicFreq( 10 ); m_ColorBlendAmountControl.SetPos( m_nDefaultColorBlendAmount ); m_AlphaBlendAmountControl.SetRange( 0, 100 ); m_AlphaBlendAmountControl.SetTicFreq( 10 ); m_AlphaBlendAmountControl.SetPos( m_nDefaultAlphaBlendAmount ); m_BlendColorOperationControl.InsertString( -1, "Single"); m_BlendColorOperationControl.InsertString( -1, "Blend"); m_BlendColorOperationControl.InsertString( -1, "Or"); return TRUE; } void CSculptBlendOptions::SetColorMode( ColorMode NewMode, bool bSetDialog ) { m_ColorMode[ m_nSelectedTexture ] = NewMode; switch( m_ColorMode[ m_nSelectedTexture ] ) { case COLOR_MODE_SINGLE: m_ColorEndControl.ShowWindow( SW_HIDE ); break; case COLOR_MODE_RANGE: m_ColorEndControl.ShowWindow( SW_SHOW ); break; case COLOR_MODE_OR: m_ColorEndControl.ShowWindow( SW_SHOW ); break; } if ( bSetDialog == true ) { m_BlendColorOperationControl.SetCurSel( m_ColorMode[ m_nSelectedTexture ] ); } } void CSculptBlendOptions::SelectTexture( int nTexture ) { m_nSelectedTexture = nTexture; m_ColorStartControl.SetColor( m_vStartDrawColor[ m_nSelectedTexture ].x, m_vStartDrawColor[ m_nSelectedTexture ].y, m_vStartDrawColor[ m_nSelectedTexture ].z ); m_ColorEndControl.SetColor( m_vEndDrawColor[ m_nSelectedTexture ].x, m_vEndDrawColor[ m_nSelectedTexture ].y, m_vEndDrawColor[ m_nSelectedTexture ].z ); SetColorMode( m_ColorMode[ m_nSelectedTexture ], true ); for( int i = 0; i < MAX_MULTIBLEND_CHANNELS; i++ ) { m_ColorMaskControl[ i ].SetCheck( ( i == m_nSelectedTexture ? BST_CHECKED : BST_UNCHECKED ) ); } } //----------------------------------------------------------------------------- // Purpose: prevent the dialog from closing //----------------------------------------------------------------------------- void CSculptBlendOptions::OnOK() { } //----------------------------------------------------------------------------- // Purpose: prevent the dialog from closing //----------------------------------------------------------------------------- void CSculptBlendOptions::OnCancel() { } //----------------------------------------------------------------------------- // Purpose: set up the data exchange for the variables // Input : pDX - the data exchange object //----------------------------------------------------------------------------- void CSculptBlendOptions::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_BLEND_AMOUNT, m_BlendAmountControl); DDX_Control(pDX, IDC_BLEND_AMOUNT_TEXT, m_BlendAmountTextControl); DDX_Control(pDX, IDC_TEXTURE_BUTTON1, m_TextureControl[ 0 ]); DDX_Control(pDX, IDC_TEXTURE_BUTTON2, m_TextureControl[ 1 ]); DDX_Control(pDX, IDC_TEXTURE_BUTTON3, m_TextureControl[ 2 ]); DDX_Control(pDX, IDC_TEXTURE_BUTTON4, m_TextureControl[ 3 ]); DDX_Control(pDX, IDC_TEXTURE1_MASK, m_TextureMaskControl[ 0 ]); DDX_Control(pDX, IDC_TEXTURE2_MASK, m_TextureMaskControl[ 1 ]); DDX_Control(pDX, IDC_TEXTURE3_MASK, m_TextureMaskControl[ 2 ]); DDX_Control(pDX, IDC_TEXTURE4_MASK, m_TextureMaskControl[ 3 ]); DDX_Control(pDX, IDC_COLOR1_MASK, m_ColorMaskControl[ 0 ]); DDX_Control(pDX, IDC_COLOR2_MASK, m_ColorMaskControl[ 1 ]); DDX_Control(pDX, IDC_COLOR3_MASK, m_ColorMaskControl[ 2 ]); DDX_Control(pDX, IDC_COLOR4_MASK, m_ColorMaskControl[ 3 ]); DDX_Control(pDX, IDC_COLOR_BLEND_AMOUNT, m_ColorBlendAmountControl); DDX_Control(pDX, IDC_COLOR_BLEND_AMOUNT_TEXT, m_ColorBlendAmountTextControl); DDX_Control(pDX, IDC_SET_COLOR, m_ColorStartControl); DDX_Control(pDX, IDC_SET_COLOR2, m_ColorEndControl); DDX_Control(pDX, IDC_BLEND_COLOR_OPERATION, m_BlendColorOperationControl); DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_FALLOFF_POSITION, m_FalloffPositionControl); DDX_Control(pDX, IDC_SCULPT_PUSH_OPTION_FALLOFF_FINAL, m_FalloffFinalControl); DDX_Control(pDX, IDC_ALPHA_BLEND_AMOUNT, m_AlphaBlendAmountControl); DDX_Control(pDX, IDC_ALPHA_BLEND_AMOUNT_TEXT, m_AlphaBlendAmountTextControl); } BEGIN_MESSAGE_MAP(CSculptBlendOptions, CDialog) ON_NOTIFY(NM_CUSTOMDRAW, IDC_BLEND_AMOUNT, &CSculptBlendOptions::OnNMCustomdrawBlendAmount) ON_WM_SHOWWINDOW() ON_BN_CLICKED(IDC_TEXTURE_BUTTON1, &CSculptBlendOptions::OnBnClickedTextureButton1) ON_BN_CLICKED(IDC_TEXTURE_BUTTON2, &CSculptBlendOptions::OnBnClickedTextureButton2) ON_BN_CLICKED(IDC_TEXTURE_BUTTON3, &CSculptBlendOptions::OnBnClickedTextureButton3) ON_BN_CLICKED(IDC_TEXTURE_BUTTON4, &CSculptBlendOptions::OnBnClickedTextureButton4) ON_COMMAND(ID_BLEND_SELECT_TEXTURE_1, &CSculptBlendOptions::OnBnClickedTextureButton1) ON_COMMAND(ID_BLEND_SELECT_TEXTURE_2, &CSculptBlendOptions::OnBnClickedTextureButton2) ON_COMMAND(ID_BLEND_SELECT_TEXTURE_3, &CSculptBlendOptions::OnBnClickedTextureButton3) ON_COMMAND(ID_BLEND_SELECT_TEXTURE_4, &CSculptBlendOptions::OnBnClickedTextureButton4) ON_COMMAND(ID_BLEND_SHRINK_BRUSH, &CSculptBlendOptions::ShrinkBrush) ON_COMMAND(ID_BLEND_ENLARGE_BRUSH, &CSculptBlendOptions::EnlargeBrush) ON_BN_CLICKED(IDC_SET_COLOR, &CSculptBlendOptions::OnBnClickedSetColor) ON_BN_CLICKED(IDC_SET_COLOR2, &CSculptBlendOptions::OnBnClickedSetColor2) ON_NOTIFY(NM_CUSTOMDRAW, IDC_COLOR_BLEND_AMOUNT, &CSculptBlendOptions::OnNMCustomdrawColorBlendAmount) ON_CBN_SELCHANGE(IDC_BLEND_COLOR_OPERATION, &CSculptBlendOptions::OnCbnSelchangeBlendColorOperation) ON_NOTIFY(NM_CUSTOMDRAW, IDC_ALPHA_BLEND_AMOUNT, &CSculptBlendOptions::OnNMCustomdrawAlphaBlendAmount) ON_WM_RBUTTONDBLCLK() END_MESSAGE_MAP() void CSculptBlendOptions::OnShowWindow(BOOL bShow, UINT nStatus) { __super::OnShowWindow(bShow, nStatus); if ( bShow == FALSE ) { m_nDefaultFalloffPosition = m_FalloffPositionControl.GetPos(); m_nDefaultFalloffFinal = m_FalloffFinalControl.GetPos(); m_nDefaultBlendAmount = m_BlendAmountControl.GetPos(); m_nDefaultColorBlendAmount = m_ColorBlendAmountControl.GetPos(); m_nDefaultAlphaBlendAmount = m_AlphaBlendAmountControl.GetPos(); APP()->ClearCustomAccelerator(); return; } // Get the displacement manager from the active map document. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( pDispMgr ) { int nDispCount = pDispMgr->SelectCount(); for ( int iDisp = 0; iDisp < nDispCount; iDisp++ ) { CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp ); if ( pDisp ) { CMapFace *pFace = static_cast< CMapFace * >( pDisp->GetParent() ); IMaterial *pMaterial = pFace->GetTexture()->GetMaterial(); if ( strcmpi( pMaterial->GetShaderName(), "Lightmapped_4WayBlend" ) == 0 ) { m_b4WayBlendMode = true; } else { m_b4WayBlendMode = false; } for( int i = 1; i <= MAX_MULTIBLEND_CHANNELS; i++ ) { char temp[ 128 ]; if ( i == 1 ) { sprintf( temp, "$basetexture" ); } else { sprintf( temp, "$basetexture%d", i ); } IMaterialVar *pMaterialVar = pMaterial->FindVar( temp, NULL, false ); if ( pMaterialVar != NULL ) { IEditorTexture *pTexture = g_Textures.FindActiveTexture( pMaterialVar->GetStringValue() ); pTexture->Load(); m_TextureControl[ i - 1 ].SetTexture( pTexture ); m_TextureControl[ i - 1 ].SetSelected( ( m_nSelectedTexture == i - 1 ) ); m_TextureMaskControl[ i - 1 ].SetCheck( BST_CHECKED ); m_ColorMaskControl[ i - 1 ].SetCheck( BST_UNCHECKED ); } m_ColorMaskControl[ i - 1 ].EnableWindow( !m_b4WayBlendMode ); } m_AlphaBlendAmountControl.EnableWindow( !m_b4WayBlendMode ); m_AlphaBlendAmountTextControl.EnableWindow( !m_b4WayBlendMode ); m_ColorBlendAmountControl.EnableWindow( !m_b4WayBlendMode ); m_ColorBlendAmountTextControl.EnableWindow( !m_b4WayBlendMode ); m_BlendColorOperationControl.EnableWindow( !m_b4WayBlendMode ); m_ColorStartControl.EnableWindow( !m_b4WayBlendMode ); m_ColorEndControl.EnableWindow( !m_b4WayBlendMode ); break; } } } m_TextureControl[ m_nSelectedTexture ].SetSelected( true ); SelectTexture( m_nSelectedTexture ); APP()->SetCustomAccelerator( m_hWnd, IDR_BLEND_ACCELERATOR ); } //----------------------------------------------------------------------------- // 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 CSculptBlendOptions::BeginPaint( CMapView3D *pView, const Vector2D &vPoint ) { __super::BeginPaint( pView, vPoint ); if ( m_bCtrlDown ) { m_Direction = -1.0f; } else { m_Direction = 1.0f; } m_nLastCollideDisplacement = -1; return true; } //----------------------------------------------------------------------------- // Purpose: draws the tool in the 3d view // Input : pRender - the 3d renderer //----------------------------------------------------------------------------- void CSculptBlendOptions::RenderTool3D( CRender3D *pRender ) { pRender->PushRenderMode( RENDER_MODE_WIREFRAME ); m_flFalloffSpot = m_FalloffPositionControl.GetPos() / 100.0f; m_flFalloffEndingValue = m_FalloffFinalControl.GetPos() / 100.0f; 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_Direction < 0.0f ) { // red for negative blending 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(); } else { // green for positive blending 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(); } pRender->PopRenderMode(); } //----------------------------------------------------------------------------- // 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 CSculptBlendOptions::OnRMouseDown3D( CMapView3D *pView, UINT nFlags, const Vector2D &vPoint ) { CSculptTool::OnRMouseDown3D( pView, nFlags, vPoint ); return true; } bool CSculptBlendOptions::DoPaint( CMapView3D *pView, const Vector2D &vPoint ) { Vector vCollisionPoint, vCollisionNormal; float flCollisionIntercept; int nCollideDisplacement, nCollideTri; if ( FindCollisionIntercept( pView->GetCamera(), vPoint, true, vCollisionPoint, vCollisionNormal, flCollisionIntercept, &nCollideDisplacement, &nCollideTri ) == false ) { return false; } // if ( m_nLastCollideDisplacement != -1 && m_nLastCollideDisplacement == nCollideDisplacement && m_nLastCollideTri == nCollideTri ) // { // return false; // } m_nLastCollideDisplacement = nCollideDisplacement; m_nLastCollideTri = nCollideTri; return __super::DoPaint( pView, vPoint ); } //----------------------------------------------------------------------------- // 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 CSculptBlendOptions::DoPaintOperation( CMapView3D *pView, const Vector2D &vPoint, CMapDisp *pDisp, CMapDisp *pOrigDisp ) { Vector4D vBlend, vPaintBlend; Vector4D vAlphaBlend, vPaintAlphaBlend; Vector vColor[ MAX_MULTIBLEND_CHANNELS ], vPaintColor[ MAX_MULTIBLEND_CHANNELS ]; float flDistance; float flLengthPercent; int nIndex = m_nSelectedTexture; float flTextureBlendAmount = ( float )m_BlendAmountControl.GetPos() / 2000.0f; float flColorBlendAmount = ( float )m_ColorBlendAmountControl.GetPos() / 2000.0f; float flAlphaBlendAmount = ( float )m_AlphaBlendAmountControl.GetPos() / 1000.0f; bool bDrawTexture = ( m_BlendAmountControl.GetPos() > 0 ); bool bDrawAlpha = ( m_AlphaBlendAmountControl.GetPos() > 0 ); bool bDrawColor = ( m_ColorBlendAmountControl.GetPos() > 0 ); bool bDrawTextureChannel[ MAX_MULTIBLEND_CHANNELS ], bDrawColorChannel[ MAX_MULTIBLEND_CHANNELS ]; m_flFalloffSpot = m_FalloffPositionControl.GetPos() / 100.0f; m_flFalloffEndingValue = m_FalloffFinalControl.GetPos() / 100.0f; if ( nIndex < 0 ) { return; } flAlphaBlendAmount *= m_Direction; if ( WinTab_Opened() == true ) { flTextureBlendAmount *= WinTab_GetPressure(); flColorBlendAmount *= WinTab_GetPressure(); flAlphaBlendAmount *= WinTab_GetPressure(); } for( int i = 0; i < MAX_MULTIBLEND_CHANNELS; i++ ) { bDrawTextureChannel[ i ] = ( bDrawTexture == true && m_TextureMaskControl[ i ].GetCheck() == BST_CHECKED ); bDrawColorChannel[ i ] = ( bDrawColor == true && m_ColorMaskControl[ i ].GetCheck() == BST_CHECKED ); } AddToUndo( &pDisp ); int nVertCount = pDisp->GetSize(); for ( int iVert = 0; iVert < nVertCount; iVert++ ) { if ( IsPointInScreenCircle( pView, pDisp, pOrigDisp, iVert, true, false, &flLengthPercent ) ) { pDisp->GetMultiBlend( iVert, vBlend, vAlphaBlend, vColor[ 0 ], vColor[ 1 ], vColor[ 2 ], vColor[ 3 ] ); if ( flLengthPercent > m_flFalloffSpot ) { flLengthPercent = ( flLengthPercent - m_flFalloffSpot ) / ( 1.0f - m_flFalloffSpot ); flLengthPercent = 1.0 - flLengthPercent; flDistance = ( ( 1.0f - m_flFalloffEndingValue ) * flLengthPercent ) + ( m_flFalloffEndingValue ); } else { flDistance = 1.0f; } if ( flDistance == 0.0f ) { continue; } float flTextureAmount = flTextureBlendAmount * flDistance; float flColorAmount = flColorBlendAmount * flDistance; float flAlphaAmount = flAlphaBlendAmount * flDistance; vPaintBlend = vBlend; vPaintAlphaBlend = vAlphaBlend; vPaintColor[ 0 ] = vColor[ 0 ]; vPaintColor[ 1 ] = vColor[ 1 ]; vPaintColor[ 2 ] = vColor[ 2 ]; vPaintColor[ 3 ] = vColor[ 3 ]; Assert( MAX_MULTIBLEND_CHANNELS == 4 ); if ( bDrawTexture == true ) { if ( m_Direction > 0.0f ) { if ( nIndex == 4 ) { float flRemainder = flTextureAmount; #if 1 // for( int i = 1; i < MAX_MULTIBLEND_CHANNELS; i++ ) for( int i = MAX_MULTIBLEND_CHANNELS - 1; i > 0; i-- ) { if ( bDrawTextureChannel[ i ] == false ) { continue; } if ( vPaintBlend[ i ] > flRemainder ) { vPaintBlend[ i ] -= flRemainder; flRemainder = 0.0f; break; } else { flRemainder -= vPaintBlend[ i ]; vPaintBlend[ i ] = 0.0f; } } #else for( int i = MAX_MULTIBLEND_CHANNELS - 1; i > 0; i-- ) { if ( m_TextureMaskControl[ i ].GetCheck() != BST_CHECKED ) { continue; } if ( vPaintBlend[ i ] > flRemainder ) { vPaintBlend[ i ] -= flRemainder; } else { vPaintBlend[ i ] = 0.0f; } } #endif } else { if ( m_b4WayBlendMode ) { vPaintBlend[ nIndex ] += flTextureAmount; for ( int i = nIndex + 1; i < MAX_MULTIBLEND_CHANNELS; i++) { if ( bDrawTextureChannel[ i ] == false ) { continue; } if ( vPaintBlend[ i ] > flTextureAmount ) { vPaintBlend[ i ] -= flTextureAmount; } else { vPaintBlend[ i ] = 0.0f; } } } else // multiblend { for( int i = nIndex; i >= 0; i-- ) // for( int i = MAX_MULTIBLEND_CHANNELS - 1; i > 0; i-- ) { if ( i == nIndex ) { vPaintBlend[ i ] += flTextureAmount; } else { if ( bDrawTextureChannel[ i ] == false ) { continue; } if ( vPaintBlend[ i ] > flTextureAmount ) { vPaintBlend[ i ] -= flTextureAmount; flTextureAmount = 0.0f; } else { flTextureAmount -= vPaintBlend[ i ]; vPaintBlend[ i ] = 0.0f; } } } } } } else { vPaintBlend[ nIndex ] -= flTextureAmount; } vPaintBlend.x = clamp( vPaintBlend.x, 0.0f, 1.0f ); vPaintBlend.y = clamp( vPaintBlend.y, 0.0f, 1.0f ); vPaintBlend.z = clamp( vPaintBlend.z, 0.0f, 1.0f ); vPaintBlend.w = clamp( vPaintBlend.w, 0.0f, 1.0f ); } if ( bDrawColor == true ) { Vector vResultColor; switch( m_ColorMode[ nIndex ] ) { case COLOR_MODE_SINGLE: vResultColor = m_vStartDrawColor[ nIndex ]; break; case COLOR_MODE_RANGE: { float flRange = RandomFloat( 0.0f, 1.0f ); vResultColor.x = m_vStartDrawColor[ nIndex ].x + ( ( m_vEndDrawColor[ nIndex ].x - m_vStartDrawColor[ nIndex ].x ) * flRange ); vResultColor.y = m_vStartDrawColor[ nIndex ].y + ( ( m_vEndDrawColor[ nIndex ].y - m_vStartDrawColor[ nIndex ].y ) * flRange ); vResultColor.z = m_vStartDrawColor[ nIndex ].z + ( ( m_vEndDrawColor[ nIndex ].z - m_vStartDrawColor[ nIndex ].z ) * flRange ); } break; case COLOR_MODE_OR: if ( RandomInt( 1, 100 ) > 50 ) { vResultColor = m_vStartDrawColor[ nIndex ]; } else { vResultColor = m_vEndDrawColor[ nIndex ]; } break; } if ( m_Direction < 0.0f ) { vResultColor.Init( 1.0f, 1.0f, 1.0f ); } for( int i = 0; i < MAX_MULTIBLEND_CHANNELS; i++ ) { if ( bDrawColorChannel[ i ] == true ) { vPaintColor[ i ] = ( vPaintColor[ i ] * ( 1.0f - flColorAmount ) ) + ( vResultColor * flColorAmount ); } } } if ( bDrawAlpha == true ) { vPaintAlphaBlend[ nIndex ] = clamp( vPaintAlphaBlend[ nIndex ] + flAlphaAmount, 0.0f, 2.0f ); } pDisp->SetMultiBlend( iVert, vPaintBlend, vPaintAlphaBlend, vPaintColor[ 0 ], vPaintColor[ 1 ], vPaintColor[ 2 ], vPaintColor[ 3 ] ); } } } void CSculptBlendOptions::OnNMCustomdrawBlendAmount(NMHDR *pNMHDR, LRESULT *pResult) { // LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); // TODO: Add your control notification handler code here *pResult = 0; if ( m_BlendAmountControl.GetPos() == 0 ) { m_BlendAmountTextControl.SetWindowText( "Off" ); } else { char temp[ 128 ]; sprintf( temp, "%d%%", m_BlendAmountControl.GetPos() ); m_BlendAmountTextControl.SetWindowText( temp ); } } void CSculptBlendOptions::OnBnClickedTextureButton1() { m_TextureControl[ m_nSelectedTexture ].SetSelected( false ); SelectTexture( 0 ); m_TextureControl[ m_nSelectedTexture ].SetSelected( true ); } void CSculptBlendOptions::OnBnClickedTextureButton2() { m_TextureControl[ m_nSelectedTexture ].SetSelected( false ); SelectTexture( 1 ); m_TextureControl[ m_nSelectedTexture ].SetSelected( true ); } void CSculptBlendOptions::OnBnClickedTextureButton3() { m_TextureControl[ m_nSelectedTexture ].SetSelected( false ); SelectTexture( 2 ); m_TextureControl[ m_nSelectedTexture ].SetSelected( true ); } void CSculptBlendOptions::OnBnClickedTextureButton4() { m_TextureControl[ m_nSelectedTexture ].SetSelected( false ); SelectTexture( 3 ); m_TextureControl[ m_nSelectedTexture ].SetSelected( true ); } #define BRUSH_CHANGE_AMOUNT 4 void CSculptBlendOptions::ShrinkBrush() { if ( m_BrushSize > BRUSH_CHANGE_AMOUNT + 1 ) { m_BrushSize -= BRUSH_CHANGE_AMOUNT; } } void CSculptBlendOptions::EnlargeBrush() { m_BrushSize += BRUSH_CHANGE_AMOUNT; } #include void CSculptBlendOptions::OnBnClickedSetColor() { CColorDialog dlg( RGB( m_vStartDrawColor[ m_nSelectedTexture ].x * 255, m_vStartDrawColor[ m_nSelectedTexture ].y * 255, m_vStartDrawColor[ m_nSelectedTexture ].z * 255 ), CC_FULLOPEN ); if ( dlg.DoModal() == IDOK ) { m_vStartDrawColor[ m_nSelectedTexture ].x = GetRValue( dlg.m_cc.rgbResult ) / 255.0f; m_vStartDrawColor[ m_nSelectedTexture ].y = GetGValue( dlg.m_cc.rgbResult ) / 255.0f; m_vStartDrawColor[ m_nSelectedTexture ].z = GetBValue( dlg.m_cc.rgbResult ) / 255.0f; m_ColorStartControl.SetColor( m_vStartDrawColor[ m_nSelectedTexture ].x, m_vStartDrawColor[ m_nSelectedTexture ].y, m_vStartDrawColor[ m_nSelectedTexture ].z ); } } void CSculptBlendOptions::OnBnClickedSetColor2() { CColorDialog dlg( RGB( m_vEndDrawColor[ m_nSelectedTexture ].x * 255, m_vEndDrawColor[ m_nSelectedTexture ].y * 255, m_vEndDrawColor[ m_nSelectedTexture ].z * 255 ), CC_FULLOPEN ); if ( dlg.DoModal() == IDOK ) { m_vEndDrawColor[ m_nSelectedTexture ].x = GetRValue( dlg.m_cc.rgbResult ) / 255.0f; m_vEndDrawColor[ m_nSelectedTexture ].y = GetGValue( dlg.m_cc.rgbResult ) / 255.0f; m_vEndDrawColor[ m_nSelectedTexture ].z = GetBValue( dlg.m_cc.rgbResult ) / 255.0f; m_ColorEndControl.SetColor( m_vEndDrawColor[ m_nSelectedTexture ].x, m_vEndDrawColor[ m_nSelectedTexture ].y, m_vEndDrawColor[ m_nSelectedTexture ].z ); } } void CSculptBlendOptions::OnNMCustomdrawColorBlendAmount(NMHDR *pNMHDR, LRESULT *pResult) { *pResult = 0; if ( m_ColorBlendAmountControl.GetPos() == 0 ) { m_ColorBlendAmountTextControl.SetWindowText( "Off" ); } else { char temp[ 128 ]; sprintf( temp, "%d%%", m_ColorBlendAmountControl.GetPos() ); m_ColorBlendAmountTextControl.SetWindowText( temp ); } } void CSculptBlendOptions::OnCbnSelchangeBlendColorOperation() { SetColorMode( ( ColorMode )m_BlendColorOperationControl.GetCurSel(), false ); } void CSculptBlendOptions::OnNMCustomdrawAlphaBlendAmount(NMHDR *pNMHDR, LRESULT *pResult) { // LPNMCUSTOMDRAW pNMCD = reinterpret_cast(pNMHDR); *pResult = 0; if ( m_AlphaBlendAmountControl.GetPos() == 0 ) { m_AlphaBlendAmountTextControl.SetWindowText( "Off" ); } else { char temp[ 128 ]; sprintf( temp, "%d%%", m_AlphaBlendAmountControl.GetPos() ); m_AlphaBlendAmountTextControl.SetWindowText( temp ); } } void CSculptBlendOptions::OnRButtonDblClk(UINT nFlags, CPoint point) { __super::OnRButtonDblClk(nFlags, point); // Get the displacement manager from the active map document. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager(); if( !pDispMgr ) { return; } bool bDrawTexture = ( m_BlendAmountControl.GetPos() > 0 ); bool bDrawAlpha = ( m_AlphaBlendAmountControl.GetPos() > 0 ); bool bDrawColor = ( m_ColorBlendAmountControl.GetPos() > 0 ); bool bDrawTextureChannel[ MAX_MULTIBLEND_CHANNELS ], bDrawColorChannel[ MAX_MULTIBLEND_CHANNELS ]; Vector4D vBlend, vPaintBlend, vAlphaBlend, vPaintAlphaBlend; Vector vColor[ MAX_MULTIBLEND_CHANNELS ], vPaintColor[ MAX_MULTIBLEND_CHANNELS ]; vPaintBlend.Init( 1.0f, 0.0f, 0.0f, 0.0f ); vPaintAlphaBlend.Init(); for( int i = 0; i < MAX_MULTIBLEND_CHANNELS; i++ ) { vPaintColor[ i ] = Vector( 1.0f, 1.0f, 1.0f ); bDrawTextureChannel[ i ] = ( bDrawTexture == true && m_TextureMaskControl[ i ].GetCheck() == BST_CHECKED ); bDrawColorChannel[ i ] = ( bDrawColor == true && m_ColorMaskControl[ i ].GetCheck() == BST_CHECKED ); } // 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 ) { AddToUndo( &pDisp ); int nVertCount = pDisp->GetSize(); for ( int iVert = 0; iVert < nVertCount; iVert++ ) { pDisp->GetMultiBlend( iVert, vBlend, vAlphaBlend, vColor[ 0 ], vColor[ 1 ], vColor[ 2 ], vColor[ 3 ] ); if ( bDrawAlpha == true ) { vAlphaBlend = vPaintAlphaBlend; } for( int i = 0; i < MAX_MULTIBLEND_CHANNELS; i++ ) { if ( bDrawTextureChannel[ i ] == true ) { vBlend[ i ] = vPaintBlend[ i ]; } if ( bDrawColorChannel[ i ] == true ) { vColor[ i ] = vPaintColor[ i ]; } } pDisp->SetMultiBlend( iVert, vBlend, vAlphaBlend, vColor[ 0 ], vColor[ 1 ], vColor[ 2 ], vColor[ 3 ] ); } } } pDispMgr->PostUndo(); }