443 lines
13 KiB
C++
443 lines
13 KiB
C++
//========= 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 );
|
|
} |