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