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.

442 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdafx.h>
  8. #include "DispPaint.h"
  9. #include "ToolDisplace.h"
  10. #include "CollisionUtils.h"
  11. #include "DispManager.h"
  12. #include "MapDoc.h"
  13. #include "MapDisp.h"
  14. #include "GlobalFunctions.h"
  15. #include "History.h"
  16. #include "DispSew.h"
  17. // memdbgon must be the last include file in a .cpp file!!!
  18. #include <tier0/memdbgon.h>
  19. #define DISPPAINT_RADIUS_OUTER_CLAMP 0.01f
  20. //-----------------------------------------------------------------------------
  21. // Purpose: constructor
  22. //-----------------------------------------------------------------------------
  23. CDispPaintMgr::CDispPaintMgr()
  24. {
  25. }
  26. //-----------------------------------------------------------------------------
  27. // Purpose: destructor
  28. //-----------------------------------------------------------------------------
  29. CDispPaintMgr::~CDispPaintMgr()
  30. {
  31. m_aNudgeData.Purge();
  32. }
  33. //-----------------------------------------------------------------------------
  34. // Purpose:
  35. //-----------------------------------------------------------------------------
  36. bool CDispPaintMgr::Paint( SpatialPaintData_t &spatialData, bool bAutoSew )
  37. {
  38. // Setup painting.
  39. if ( !PrePaint( spatialData ) )
  40. return false;
  41. // Handle painting.
  42. if ( !DoPaint( spatialData ) )
  43. return false;
  44. // Finish painting.
  45. if ( !PostPaint( bAutoSew ) )
  46. return false;
  47. // Successful paint operation.
  48. return true;
  49. }
  50. //-----------------------------------------------------------------------------
  51. // Purpose:
  52. //-----------------------------------------------------------------------------
  53. bool CDispPaintMgr::PrePaint( SpatialPaintData_t &spatialData )
  54. {
  55. // Generate cached spatial data.
  56. spatialData.m_flRadius2 = ( spatialData.m_flRadius * spatialData.m_flRadius );
  57. spatialData.m_flOORadius2 = 1.0f / spatialData.m_flRadius2;
  58. // Setup nudge data.
  59. if ( spatialData.m_bNudgeInit )
  60. {
  61. m_aNudgeData.RemoveAll();
  62. }
  63. return true;
  64. }
  65. //-----------------------------------------------------------------------------
  66. // Purpose:
  67. //-----------------------------------------------------------------------------
  68. bool CDispPaintMgr::PostPaint( bool bAutoSew )
  69. {
  70. // Get the displacement manager from the active map document.
  71. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
  72. if( !pDispMgr )
  73. return false;
  74. // Update the modified displacements.
  75. int nDispCount = pDispMgr->SelectCount();
  76. for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
  77. {
  78. CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
  79. if ( pDisp )
  80. {
  81. pDisp->Paint_Update( false );
  82. }
  83. }
  84. // Auto "sew" if necessary.
  85. if ( bAutoSew )
  86. {
  87. FaceListSewEdges();
  88. }
  89. return true;
  90. }
  91. //-----------------------------------------------------------------------------
  92. // Purpose:
  93. //-----------------------------------------------------------------------------
  94. bool CDispPaintMgr::DoPaint( SpatialPaintData_t &spatialData )
  95. {
  96. // Get the displacement manager from the active map document.
  97. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
  98. if( !pDispMgr )
  99. return false;
  100. // Special case - nudging!
  101. if ( spatialData.m_bNudge && !spatialData.m_bNudgeInit )
  102. {
  103. DoNudgeAdd( spatialData );
  104. return true;
  105. }
  106. // For each displacement surface is the selection list attempt to paint on it.
  107. int nDispCount = pDispMgr->SelectCount();
  108. for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
  109. {
  110. CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
  111. if ( pDisp )
  112. {
  113. // Test paint sphere displacement bbox for overlap.
  114. Vector vBBoxMin, vBBoxMax;
  115. pDisp->GetBoundingBox( vBBoxMin, vBBoxMax );
  116. if ( PaintSphereDispBBoxOverlap( spatialData.m_vCenter, spatialData.m_flRadius, vBBoxMin, vBBoxMax ) )
  117. {
  118. // Paint with the correct effect
  119. switch ( spatialData.m_nEffect )
  120. {
  121. case DISPPAINT_EFFECT_RAISELOWER:
  122. {
  123. DoPaintAdd( spatialData, pDisp );
  124. break;
  125. }
  126. case DISPPAINT_EFFECT_RAISETO:
  127. {
  128. DoPaintEqual( spatialData, pDisp );
  129. break;
  130. }
  131. case DISPPAINT_EFFECT_SMOOTH:
  132. {
  133. DoPaintSmooth( spatialData, pDisp );
  134. break;
  135. }
  136. }
  137. }
  138. }
  139. }
  140. // Successful paint.
  141. return true;
  142. }
  143. //-----------------------------------------------------------------------------
  144. // Purpose:
  145. //-----------------------------------------------------------------------------
  146. void CDispPaintMgr::NudgeAdd( CMapDisp *pDisp, int iVert )
  147. {
  148. int iNudge = m_aNudgeData.AddToTail();
  149. m_aNudgeData[iNudge].m_hDisp = pDisp->GetEditHandle();
  150. m_aNudgeData[iNudge].m_iVert = iVert;
  151. }
  152. //-----------------------------------------------------------------------------
  153. // Purpose:
  154. //-----------------------------------------------------------------------------
  155. void CDispPaintMgr::DoNudgeAdd( SpatialPaintData_t &spatialData )
  156. {
  157. Vector vPaintPos, vVert;
  158. float flDistance2;
  159. int nNudgeCount = m_aNudgeData.Count();
  160. for ( int iNudge = 0; iNudge < nNudgeCount; iNudge++ )
  161. {
  162. DispVertPair_t *pPairData = &m_aNudgeData[iNudge];
  163. // Get the current vert.
  164. CMapDisp *pDisp = EditDispMgr()->GetDisp( pPairData->m_hDisp );
  165. pDisp->GetVert( pPairData->m_iVert, vVert );
  166. if ( IsInSphereRadius( spatialData.m_vCenter, spatialData.m_flRadius2, vVert, flDistance2 ) )
  167. {
  168. // Build the new position (paint value) and set it.
  169. if ( spatialData.m_uiBrushType == DISPPAINT_BRUSHTYPE_SOFT )
  170. {
  171. DoPaintOneOverR( spatialData, vVert, flDistance2, vPaintPos );
  172. }
  173. else if ( spatialData.m_uiBrushType == DISPPAINT_BRUSHTYPE_HARD )
  174. {
  175. DoPaintOne( spatialData, vVert, vPaintPos );
  176. }
  177. AddToUndo( &pDisp );
  178. pDisp->Paint_SetValue( pPairData->m_iVert, vPaintPos );
  179. }
  180. }
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose:
  184. //-----------------------------------------------------------------------------
  185. bool CDispPaintMgr::PaintSphereDispBBoxOverlap( const Vector &vCenter, float flRadius,
  186. const Vector &vBBoxMin, const Vector &vBBoxMax )
  187. {
  188. return IsBoxIntersectingSphere( vBBoxMin, vBBoxMax, vCenter, flRadius );
  189. }
  190. //-----------------------------------------------------------------------------
  191. // Purpose:
  192. //-----------------------------------------------------------------------------
  193. bool CDispPaintMgr::IsInSphereRadius( const Vector &vCenter, float flRadius2,
  194. const Vector &vPos, float &flDistance2 )
  195. {
  196. Vector vTmp;
  197. VectorSubtract( vPos, vCenter, vTmp );
  198. flDistance2 = ( vTmp.x * vTmp.x ) + ( vTmp.y * vTmp.y ) + ( vTmp.z * vTmp.z );
  199. return ( flDistance2 < flRadius2 );
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Purpose:
  203. //-----------------------------------------------------------------------------
  204. void CDispPaintMgr::AddToUndo( CMapDisp **pDisp )
  205. {
  206. CMapDisp *pUndoDisp = *pDisp;
  207. if ( pUndoDisp->Paint_IsDirty() )
  208. return;
  209. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
  210. if( pDispMgr )
  211. {
  212. EditDispHandle_t handle = pUndoDisp->GetEditHandle();
  213. pDispMgr->Undo( handle, false );
  214. *pDisp = EditDispMgr()->GetDisp( handle );
  215. }
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Purpose:
  219. //-----------------------------------------------------------------------------
  220. void CDispPaintMgr::DoPaintAdd( SpatialPaintData_t &spatialData, CMapDisp *pDisp )
  221. {
  222. Vector vPaintPos, vVert;
  223. float flDistance2;
  224. int nVertCount = pDisp->GetSize();
  225. for ( int iVert = 0; iVert < nVertCount; iVert++ )
  226. {
  227. // Get the current vert.
  228. pDisp->GetVert( iVert, vVert );
  229. if ( IsInSphereRadius( spatialData.m_vCenter, spatialData.m_flRadius2, vVert, flDistance2 ) )
  230. {
  231. // Build the new position (paint value) and set it.
  232. if ( spatialData.m_uiBrushType == DISPPAINT_BRUSHTYPE_SOFT )
  233. {
  234. DoPaintOneOverR( spatialData, vVert, flDistance2, vPaintPos );
  235. }
  236. else if ( spatialData.m_uiBrushType == DISPPAINT_BRUSHTYPE_HARD )
  237. {
  238. DoPaintOne( spatialData, vVert, vPaintPos );
  239. }
  240. AddToUndo( &pDisp );
  241. pDisp->Paint_SetValue( iVert, vPaintPos );
  242. // Add data to nudge list.
  243. if ( spatialData.m_bNudgeInit )
  244. {
  245. NudgeAdd( pDisp, iVert );
  246. }
  247. }
  248. }
  249. }
  250. //-----------------------------------------------------------------------------
  251. // Purpose:
  252. //-----------------------------------------------------------------------------
  253. void CDispPaintMgr::DoPaintEqual( SpatialPaintData_t &spatialData, CMapDisp *pDisp )
  254. {
  255. Vector vPaintPos, vVert, vFlatVert;
  256. float flDistance2;
  257. int nVertCount = pDisp->GetSize();
  258. for ( int iVert = 0; iVert < nVertCount; iVert++ )
  259. {
  260. // Get the current vert.
  261. pDisp->GetVert( iVert, vVert );
  262. if ( IsInSphereRadius( spatialData.m_vCenter, spatialData.m_flRadius2, vVert, flDistance2 ) )
  263. {
  264. // Get the base vert.
  265. pDisp->GetFlatVert( iVert, vFlatVert );
  266. // Build the new position (paint value) and set it.
  267. DoPaintOne( spatialData, vFlatVert, vPaintPos );
  268. AddToUndo( &pDisp );
  269. pDisp->Paint_SetValue( iVert, vPaintPos );
  270. }
  271. }
  272. }
  273. //-----------------------------------------------------------------------------
  274. // Purpose:
  275. //-----------------------------------------------------------------------------
  276. void CDispPaintMgr::DoPaintSmooth( SpatialPaintData_t &spatialData, CMapDisp *pDisp )
  277. {
  278. Vector vPaintPos, vVert;
  279. float flDistance2;
  280. int nVertCount = pDisp->GetSize();
  281. for ( int iVert = 0; iVert < nVertCount; iVert++ )
  282. {
  283. // Get the current vert.
  284. pDisp->GetVert( iVert, vVert );
  285. if ( IsInSphereRadius( spatialData.m_vCenter, spatialData.m_flRadius2, vVert, flDistance2 ) )
  286. {
  287. // Build the new smoothed position and set it.
  288. if ( DoPaintSmoothOneOverExp( spatialData, vVert, vPaintPos ) )
  289. {
  290. AddToUndo( &pDisp );
  291. pDisp->Paint_SetValue( iVert, vPaintPos );
  292. }
  293. }
  294. }
  295. }
  296. //-----------------------------------------------------------------------------
  297. // Purpose:
  298. //-----------------------------------------------------------------------------
  299. float CDispPaintMgr::CalcSmoothRadius2( const SpatialPaintData_t &spatialData, const Vector &vPoint )
  300. {
  301. Vector vTmp;
  302. VectorSubtract( spatialData.m_vCenter, vPoint, vTmp );
  303. float flDistance2 = ( vTmp.x * vTmp.x ) + ( vTmp.y * vTmp.y ) + ( vTmp.z * vTmp.z );
  304. float flRatio = flDistance2 / spatialData.m_flRadius2;
  305. flRatio = 1.0f - flRatio;
  306. float flRadius = flRatio * spatialData.m_flRadius;
  307. return ( flRadius * flRadius );
  308. }
  309. //-----------------------------------------------------------------------------
  310. // Purpose:
  311. //-----------------------------------------------------------------------------
  312. bool CDispPaintMgr::DoPaintSmoothOneOverExp( const SpatialPaintData_t &spatialData,
  313. const Vector &vNewCenter,
  314. Vector &vPaintPos )
  315. {
  316. // Get the displacement manager from the active map document.
  317. IWorldEditDispMgr *pDispMgr = GetActiveWorldEditDispManager();
  318. if( !pDispMgr )
  319. return false;
  320. // Calculate the smoothing radius.
  321. float flNewRadius2 = CalcSmoothRadius2( spatialData, vNewCenter );
  322. float flNewRadius = ( float )sqrt( flNewRadius2 );
  323. // Test all selected surfaces for smoothing.
  324. float flWeight = 0.0f;
  325. float flSmoothDist = 0.0f;
  326. // Calculate the plane dist.
  327. float flPaintDist = spatialData.m_vPaintAxis.Dot( vNewCenter );
  328. int nDispCount = pDispMgr->SelectCount();
  329. for ( int iDisp = 0; iDisp < nDispCount; iDisp++ )
  330. {
  331. CMapDisp *pDisp = pDispMgr->GetFromSelect( iDisp );
  332. if ( pDisp )
  333. {
  334. // Test paint sphere displacement bbox for overlap.
  335. Vector vBBoxMin, vBBoxMax;
  336. pDisp->GetBoundingBox( vBBoxMin, vBBoxMax );
  337. if ( PaintSphereDispBBoxOverlap( vNewCenter, flNewRadius, vBBoxMin, vBBoxMax ) )
  338. {
  339. Vector vVert;
  340. int nVertCount = pDisp->GetSize();
  341. for ( int iVert = 0; iVert < nVertCount; iVert++ )
  342. {
  343. // Get the current vert.
  344. pDisp->GetVert( iVert, vVert );
  345. float flDistance2 = 0.0f;
  346. if ( IsInSphereRadius( vNewCenter, flNewRadius2, vVert, flDistance2 ) )
  347. {
  348. float flRatio = flDistance2 / flNewRadius2;
  349. float flFactor = 1.0f / exp( flRatio );
  350. if ( flFactor != 1.0f )
  351. {
  352. flFactor *= 1.0f / ( spatialData.m_flScalar * 2.0f );
  353. }
  354. Vector vProjectVert;
  355. float flProjectDist = DotProduct( vVert, spatialData.m_vPaintAxis ) - flPaintDist;
  356. flSmoothDist += ( flProjectDist * flFactor );
  357. flWeight += flFactor;
  358. }
  359. }
  360. }
  361. }
  362. }
  363. // Re-normalize the smoothing position.
  364. flSmoothDist /= flWeight;
  365. vPaintPos = vNewCenter + ( spatialData.m_vPaintAxis * flSmoothDist );
  366. return true;
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Purpose:
  370. //-----------------------------------------------------------------------------
  371. void CDispPaintMgr::DoPaintOneOverR( const SpatialPaintData_t &spatialData,
  372. const Vector &vPos, float flDistance2,
  373. Vector &vNewPos )
  374. {
  375. float flValue = 1.0f - ( flDistance2 * spatialData.m_flOORadius2 );
  376. flValue *= spatialData.m_flScalar;
  377. VectorScale( spatialData.m_vPaintAxis, flValue, vNewPos );
  378. VectorAdd( vNewPos, vPos, vNewPos );
  379. }
  380. //-----------------------------------------------------------------------------
  381. // Purpose:
  382. //-----------------------------------------------------------------------------
  383. void CDispPaintMgr::DoPaintOne( const SpatialPaintData_t &spatialData,
  384. const Vector &vPos, Vector &vNewPos )
  385. {
  386. float flValue = spatialData.m_flScalar;
  387. VectorScale( spatialData.m_vPaintAxis, flValue, vNewPos );
  388. VectorAdd( vNewPos, vPos, vNewPos );
  389. }