summaryrefslogtreecommitdiff
path: root/hammer/disppaint.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'hammer/disppaint.cpp')
-rw-r--r--hammer/disppaint.cpp443
1 files changed, 443 insertions, 0 deletions
diff --git a/hammer/disppaint.cpp b/hammer/disppaint.cpp
new file mode 100644
index 0000000..02383cd
--- /dev/null
+++ b/hammer/disppaint.cpp
@@ -0,0 +1,443 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include <stdafx.h>
+#include "DispPaint.h"
+#include "ToolDisplace.h"
+#include "CollisionUtils.h"
+#include "DispManager.h"
+#include "MapDoc.h"
+#include "MapDisp.h"
+#include "GlobalFunctions.h"
+#include "History.h"
+#include "DispSew.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+#define DISPPAINT_RADIUS_OUTER_CLAMP 0.01f
+
+//-----------------------------------------------------------------------------
+// Purpose: constructor
+//-----------------------------------------------------------------------------
+CDispPaintMgr::CDispPaintMgr()
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: destructor
+//-----------------------------------------------------------------------------
+CDispPaintMgr::~CDispPaintMgr()
+{
+ m_aNudgeData.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CDispPaintMgr::Paint( SpatialPaintData_t &spatialData, bool bAutoSew )
+{
+ // Setup painting.
+ if ( !PrePaint( spatialData ) )
+ return false;
+
+ // Handle painting.
+ if ( !DoPaint( spatialData ) )
+ return false;
+
+ // Finish painting.
+ if ( !PostPaint( bAutoSew ) )
+ return false;
+
+ // Successful paint operation.
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CDispPaintMgr::PrePaint( SpatialPaintData_t &spatialData )
+{
+ // Generate cached spatial data.
+ spatialData.m_flRadius2 = ( spatialData.m_flRadius * spatialData.m_flRadius );
+ spatialData.m_flOORadius2 = 1.0f / spatialData.m_flRadius2;
+
+ // Setup nudge data.
+ if ( spatialData.m_bNudgeInit )
+ {
+ m_aNudgeData.RemoveAll();
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CDispPaintMgr::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:
+//-----------------------------------------------------------------------------
+bool CDispPaintMgr::DoPaint( SpatialPaintData_t &spatialData )
+{
+ // Get the displacement manager from the active map document.
+ IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
+ if( !pDispMgr )
+ return false;
+
+ // Special case - nudging!
+ if ( spatialData.m_bNudge && !spatialData.m_bNudgeInit )
+ {
+ DoNudgeAdd( spatialData );
+ return true;
+ }
+
+ // 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 )
+ {
+ // Test paint sphere displacement bbox for overlap.
+ Vector vBBoxMin, vBBoxMax;
+ pDisp->GetBoundingBox( vBBoxMin, vBBoxMax );
+ if ( PaintSphereDispBBoxOverlap( spatialData.m_vCenter, spatialData.m_flRadius, vBBoxMin, vBBoxMax ) )
+ {
+ // Paint with the correct effect
+ switch ( spatialData.m_nEffect )
+ {
+ case DISPPAINT_EFFECT_RAISELOWER:
+ {
+ DoPaintAdd( spatialData, pDisp );
+ break;
+ }
+ case DISPPAINT_EFFECT_RAISETO:
+ {
+ DoPaintEqual( spatialData, pDisp );
+ break;
+ }
+ case DISPPAINT_EFFECT_SMOOTH:
+ {
+ DoPaintSmooth( spatialData, pDisp );
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Successful paint.
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDispPaintMgr::NudgeAdd( CMapDisp *pDisp, int iVert )
+{
+ int iNudge = m_aNudgeData.AddToTail();
+ m_aNudgeData[iNudge].m_hDisp = pDisp->GetEditHandle();
+ m_aNudgeData[iNudge].m_iVert = iVert;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDispPaintMgr::DoNudgeAdd( SpatialPaintData_t &spatialData )
+{
+ Vector vPaintPos, vVert;
+ float flDistance2;
+
+ int nNudgeCount = m_aNudgeData.Count();
+ for ( int iNudge = 0; iNudge < nNudgeCount; iNudge++ )
+ {
+ DispVertPair_t *pPairData = &m_aNudgeData[iNudge];
+
+ // Get the current vert.
+ CMapDisp *pDisp = EditDispMgr()->GetDisp( pPairData->m_hDisp );
+ pDisp->GetVert( pPairData->m_iVert, vVert );
+
+ if ( IsInSphereRadius( spatialData.m_vCenter, spatialData.m_flRadius2, vVert, flDistance2 ) )
+ {
+ // Build the new position (paint value) and set it.
+ if ( spatialData.m_uiBrushType == DISPPAINT_BRUSHTYPE_SOFT )
+ {
+ DoPaintOneOverR( spatialData, vVert, flDistance2, vPaintPos );
+ }
+ else if ( spatialData.m_uiBrushType == DISPPAINT_BRUSHTYPE_HARD )
+ {
+ DoPaintOne( spatialData, vVert, vPaintPos );
+ }
+ AddToUndo( &pDisp );
+ pDisp->Paint_SetValue( pPairData->m_iVert, vPaintPos );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CDispPaintMgr::PaintSphereDispBBoxOverlap( const Vector &vCenter, float flRadius,
+ const Vector &vBBoxMin, const Vector &vBBoxMax )
+{
+ return IsBoxIntersectingSphere( vBBoxMin, vBBoxMax, vCenter, flRadius );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CDispPaintMgr::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:
+//-----------------------------------------------------------------------------
+void CDispPaintMgr::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 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDispPaintMgr::DoPaintAdd( SpatialPaintData_t &spatialData, CMapDisp *pDisp )
+{
+ Vector vPaintPos, vVert;
+ 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 ) )
+ {
+ // Build the new position (paint value) and set it.
+ if ( spatialData.m_uiBrushType == DISPPAINT_BRUSHTYPE_SOFT )
+ {
+ DoPaintOneOverR( spatialData, vVert, flDistance2, vPaintPos );
+ }
+ else if ( spatialData.m_uiBrushType == DISPPAINT_BRUSHTYPE_HARD )
+ {
+ DoPaintOne( spatialData, vVert, vPaintPos );
+ }
+ AddToUndo( &pDisp );
+ pDisp->Paint_SetValue( iVert, vPaintPos );
+
+ // Add data to nudge list.
+ if ( spatialData.m_bNudgeInit )
+ {
+ NudgeAdd( pDisp, iVert );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDispPaintMgr::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 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDispPaintMgr::DoPaintSmooth( SpatialPaintData_t &spatialData, CMapDisp *pDisp )
+{
+ Vector vPaintPos, vVert;
+ 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 ) )
+ {
+ // Build the new smoothed position and set it.
+ if ( DoPaintSmoothOneOverExp( spatialData, vVert, vPaintPos ) )
+ {
+ AddToUndo( &pDisp );
+ pDisp->Paint_SetValue( iVert, vPaintPos );
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CDispPaintMgr::CalcSmoothRadius2( const SpatialPaintData_t &spatialData, const Vector &vPoint )
+{
+ Vector vTmp;
+ VectorSubtract( spatialData.m_vCenter, vPoint, vTmp );
+ float flDistance2 = ( vTmp.x * vTmp.x ) + ( vTmp.y * vTmp.y ) + ( vTmp.z * vTmp.z );
+
+ float flRatio = flDistance2 / spatialData.m_flRadius2;
+ flRatio = 1.0f - flRatio;
+
+ float flRadius = flRatio * spatialData.m_flRadius;
+ return ( flRadius * flRadius );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CDispPaintMgr::DoPaintSmoothOneOverExp( const SpatialPaintData_t &spatialData,
+ 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( spatialData, vNewCenter );
+ 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 = 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 / ( spatialData.m_flScalar * 2.0f );
+ }
+
+ Vector vProjectVert;
+ float flProjectDist = DotProduct( vVert, spatialData.m_vPaintAxis ) - flPaintDist;
+ flSmoothDist += ( flProjectDist * flFactor );
+ flWeight += flFactor;
+ }
+ }
+ }
+ }
+ }
+
+ // Re-normalize the smoothing position.
+ flSmoothDist /= flWeight;
+ vPaintPos = vNewCenter + ( spatialData.m_vPaintAxis * flSmoothDist );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDispPaintMgr::DoPaintOneOverR( const SpatialPaintData_t &spatialData,
+ const Vector &vPos, float flDistance2,
+ Vector &vNewPos )
+{
+ float flValue = 1.0f - ( flDistance2 * spatialData.m_flOORadius2 );
+ flValue *= spatialData.m_flScalar;
+ VectorScale( spatialData.m_vPaintAxis, flValue, vNewPos );
+ VectorAdd( vNewPos, vPos, vNewPos );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDispPaintMgr::DoPaintOne( const SpatialPaintData_t &spatialData,
+ const Vector &vPos, Vector &vNewPos )
+{
+ float flValue = spatialData.m_flScalar;
+ VectorScale( spatialData.m_vPaintAxis, flValue, vNewPos );
+ VectorAdd( vNewPos, vPos, vNewPos );
+} \ No newline at end of file