Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

443 lines
13 KiB

//========= 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 );
}