Counter Strike : Global Offensive Source Code
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.

1656 lines
46 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //==========================================================================//
  6. #include "paint.h"
  7. #include "materialsystem/imaterial.h"
  8. #include "pixelwriter.h"
  9. #include "gl_model_private.h"
  10. #include "gl_matsysiface.h"
  11. #include "dt_common.h"
  12. #include "keyvalues.h"
  13. #include <vstdlib/random.h> // RandomFloat
  14. #include "collisionutils.h" // ray triangle test
  15. #include "cmodel_private.h"
  16. // debug stuff
  17. #include "debugoverlay.h"
  18. #include "con_nprint.h"
  19. // src/public/
  20. #include "game/shared/portal2/paint_enum.h"
  21. #include "tier1/callqueue.h"
  22. // memdbgon must be the last include file in a .cpp file!!!
  23. #include "tier0/memdbgon.h"
  24. extern ConVar r_redownloadallpaintmaps;
  25. static const BYTE NUM_ALPHA_BITS = 5;
  26. static const BYTE PAINT_COLOR_BITS = 7 << NUM_ALPHA_BITS; // 224
  27. static const BYTE PAINT_ALPHA_BITS = PAINT_COLOR_BITS ^ 0xFF; // 31
  28. ConVar paint_max_surface_border_alpha("paint_max_surface_border_alpha", "0.7f", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED );
  29. ConVar paint_alpha_offset_enabled("paint_alpha_offset_enabled", "1", FCVAR_DEVELOPMENTONLY | FCVAR_REPLICATED );
  30. ConVar paintsplat_bias("paintsplat_bias", "0.1f", FCVAR_REPLICATED | FCVAR_CHEAT, "Change bias value for computing circle buffer" );
  31. ConVar paintsplat_noise_enabled("paintsplat_noise_enabled", "1", FCVAR_REPLICATED | FCVAR_CHEAT );
  32. ConVar paintsplat_max_alpha_noise("paintsplat_max_alpha_noise", "0.1f", FCVAR_REPLICATED | FCVAR_CHEAT, "Max noise value of circle alpha" );
  33. ConVar paint_min_valid_alpha_value("paint_min_valid_alpha_value", "0.7f", FCVAR_REPLICATED | FCVAR_DEVELOPMENTONLY );
  34. ConVar debug_paint_alpha("debug_paint_alpha", "0", FCVAR_DEVELOPMENTONLY );
  35. int GetDiameter( int radius )
  36. {
  37. return radius * 2 + 1;
  38. }
  39. BYTE GetColorIndex( BYTE byte )
  40. {
  41. return ( PAINT_COLOR_BITS & byte ) >> NUM_ALPHA_BITS;
  42. }
  43. void SetColorBits( BYTE& byte, BYTE colorIndex, float alpha )
  44. {
  45. BYTE nAlpha = static_cast< BYTE >( alpha * PAINT_ALPHA_BITS );
  46. BYTE nColor = colorIndex << NUM_ALPHA_BITS;
  47. byte = nColor | nAlpha;
  48. }
  49. float GetAlpha( BYTE byte )
  50. {
  51. float alpha = ( PAINT_ALPHA_BITS & byte );
  52. alpha /= PAINT_ALPHA_BITS;
  53. alpha = clamp( alpha, 0.f, 1.f );
  54. return alpha;
  55. }
  56. #if !defined( LINUX )
  57. // draw a surface in random color
  58. void DebugDrawSurface( SurfaceHandle_t surfID )
  59. {
  60. Color surfColor = Color( RandomInt(0, 255), RandomInt(0, 255), RandomInt(0, 255), 128 );
  61. // check if the sphere actually intersecting with the surface using barycentric test
  62. int nFirstVertex = MSurf_FirstVertIndex( surfID );
  63. int numVert = MSurf_VertCount( surfID );
  64. int vertIndex = host_state.worldbrush->vertindices[nFirstVertex];
  65. Vector vOrigin = host_state.worldbrush->vertexes[vertIndex].position;
  66. for (int v = 1; v < numVert - 1; ++v )
  67. {
  68. vertIndex = host_state.worldbrush->vertindices[nFirstVertex+v];
  69. Vector v1 = host_state.worldbrush->vertexes[vertIndex].position;
  70. vertIndex = host_state.worldbrush->vertindices[nFirstVertex+v+1];
  71. Vector v2 = host_state.worldbrush->vertexes[vertIndex].position;
  72. CDebugOverlay::AddTriangleOverlay( vOrigin, v1, v2, surfColor.r(), surfColor.g(), surfColor.b(), 128, false, 0.1f );
  73. }
  74. }
  75. #endif
  76. extern MaterialSystem_SortInfo_t *materialSortInfoArray;
  77. CPaintmapDataManager g_PaintManager;
  78. CPaintmapDataManager::CPaintmapDataManager( void )
  79. : m_pPaintTextureDataArray( NULL ), m_iPaintmaps( 0 ), m_bShouldRegister( false )
  80. {
  81. }
  82. CPaintmapDataManager::~CPaintmapDataManager( void )
  83. {
  84. }
  85. void CPaintmapDataManager::DestroyPaintmapsData( void )
  86. {
  87. if( m_pPaintTextureDataArray )
  88. {
  89. for( int i = 0; i != m_iPaintmaps; ++i )
  90. {
  91. m_pPaintTextureDataArray[i].Destroy();
  92. }
  93. delete []m_pPaintTextureDataArray;
  94. m_pPaintTextureDataArray = NULL;
  95. m_iPaintmaps = 0;
  96. }
  97. }
  98. void R_UpdatePaintmapRect( int paintmap, BYTE* pPaintData, int numRects, Rect_t* pRects )
  99. {
  100. materials->UpdatePaintmap( paintmap, pPaintData, numRects, pRects );
  101. }
  102. void R_UpdatePaintmap( ICallQueue *pCallQueue, int paintmap, BYTE* pPaintData, int numRects, Rect_t* pRects )
  103. {
  104. if ( !pCallQueue )
  105. {
  106. R_UpdatePaintmapRect( paintmap, pPaintData, numRects, pRects );
  107. }
  108. else
  109. {
  110. pCallQueue->QueueCall( R_UpdatePaintmapRect, paintmap, pPaintData, numRects, CUtlEnvelope<Rect_t>( pRects, numRects ) );
  111. }
  112. }
  113. void MarkSurfaceBrushes( int nSurfIndex, worldbrushdata_t *pData = host_state.worldbrush )
  114. {
  115. if ( !pData->m_pSurfaceBrushList )
  116. return;
  117. const dfacebrushlist_t &brushList = pData->m_pSurfaceBrushList[nSurfIndex];
  118. const uint16 *pBrushIndex = (brushList.m_nFaceBrushCount <= 1) ? &brushList.m_nFaceBrushStart : &pData->m_pSurfaceBrushes[brushList.m_nFaceBrushStart];
  119. CCollisionBSPData *pBSPData = GetCollisionBSPData();
  120. for ( int i = 0; i < brushList.m_nFaceBrushCount; i++ )
  121. {
  122. pBSPData->map_brushes[ pBrushIndex[i] ].contents |= CONTENTS_BRUSH_PAINT;
  123. }
  124. }
  125. void CPaintmapDataManager::UpdatePaintmapTextures()
  126. {
  127. // Can't build lightmaps if the source data has been dumped
  128. CMatRenderContextPtr pRenderContext( materials );
  129. ICallQueue *pCallQueue = pRenderContext->GetCallQueue();
  130. if( pCallQueue )
  131. pCallQueue->QueueCall( materials, &IMaterialSystem::BeginUpdatePaintmaps );
  132. else
  133. materials->BeginUpdatePaintmaps();
  134. for( int paintmap=0; paintmap < m_iPaintmaps; ++paintmap )
  135. {
  136. PaintDirtyFlags_t nDirtyFlag = m_pPaintTextureDataArray[paintmap].GetDirtyFlag();
  137. if ( nDirtyFlag == PAINTMAP_CLEAN )
  138. continue;
  139. if ( nDirtyFlag == PAINTMAP_DIRTY_FULLRECT )
  140. {
  141. R_UpdatePaintmap( pCallQueue, paintmap, m_pPaintTextureDataArray[paintmap].GetPaintmapData(), 0, NULL );
  142. }
  143. else
  144. {
  145. CUtlVectorFixedGrowable<Rect_t, 1024> *pRects = m_pPaintTextureDataArray[paintmap].GetDirtyRectList();
  146. int count = pRects->Count();
  147. Assert( count > 0 );
  148. R_UpdatePaintmap( pCallQueue, paintmap, m_pPaintTextureDataArray[paintmap].GetPaintmapData(), count, pRects->Base() );
  149. }
  150. m_pPaintTextureDataArray[paintmap].RemoveDirty();
  151. }
  152. if( pCallQueue )
  153. pCallQueue->QueueCall( materials, &IMaterialSystem::EndUpdatePaintmaps );
  154. else
  155. materials->EndUpdatePaintmaps();
  156. }
  157. BYTE* CPaintmapDataManager::GetPaintmapData( int paintmap )
  158. {
  159. if ( paintmap >= 0 && paintmap < m_iPaintmaps )
  160. {
  161. return m_pPaintTextureDataArray[paintmap].GetPaintmapData();
  162. }
  163. return NULL;
  164. }
  165. void CPaintmapDataManager::GetPaintmapSize( int paintmap, int& width, int& height )
  166. {
  167. if ( paintmap >= 0 && paintmap < m_iPaintmaps )
  168. {
  169. m_pPaintTextureDataArray[paintmap].GetPaintSize( &width, &height );
  170. }
  171. }
  172. void CPaintmapDataManager::OnRestorePaintmaps()
  173. {
  174. for ( int i=0; i<m_iPaintmaps; ++i )
  175. {
  176. m_pPaintTextureDataArray[i].MarkAsDirty();
  177. }
  178. }
  179. void CPaintmapDataManager::RemoveAllPaint( void )
  180. {
  181. if( m_pPaintTextureDataArray )
  182. {
  183. for( int i = 0; i != m_iPaintmaps; ++i )
  184. {
  185. m_pPaintTextureDataArray[i].ClearTexture();
  186. }
  187. }
  188. int nBrushCount = GetCollisionBSPData()->numbrushes;
  189. cbrush_t *pBrush = GetCollisionBSPData()->map_brushes.Base();
  190. for ( int i = 0; i < nBrushCount; i++ )
  191. {
  192. pBrush[i].contents &= ~CONTENTS_BRUSH_PAINT;
  193. }
  194. }
  195. void CPaintmapDataManager::RemovePaint( const model_t *pModel )
  196. {
  197. Assert( pModel );
  198. if ( !pModel )
  199. return;
  200. Assert( m_pPaintTextureDataArray );
  201. if ( !m_pPaintTextureDataArray )
  202. return;
  203. BYTE noColor;
  204. SetColorBits( noColor, NO_POWER, 0.f );
  205. for ( int i=0; i<pModel->brush.nummodelsurfaces; ++i )
  206. {
  207. SurfaceHandle_t surfID = SurfaceHandleFromIndex( pModel->brush.firstmodelsurface + i );
  208. int lightmapID = materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID;
  209. CPaintTextureData& paintTexture = m_pPaintTextureDataArray[ lightmapID ];
  210. Rect_t rect;
  211. rect.x = MSurf_OffsetIntoLightmapPage( surfID )[0];
  212. rect.y = MSurf_OffsetIntoLightmapPage( surfID )[1];
  213. rect.width = MSurf_LightmapExtents( surfID )[0] + 1;
  214. rect.height= MSurf_LightmapExtents( surfID )[1] + 1;
  215. for ( int y=0; y<rect.height; ++y )
  216. {
  217. for ( int x=0; x<rect.width; ++x )
  218. {
  219. paintTexture.SetPixel( x + rect.x, y + rect.y, noColor );
  220. }
  221. }
  222. paintTexture.AddDirtyRect( rect );
  223. }
  224. }
  225. void CPaintmapDataManager::PaintAllSurfaces( BYTE color )
  226. {
  227. AssertMsg( m_pPaintTextureDataArray, "Failed to paint all surfaces. Paint textures are not allocated." );
  228. if( m_pPaintTextureDataArray )
  229. {
  230. for( int i = 0; i != m_iPaintmaps; ++i )
  231. {
  232. m_pPaintTextureDataArray[i].PaintAllSurfaces( color );
  233. }
  234. }
  235. int numSurfs = host_state.worldbrush->numsurfaces;
  236. for ( int i=0; i<numSurfs; ++i )
  237. {
  238. SurfaceHandle_t surfID = SurfaceHandleFromIndex( i );
  239. MSurf_Flags( surfID ) |= SURFDRAW_PAINTED;
  240. }
  241. int nBrushCount = GetCollisionBSPData()->numbrushes;
  242. cbrush_t *pBrush = GetCollisionBSPData()->map_brushes.Base();
  243. for ( int i = 0; i < nBrushCount; i++ )
  244. {
  245. pBrush[i].contents |= CONTENTS_BRUSH_PAINT;
  246. }
  247. }
  248. void CPaintmapDataManager::BeginPaintmapsDataAllocation( int iPaintmapCount )
  249. {
  250. DestroyPaintmapsData();
  251. m_iPaintmaps = iPaintmapCount;
  252. m_pPaintTextureDataArray = new CPaintTextureData[iPaintmapCount];
  253. }
  254. void CPaintmapDataManager::AllocatePaintmapData( int iPaintmapID, int iCorrespondingLightMapWidth, int iCorrespondingLightMapHeight )
  255. {
  256. m_pPaintTextureDataArray[iPaintmapID].Init( iCorrespondingLightMapWidth, iCorrespondingLightMapHeight, iPaintmapID );
  257. }
  258. CPaintTextureData::CPaintTextureData()
  259. {
  260. m_nPaintWidth = m_nPaintHeight = 0;
  261. m_backbuffer = NULL;
  262. m_nDirtyFlag = PAINTMAP_CLEAN;
  263. }
  264. // Initializes, shuts down the material
  265. bool CPaintTextureData::Init( int width, int height, int lightmapPageID )
  266. {
  267. m_nPaintWidth = width;
  268. m_nPaintHeight = height;
  269. m_lightmapPageID = lightmapPageID;
  270. m_backbuffer = new BYTE[ m_nPaintWidth * m_nPaintHeight ];
  271. AssertMsg( m_backbuffer, "Failed to allocate paint texture data array" );
  272. ClearBuffer();
  273. return true;
  274. }
  275. void CPaintTextureData::Destroy()
  276. {
  277. if ( m_backbuffer )
  278. {
  279. delete[] m_backbuffer;
  280. m_backbuffer = NULL;
  281. }
  282. }
  283. BYTE CPaintTextureData::GetPixel( int x, int y ) const
  284. {
  285. return m_backbuffer[ y * m_nPaintWidth + x ];
  286. }
  287. void CPaintTextureData::SetPixel( int x, int y, BYTE color )
  288. {
  289. m_backbuffer[ y * m_nPaintWidth + x ] = color;
  290. }
  291. bool CPaintTextureData::Paint( const PaintRect_t& paintRect )
  292. {
  293. uint32 nChangeFlags = DrawCircle( paintRect );
  294. if ( nChangeFlags & TEXEL_CHANGED )
  295. {
  296. // no need for extra alpha if we are erasing
  297. if ( paint_alpha_offset_enabled.GetBool() && paintRect.colorIndex != NO_POWER )
  298. {
  299. Rect_t rect = paintRect.rect;
  300. int sOffset = MSurf_OffsetIntoLightmapPage( paintRect.surfID )[0];
  301. int tOffset = MSurf_OffsetIntoLightmapPage( paintRect.surfID )[1];
  302. int sMax = ( MSurf_LightmapExtents( paintRect.surfID )[0] );
  303. int tMax = ( MSurf_LightmapExtents( paintRect.surfID )[1] );
  304. if ( paintRect.rect.x > sOffset )
  305. {
  306. --rect.x;
  307. ++rect.width;
  308. }
  309. if ( paintRect.rect.y > tOffset )
  310. {
  311. --rect.y;
  312. ++rect.height;
  313. }
  314. if ( paintRect.rect.x + paintRect.rect.width - 1 < sOffset + sMax )
  315. {
  316. ++rect.width;
  317. }
  318. if ( paintRect.rect.y + paintRect.rect.height - 1 < tOffset + tMax )
  319. {
  320. ++rect.height;
  321. }
  322. AddDirtyRect( rect );
  323. }
  324. else
  325. {
  326. AddDirtyRect( paintRect.rect );
  327. }
  328. }
  329. return ( nChangeFlags & PAINT_POWER_CHANGED ) != 0;
  330. }
  331. BYTE BlendColor( BYTE colorIndex, BYTE nPrePixel, float flAlpha, float flPaintCoatPercent, float flMaxAlpha )
  332. {
  333. BYTE finalColor;
  334. // stomp erase color
  335. if ( colorIndex == NO_POWER )
  336. {
  337. // if it's erase, the alpha is always 0.f or 1.f (0.f for surrounding edges)
  338. SetColorBits( finalColor, paint_alpha_offset_enabled.GetBool() ? GetColorIndex( nPrePixel ) : colorIndex, 0.f );
  339. }
  340. else
  341. {
  342. float bAlpha = GetAlpha( nPrePixel );
  343. float flNewAlpha = clamp( bAlpha + ( flPaintCoatPercent * flAlpha ), 0.f, flMaxAlpha );
  344. SetColorBits( finalColor, colorIndex, flNewAlpha );
  345. }
  346. return finalColor;
  347. }
  348. float ComputeCircleAlpha( const PaintRect_t& paintRect, int x, int y )
  349. {
  350. float flPixelDist = Vector2D( x - paintRect.uvCenter.x, y - paintRect.uvCenter.y ).Length();
  351. float flRadiusRatio = clamp( paintRect.flCenterAlpha + flPixelDist / paintRect.flCircleRadius, 0.f, 1.f );
  352. float flAlpha = 1.f - Bias( flRadiusRatio, paintsplat_bias.GetFloat() );
  353. if ( paintsplat_noise_enabled.GetBool() )
  354. {
  355. float flNoise = paintsplat_max_alpha_noise.GetFloat();
  356. flAlpha += RandomFloat( -flNoise, flNoise );
  357. flAlpha = clamp( flAlpha, 0.f, 1.f );
  358. }
  359. return flAlpha;
  360. }
  361. uint32 CPaintTextureData::BlendLuxel( const PaintRect_t& paintRect, int x, int y, float flNewAlpha, float flMaxAlpha /*= 1.f*/ )
  362. {
  363. uint32 nChangeFlags = 0;
  364. BYTE nPrePixel = GetPixel( x, y );
  365. BYTE nPostPixel = BlendColor( paintRect.colorIndex, nPrePixel, flNewAlpha, paintRect.flPaintCoatPercent, flMaxAlpha );
  366. if ( nPrePixel != nPostPixel )
  367. {
  368. SetPixel( x, y, nPostPixel );
  369. nChangeFlags |= TEXEL_CHANGED;
  370. if ( GetColorIndex( nPrePixel ) != GetColorIndex( nPostPixel ) )
  371. {
  372. nChangeFlags |= PAINT_POWER_CHANGED;
  373. }
  374. }
  375. return nChangeFlags;
  376. }
  377. uint32 CPaintTextureData::AddSurroundingAlpha( const PaintRect_t& paintRect, int x, int y )
  378. {
  379. SurfaceHandle_t surfID = paintRect.surfID;
  380. int surroundCase = 0;
  381. int sOffset = MSurf_OffsetIntoLightmapPage( surfID )[0];
  382. int tOffset = MSurf_OffsetIntoLightmapPage( surfID )[1];
  383. int sMax = ( MSurf_LightmapExtents( surfID )[0] );
  384. int tMax = ( MSurf_LightmapExtents( surfID )[1] );
  385. surroundCase |= ( x > sOffset ) ? 1 : 0; // left
  386. surroundCase |= ( x < sOffset + sMax ) ? 2 : 0; // right
  387. surroundCase |= ( y > tOffset ) ? 4 : 0; // up
  388. surroundCase |= ( y < tOffset + tMax ) ? 8 : 0; // down
  389. uint32 nChangeFlags = 0;
  390. switch ( surroundCase )
  391. {
  392. case 1: // left only
  393. {
  394. nChangeFlags |= BlendLuxel( paintRect, x - 1, y, 0.f );
  395. break;
  396. }
  397. case 2: // right only
  398. {
  399. nChangeFlags |= BlendLuxel( paintRect, x + 1, y, 0.f );
  400. break;
  401. }
  402. case 3: // left right
  403. {
  404. nChangeFlags |= BlendLuxel( paintRect, x - 1, y, 0.f );
  405. nChangeFlags |= BlendLuxel( paintRect, x + 1, y, 0.f );
  406. break;
  407. }
  408. case 4: // up only
  409. {
  410. nChangeFlags |= BlendLuxel( paintRect, x, y - 1, 0.f );
  411. break;
  412. }
  413. case 5: // left up
  414. {
  415. nChangeFlags |= BlendLuxel( paintRect, x - 1, y, 0.f );
  416. nChangeFlags |= BlendLuxel( paintRect, x - 1, y - 1, 0.f );
  417. nChangeFlags |= BlendLuxel( paintRect, x, y - 1, 0.f );
  418. break;
  419. }
  420. case 6: // right up
  421. {
  422. nChangeFlags |= BlendLuxel( paintRect, x + 1, y, 0.f );
  423. nChangeFlags |= BlendLuxel( paintRect, x + 1, y - 1, 0.f );
  424. nChangeFlags |= BlendLuxel( paintRect, x, y - 1, 0.f );
  425. break;
  426. }
  427. case 7: // left up right
  428. {
  429. nChangeFlags |= BlendLuxel( paintRect, x - 1, y, 0.f );
  430. nChangeFlags |= BlendLuxel( paintRect, x - 1, y - 1, 0.f );
  431. nChangeFlags |= BlendLuxel( paintRect, x, y - 1, 0.f );
  432. nChangeFlags |= BlendLuxel( paintRect, x + 1, y - 1, 0.f );
  433. nChangeFlags |= BlendLuxel( paintRect, x + 1, y, 0.f );
  434. break;
  435. }
  436. case 8: // down only
  437. {
  438. nChangeFlags |= BlendLuxel( paintRect, x, y + 1, 0.f );
  439. break;
  440. }
  441. case 9: // left down
  442. {
  443. nChangeFlags |= BlendLuxel( paintRect, x - 1, y, 0.f );
  444. nChangeFlags |= BlendLuxel( paintRect, x - 1, y + 1, 0.f );
  445. nChangeFlags |= BlendLuxel( paintRect, x, y + 1, 0.f );
  446. break;
  447. }
  448. case 10: // right down
  449. {
  450. nChangeFlags |= BlendLuxel( paintRect, x + 1, y, 0.f );
  451. nChangeFlags |= BlendLuxel( paintRect, x + 1, y + 1, 0.f );
  452. nChangeFlags |= BlendLuxel( paintRect, x, y + 1, 0.f );
  453. break;
  454. }
  455. case 11: // left down right
  456. {
  457. nChangeFlags |= BlendLuxel( paintRect, x - 1, y, 0.f );
  458. nChangeFlags |= BlendLuxel( paintRect, x - 1, y + 1, 0.f );
  459. nChangeFlags |= BlendLuxel( paintRect, x, y + 1, 0.f );
  460. nChangeFlags |= BlendLuxel( paintRect, x + 1, y + 1, 0.f );
  461. nChangeFlags |= BlendLuxel( paintRect, x + 1, y, 0.f );
  462. break;
  463. }
  464. case 12: // up down
  465. {
  466. nChangeFlags |= BlendLuxel( paintRect, x, y - 1, 0.f );
  467. nChangeFlags |= BlendLuxel( paintRect, x, y + 1, 0.f );
  468. break;
  469. }
  470. case 13: // up left down
  471. {
  472. nChangeFlags |= BlendLuxel( paintRect, x, y - 1, 0.f );
  473. nChangeFlags |= BlendLuxel( paintRect, x - 1, y - 1, 0.f );
  474. nChangeFlags |= BlendLuxel( paintRect, x - 1, y, 0.f );
  475. nChangeFlags |= BlendLuxel( paintRect, x - 1, y + 1, 0.f );
  476. nChangeFlags |= BlendLuxel( paintRect, x, y + 1, 0.f );
  477. break;
  478. }
  479. case 14: // up right down
  480. {
  481. nChangeFlags |= BlendLuxel( paintRect, x, y - 1, 0.f );
  482. nChangeFlags |= BlendLuxel( paintRect, x + 1, y - 1, 0.f );
  483. nChangeFlags |= BlendLuxel( paintRect, x + 1, y, 0.f );
  484. nChangeFlags |= BlendLuxel( paintRect, x + 1, y + 1, 0.f );
  485. nChangeFlags |= BlendLuxel( paintRect, x, y + 1, 0.f );
  486. break;
  487. }
  488. case 15: // all 8 surrounding luxels
  489. {
  490. nChangeFlags |= BlendLuxel( paintRect, x, y - 1, 0.f );
  491. nChangeFlags |= BlendLuxel( paintRect, x - 1, y - 1, 0.f );
  492. nChangeFlags |= BlendLuxel( paintRect, x - 1, y, 0.f );
  493. nChangeFlags |= BlendLuxel( paintRect, x - 1, y + 1, 0.f );
  494. nChangeFlags |= BlendLuxel( paintRect, x + 1, y - 1, 0.f );
  495. nChangeFlags |= BlendLuxel( paintRect, x + 1, y, 0.f );
  496. nChangeFlags |= BlendLuxel( paintRect, x + 1, y + 1, 0.f );
  497. nChangeFlags |= BlendLuxel( paintRect, x, y + 1, 0.f );
  498. break;
  499. }
  500. default:
  501. break;
  502. }
  503. return nChangeFlags;
  504. }
  505. uint32 CPaintTextureData::DrawLine( const PaintRect_t& paintRect, int x1, int x2, int y )
  506. {
  507. uint32 nChangeFlags = 0;
  508. int sOffset = MSurf_OffsetIntoLightmapPage( paintRect.surfID )[0];
  509. int tOffset = MSurf_OffsetIntoLightmapPage( paintRect.surfID )[1];
  510. int sMax = ( MSurf_LightmapExtents( paintRect.surfID )[0] );
  511. int tMax = ( MSurf_LightmapExtents( paintRect.surfID )[1] );
  512. int start = x1;
  513. int end = x2;
  514. // clamp border luxel alpha below paint_max_surface_border_alpha threshold so the shader won't look up color from other surface border
  515. float flMaxAlpha = 1.f;
  516. if ( y == tOffset || y == tOffset + tMax )
  517. {
  518. flMaxAlpha = paint_max_surface_border_alpha.GetFloat();
  519. }
  520. else
  521. {
  522. if ( x1 == sOffset )
  523. {
  524. float flAlpha = ( paintRect.colorIndex == NO_POWER ) ? 1.f : ComputeCircleAlpha( paintRect, x1, y );
  525. nChangeFlags |= BlendLuxel( paintRect, x1, y, flAlpha, paint_max_surface_border_alpha.GetFloat() );
  526. ++start;
  527. }
  528. if ( x2 == sOffset + sMax )
  529. {
  530. float flAlpha = ( paintRect.colorIndex == NO_POWER ) ? 1.f : ComputeCircleAlpha( paintRect, x2, y );
  531. nChangeFlags |= BlendLuxel( paintRect, x2, y, flAlpha, paint_max_surface_border_alpha.GetFloat() );
  532. --end;
  533. }
  534. }
  535. for ( int x = start; x <= end; ++x )
  536. {
  537. float flAlpha = ( paintRect.colorIndex == NO_POWER ) ? 1.f : ComputeCircleAlpha( paintRect, x, y );
  538. nChangeFlags |= BlendLuxel( paintRect, x, y, flAlpha, flMaxAlpha );
  539. }
  540. // don't add surrounding alpha if we are erasing
  541. if ( paint_alpha_offset_enabled.GetBool() && paintRect.colorIndex != NO_POWER )
  542. {
  543. if ( x1 == x2 )
  544. {
  545. nChangeFlags |= AddSurroundingAlpha( paintRect, x1, y );
  546. }
  547. else
  548. {
  549. nChangeFlags |= AddSurroundingAlpha( paintRect, x1, y );
  550. nChangeFlags |= AddSurroundingAlpha( paintRect, x2, y );
  551. }
  552. }
  553. return nChangeFlags;
  554. }
  555. uint32 CPaintTextureData::Draw2Lines( const PaintRect_t& paintRect, float x, float y )
  556. {
  557. const Rect_t& rect = paintRect.rect;
  558. int minX = rect.x;
  559. int minY = rect.y;
  560. int maxX = rect.x + rect.width - 1;
  561. int maxY = rect.y + rect.height - 1;
  562. const Vector2D& uvCenter = paintRect.uvCenter;
  563. int x1 = MAX( ( int )( uvCenter.x - x - 0.5f ), minX );
  564. int y1 = MAX( ( int )( uvCenter.y - y - 0.5f ), minY );
  565. int x2 = MIN( ( int )( uvCenter.x + x + 0.5f ), maxX );
  566. int y2 = MIN( ( int )( uvCenter.y + y + 0.5f ), maxY );
  567. // if line is outside the rect, don't do anything
  568. if ( x1 > maxX || x2 < minX )
  569. return 0;
  570. uint32 nChangeFlags = 0;
  571. // draw line1
  572. if ( minY <= y1 && y1 <= maxY )
  573. {
  574. nChangeFlags |= DrawLine( paintRect, x1, x2, y1 );
  575. }
  576. if ( y1 != y2 )
  577. {
  578. if ( minY <= y2 && y2 <= maxY )
  579. {
  580. nChangeFlags |= DrawLine( paintRect, x1, x2, y2 );
  581. }
  582. }
  583. return nChangeFlags;
  584. }
  585. uint32 CPaintTextureData::Draw4Lines( const PaintRect_t& paintRect, float x, float y )
  586. {
  587. uint32 nChangeFlags = 0;
  588. nChangeFlags |= Draw2Lines( paintRect, x, y );
  589. if ( x != y )
  590. {
  591. nChangeFlags |= Draw2Lines( paintRect, y, x );
  592. }
  593. return nChangeFlags;
  594. }
  595. uint32 CPaintTextureData::DrawCircle( const PaintRect_t& paintRect )
  596. {
  597. // assume uvExtents x and y are about the same
  598. float radius = paintRect.flCircleRadius;
  599. float error = -radius;
  600. float x = radius;
  601. float y = 0;
  602. uint32 nChangeFlags = 0;
  603. while (x >= y)
  604. {
  605. nChangeFlags |= Draw4Lines( paintRect, x, y );
  606. error += y;
  607. ++y;
  608. error += y;
  609. if (error >= 0)
  610. {
  611. --x;
  612. error -= x;
  613. error -= x;
  614. }
  615. }
  616. return nChangeFlags;
  617. }
  618. // Returns the tecxoord range
  619. void CPaintTextureData::GetTexCoordRange( float *pMaxU, float *pMaxV )
  620. {
  621. *pMaxU = *pMaxV = 1.0f;
  622. return;
  623. }
  624. // Returns the size of the paint texture (stored in a subrect of the material itself)
  625. void CPaintTextureData::GetPaintSize( int *pWidth, int *pHeight )
  626. {
  627. *pWidth = m_nPaintWidth;
  628. *pHeight = m_nPaintHeight;
  629. }
  630. void CPaintTextureData::ClearTexture()
  631. {
  632. ClearBuffer();
  633. MarkAsDirty();
  634. }
  635. void CPaintTextureData::GetPixels( const Rect_t& splatRect, CUtlVector<BYTE>& surfColors )
  636. {
  637. //sample color inside rect
  638. for (int y = 0; y < splatRect.height; ++y )
  639. {
  640. for (int x = 0; x < splatRect.width; ++x )
  641. {
  642. BYTE packedColor = GetPixel( splatRect.x + x, splatRect.y + y );
  643. BYTE colorIndex = GetColorIndex( packedColor );
  644. float flAlpha = GetAlpha( packedColor );
  645. if( flAlpha > paint_min_valid_alpha_value.GetFloat() )
  646. surfColors.AddToTail( colorIndex );
  647. if( debug_paint_alpha.GetBool() )
  648. {
  649. Con_NPrintf( y * splatRect.width + x, "(%d, %d), Alpha: %f\n", x, y, flAlpha );
  650. }
  651. }
  652. }
  653. }
  654. void CPaintTextureData::ClearBuffer( BYTE *pByte )
  655. {
  656. BYTE nPixel = ( pByte ) ? *pByte : NO_POWER << NUM_ALPHA_BITS;
  657. V_memset( m_backbuffer, nPixel, m_nPaintWidth * m_nPaintHeight );
  658. }
  659. // paint all surfaces
  660. void CPaintTextureData::PaintAllSurfaces( BYTE colorIndex )
  661. {
  662. BYTE nPixel;
  663. SetColorBits( nPixel, colorIndex, 1.f );
  664. ClearBuffer( &nPixel );
  665. MarkAsDirty();
  666. }
  667. void CPaintTextureData::GetSurfacePaintData( SurfaceHandle_t surfID, CUtlVector< BYTE > &data ) const
  668. {
  669. if ( !m_backbuffer )
  670. return;
  671. short x = MSurf_OffsetIntoLightmapPage( surfID )[0];
  672. short y = MSurf_OffsetIntoLightmapPage( surfID )[1];
  673. short width = MSurf_LightmapExtents( surfID )[0] + 1;
  674. short height = MSurf_LightmapExtents( surfID )[1] + 1;
  675. int nDataCount = width * height;
  676. // make sure alloc number is divisible by 4
  677. uint nAllocSize = nDataCount + ( ( 4 - ( nDataCount % 4 ) ) % 4 );
  678. Assert( ( nAllocSize % 4 ) == 0 );
  679. data.EnsureCount( nAllocSize );
  680. BYTE* pOut = data.Base();
  681. const BYTE* pEnd = data.Base() + nDataCount;
  682. const BYTE* pRead = &m_backbuffer[ y * m_nPaintWidth + x ];
  683. while( pOut < pEnd )
  684. {
  685. V_memcpy( pOut, pRead, width );
  686. pOut += width;
  687. pRead += m_nPaintWidth;
  688. }
  689. Assert( pOut == pEnd );
  690. }
  691. void CPaintTextureData::SetSurfacePaintData( SurfaceHandle_t surfID, const CUtlVector< BYTE > &data )
  692. {
  693. if ( !m_backbuffer )
  694. return;
  695. Rect_t rect;
  696. rect.x = MSurf_OffsetIntoLightmapPage( surfID )[0];
  697. rect.y = MSurf_OffsetIntoLightmapPage( surfID )[1];
  698. rect.width = MSurf_LightmapExtents( surfID )[0] + 1;
  699. rect.height = MSurf_LightmapExtents( surfID )[1] + 1;
  700. uint nDataCount = rect.width * rect.height;
  701. const BYTE* pRead = data.Base();
  702. const BYTE* pEnd = data.Base() + nDataCount;
  703. BYTE *pSet = &m_backbuffer[ rect.y * m_nPaintWidth + rect.x ];
  704. while ( pRead < pEnd )
  705. {
  706. V_memcpy( pSet, pRead, rect.width );
  707. pRead += rect.width;
  708. pSet += m_nPaintWidth;
  709. }
  710. AddDirtyRect( rect );
  711. // mark surf as painted
  712. MSurf_Flags( surfID ) |= SURFDRAW_PAINTED;
  713. MarkSurfaceBrushes( MSurf_Index(surfID) );
  714. }
  715. void EncodeDataRLE( const uint32* pBuffer, uint32 nDwordCount, CUtlVector< uint32 > &data )
  716. {
  717. if ( nDwordCount == 0 )
  718. return;
  719. // first build a list of runs into a local utlvector
  720. CUtlVectorFixedGrowable<intp, 1024> outList;
  721. // this is the start of the run of unique data to copy
  722. const uint32 *pCopyStart = pBuffer;
  723. // we need to find at least a run of 3 in order for encoding a run to pay for itself, keep last two
  724. uint32 nSymbol0 = *pCopyStart;
  725. uint32 nSymbol1 = pCopyStart[1];
  726. const uint32 *pEndOfData = pCopyStart + nDwordCount;
  727. // start here with the first two loaded up and ready to copy
  728. const uint32 *pCurrent = pCopyStart + 2;
  729. // this is the size in dwords of the rle output buffer
  730. uint32 nRLESize = 0;
  731. // anything that defines _GAMECONSOLE should support prefetches
  732. #if defined(_GAMECONSOLE)
  733. // prefetch the first 7 cache lines
  734. for ( int i = 0; i < 7; i++ )
  735. {
  736. PREFETCH_128( pCurrent + (i * 32), 0 );
  737. }
  738. #endif
  739. // this is the next read that will trigger a prefetch (not every read should prefetch)
  740. const uint32 *pNextPrefetch = pCurrent + 32;
  741. // this is the end of the space we'd want to prefetch ahead of
  742. const uint32 *pLastPrefetch = pEndOfData - (8 * 32);
  743. // this is how far ahead of the current read to prefetch
  744. #if defined(_GAMECONSOLE)
  745. const uint32 nPrefetchOffsetDwords = 7 * 32; // will cause an error if your platform has prefetch but not _GAMECONSOLE
  746. #endif
  747. while ( pCurrent < pEndOfData )
  748. {
  749. uint32 nSymbol2 = *pCurrent;
  750. if ( nSymbol0 == nSymbol1 && nSymbol1 == nSymbol2 )
  751. {
  752. // found a run
  753. const uint32 *pRunStart = pCurrent - 2;
  754. int nCopyOut = pRunStart - pCopyStart;
  755. // copy any previous run of non-similar values
  756. if ( nCopyOut )
  757. {
  758. // note, we're building a list of copies, so just add a record
  759. outList.AddToTail( nCopyOut );
  760. outList.AddToTail( intp(pCopyStart) );
  761. nRLESize += nCopyOut + 1;
  762. pCopyStart = pRunStart;
  763. }
  764. // follow current run until its end
  765. pCurrent++;
  766. while ( pCurrent < pEndOfData && *pCurrent == nSymbol0 )
  767. {
  768. pCurrent++;
  769. if ( pCurrent >= pNextPrefetch && pNextPrefetch < pLastPrefetch )
  770. {
  771. // schedule prefetches 128 bytes (32 dwords) apart
  772. PREFETCH_128( pNextPrefetch + nPrefetchOffsetDwords, 0 );
  773. pNextPrefetch += 32;
  774. }
  775. }
  776. // write out the run
  777. int nRunCount = pCurrent - pRunStart;
  778. outList.AddToTail( -nRunCount );
  779. outList.AddToTail( nSymbol0 );
  780. nRLESize += 2;
  781. if ( pCurrent < pEndOfData )
  782. {
  783. // restart run detection
  784. nSymbol2 = *pCurrent;
  785. nSymbol1 = nSymbol2+1; // make these not equal so a run will not be detected
  786. }
  787. pCopyStart = pCurrent;
  788. }
  789. // advance read cursor and shift over previous run-detect state
  790. pCurrent++;
  791. nSymbol0 = nSymbol1;
  792. nSymbol1 = nSymbol2;
  793. if ( pCurrent >= pNextPrefetch && pNextPrefetch < pLastPrefetch )
  794. {
  795. PREFETCH_128( pNextPrefetch + nPrefetchOffsetDwords, 0 );
  796. pNextPrefetch += 32;
  797. }
  798. }
  799. int nCopyOut = pEndOfData - pCopyStart;
  800. if ( nCopyOut )
  801. {
  802. // add another record if the map ends in a copy
  803. outList.AddToTail( nCopyOut );
  804. outList.AddToTail( (intp)pCopyStart );
  805. nRLESize += nCopyOut + 1;
  806. pCopyStart = pEndOfData;
  807. }
  808. // now build the full RLE output from the records we just made
  809. // we can allocate once because we know the output size
  810. data.SetCount( nRLESize );
  811. uint32 *pOut = data.Base();
  812. const intp *pRLE = outList.Base();
  813. const intp *pEndRLE = pRLE + outList.Count();
  814. while ( pRLE < pEndRLE )
  815. {
  816. int nCount = *pRLE++;
  817. if ( nCount < 0 )
  818. {
  819. // run, insert in output
  820. *pOut++ = uint32( nCount );
  821. *pOut++ = uint32( *pRLE++ );
  822. }
  823. else
  824. {
  825. // copy, do the actual copy now
  826. *pOut++ = uint32( nCount );
  827. const uint32 *pSource = (const uint32 *)*pRLE++;
  828. for ( int i = 0; i < nCount; i++ )
  829. {
  830. *pOut++ = *pSource++;
  831. }
  832. }
  833. }
  834. // make sure our buffer contains exactly what the records predicted
  835. Assert( (uint32)(pOut - data.Base()) == nRLESize );
  836. }
  837. void CPaintmapDataManager::GetPaintmapDataRLE( CUtlVector< uint32 > &data ) const
  838. {
  839. for ( int i=0; i< host_state.worldbrush->numsurfaces; ++i )
  840. {
  841. SurfaceHandle_t surfID = SurfaceHandleFromIndex( i );
  842. if ( MSurf_Flags( surfID ) & SURFDRAW_PAINTED )
  843. {
  844. int lightmapID = materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID;
  845. // get paint data from a surface
  846. CUtlVector< BYTE > surfPaintData;
  847. m_pPaintTextureDataArray[lightmapID].GetSurfacePaintData( surfID, surfPaintData );
  848. // encode into RLE format
  849. CUtlVector< uint32 > rleData;
  850. uint nDwordCount = surfPaintData.Count() / 4;
  851. EncodeDataRLE( (uint32*)surfPaintData.Base(), nDwordCount, rleData );
  852. // write surface index, RLE size, and RLE data to output data
  853. data.AddToTail( i );
  854. data.AddToTail( rleData.Count() );
  855. data.AddMultipleToTail( rleData.Count(), rleData.Base() );
  856. }
  857. }
  858. }
  859. void DecodeDataRLE( const uint32* pRLEStart, uint rleCount, SurfaceHandle_t surfID, uint32* pOutput, uint nDwordCount )
  860. {
  861. // NOTE: This assumes the size is always correct, will assert if not
  862. const uint32 *pRLE = pRLEStart;
  863. const uint32 *pEndOfData = pRLE + rleCount;
  864. #if _DEBUG
  865. const uint32* pOutStart = pOutput;
  866. #endif
  867. while ( pRLE < pEndOfData )
  868. {
  869. int32 nCount = int32(*pRLE++);
  870. if ( nCount < 0 ) // run, output repeated value nCount times
  871. {
  872. nCount = -nCount;
  873. uint32 nRunVal = *pRLE++;
  874. for ( int i = 0; i < nCount; i++ )
  875. {
  876. *pOutput++ = nRunVal;
  877. }
  878. }
  879. else // copy raw dwords from input
  880. {
  881. for ( int i = 0; i < nCount; i++ )
  882. {
  883. *pOutput++ = *pRLE++;
  884. }
  885. }
  886. }
  887. Assert( pRLE == pEndOfData );
  888. Assert( pOutput - pOutStart == nDwordCount );
  889. }
  890. void CPaintmapDataManager::LoadPaintmapDataRLE( const CUtlVector< uint32 > &data )
  891. {
  892. const uint32* pCurrent = data.Base();
  893. const uint32* pEnd = pCurrent + data.Count();
  894. while ( pCurrent < pEnd )
  895. {
  896. // assumes that the surface index is always the same (it'll change if the map's changed)
  897. int surfIndex = *pCurrent++;
  898. SurfaceHandle_t surfID = SurfaceHandleFromIndex( surfIndex );
  899. int lightmapID = materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID;
  900. uint nRLESize = *pCurrent++;
  901. // alloc output. make sure the size is divisible by 4 to copy data from array of uint32
  902. CUtlVector< BYTE > rawPaintData;
  903. int width = MSurf_LightmapExtents( surfID )[0] + 1;
  904. int height = MSurf_LightmapExtents( surfID )[1] + 1;
  905. int nByteCount = width * height;
  906. uint nAllocSize = nByteCount + ( ( 4 - ( nByteCount % 4 ) ) % 4 );
  907. Assert( ( nAllocSize % 4 ) == 0 );
  908. rawPaintData.SetCount( nAllocSize );
  909. uint nDWord = nAllocSize / 4;
  910. DecodeDataRLE( pCurrent, nRLESize, surfID, (uint32*)rawPaintData.Base(), nDWord );
  911. pCurrent += nRLESize;
  912. m_pPaintTextureDataArray[lightmapID].SetSurfacePaintData( surfID, rawPaintData );
  913. }
  914. }
  915. void CPaintTextureData::AddDirtyRect( const Rect_t& rect )
  916. {
  917. MarkAsDirty( PAINTMAP_DIRTY_SUBRECT );
  918. m_dirtyRects.AddToTail( rect );
  919. }
  920. CUtlVectorFixedGrowable<Rect_t, 1024>* CPaintTextureData::GetDirtyRectList()
  921. {
  922. return &m_dirtyRects;
  923. }
  924. void CPaintTextureData::RemoveDirty()
  925. {
  926. m_nDirtyFlag = PAINTMAP_CLEAN;
  927. m_dirtyRects.RemoveAll();
  928. }
  929. PaintDirtyFlags_t CPaintTextureData::GetDirtyFlag() const
  930. {
  931. return m_nDirtyFlag;
  932. }
  933. void CPaintTextureData::MarkAsDirty( PaintDirtyFlags_t nDirtyFlag /*= PAINTMAP_DIRTY_FULLRECT*/ )
  934. {
  935. m_nDirtyFlag = ( m_nDirtyFlag == PAINTMAP_DIRTY_FULLRECT ) ? m_nDirtyFlag : nDirtyFlag;
  936. }
  937. void ProjectPointOntoSurfaceTexture( const SurfaceCtx_t& ctx, SurfaceHandle_t surfID, const Vector& vPoint, Vector2D& uv )
  938. {
  939. mtexinfo_t* pTexInfo = MSurf_TexInfo( surfID );
  940. uv.x = DotProduct ( vPoint, pTexInfo->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D()) +
  941. pTexInfo->lightmapVecsLuxelsPerWorldUnits[0][3];
  942. uv.x -= MSurf_LightmapMins( surfID )[0];
  943. uv.x += 0.5f;
  944. uv.y = DotProduct ( vPoint, pTexInfo->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D()) +
  945. pTexInfo->lightmapVecsLuxelsPerWorldUnits[1][3];
  946. uv.y -= MSurf_LightmapMins( surfID )[1];
  947. uv.y += 0.5f;
  948. uv *= ctx.m_Scale;
  949. uv += ctx.m_Offset;
  950. // convert back to the old space
  951. uv /= ctx.m_Scale;
  952. }
  953. bool ComputePaintRect( SurfaceHandle_t surfID, const Vector &vPosition, float flSphereRadius, PaintRect_t& paintRect )
  954. {
  955. #if !defined( LINUX )
  956. // find dist from plane
  957. VPlane forwardFacingPlane = MSurf_GetForwardFacingPlane( surfID );
  958. float distFromPlane = forwardFacingPlane.DistTo( vPosition );
  959. AssertMsg( distFromPlane < flSphereRadius, "How did this surface intersect with the query sphere?" );
  960. float circleRadius = FastSqrt( flSphereRadius * flSphereRadius - distFromPlane * distFromPlane );
  961. SurfaceCtx_t ctx;
  962. SurfSetupSurfaceContext( ctx, surfID );
  963. Vector2D uvCenter, uvExtents;
  964. {
  965. ProjectPointOntoSurfaceTexture( ctx, surfID, vPosition, uvCenter );
  966. mtexinfo_t *pTexInfo = MSurf_TexInfo( surfID );
  967. uvExtents.x = circleRadius * pTexInfo->lightmapVecsLuxelsPerWorldUnits[0].AsVector3D().Length();
  968. uvExtents.y = circleRadius * pTexInfo->lightmapVecsLuxelsPerWorldUnits[1].AsVector3D().Length();
  969. }
  970. Assert( uvExtents.x > 0 && uvExtents.y > 0 );
  971. Vector2D uvMins, uvMaxs;
  972. uvMins = uvCenter - uvExtents;
  973. uvMaxs = uvCenter + uvExtents;
  974. int sOffset = MSurf_OffsetIntoLightmapPage( surfID )[0];
  975. int tOffset = MSurf_OffsetIntoLightmapPage( surfID )[1];
  976. int sMax = ( MSurf_LightmapExtents( surfID )[0] );
  977. int tMax = ( MSurf_LightmapExtents( surfID )[1] );
  978. if ( ( sOffset <= uvMaxs.x && uvMins.x <= sOffset + sMax ) && ( tOffset <= uvMaxs.y && uvMins.y <= tOffset + tMax ) )
  979. {
  980. // init paintRect
  981. float flRoundedCircleRadius = floor( fpmax( uvExtents.x, uvExtents.y ) + 0.5f );
  982. int surfWidth = sMax + 1;
  983. int surfHeight = tMax + 1;
  984. paintRect.flCenterAlpha = distFromPlane / flSphereRadius;
  985. paintRect.flCircleRadius = flRoundedCircleRadius;
  986. paintRect.uvCenter = uvCenter;
  987. paintRect.surfID = surfID;
  988. int startX = MAX( ( int )( uvCenter.x - flRoundedCircleRadius - 0.5f ), sOffset );
  989. int startY = MAX( ( int )( uvCenter.y - flRoundedCircleRadius - 0.5f ), tOffset );
  990. int endX = MIN( ( int )( uvCenter.x + flRoundedCircleRadius + 0.5f ), sOffset + surfWidth );
  991. int endY = MIN( ( int )( uvCenter.y + flRoundedCircleRadius + 0.5f ), tOffset + surfHeight );
  992. Rect_t rect;
  993. rect.x = startX;
  994. rect.y = startY;
  995. rect.width = endX - startX;
  996. rect.height = endY - startY;
  997. paintRect.rect = rect;
  998. return ( rect.width > 0 && rect.height > 0 );
  999. }
  1000. #endif
  1001. return false;
  1002. }
  1003. bool R_PaintSurface( SurfaceHandle_t surfID, const Vector &vPosition, float flSphereRadius, const VPlane& basePlane, BYTE colorIndex, float flPaintCoatPercent )
  1004. {
  1005. bool bAddedPaint = false;
  1006. int lightmapID = materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID;
  1007. PaintRect_t paintRect;
  1008. paintRect.colorIndex = colorIndex;
  1009. paintRect.flPaintCoatPercent = flPaintCoatPercent;
  1010. if ( ComputePaintRect( surfID, vPosition, flSphereRadius, paintRect ) )
  1011. {
  1012. if ( g_PaintManager.m_pPaintTextureDataArray[ lightmapID ].Paint( paintRect ) )
  1013. {
  1014. // HACK SUPER HACK! mark surface as painted to optimize rendering
  1015. MSurf_Flags( surfID ) |= SURFDRAW_PAINTED;
  1016. MarkSurfaceBrushes( MSurf_Index(surfID) );
  1017. bAddedPaint = true;
  1018. }
  1019. }
  1020. return bAddedPaint;
  1021. }
  1022. //-----------------------------------------------------------------------------
  1023. // find a surface to paint by traversing through all surfaces in a model
  1024. //-----------------------------------------------------------------------------
  1025. struct paintinfo_t
  1026. {
  1027. Vector m_vPosition;
  1028. VPlane m_plane; // closest plane to the sphere
  1029. worldbrushdata_t *m_pBrush; // The shared brush data for this model
  1030. float m_flSize; // radius of surf searching sphere
  1031. float m_flCurrentDistance; // distance away from the center of the sphere
  1032. CUtlVector<SurfaceHandle_t> m_aApplySurfs;
  1033. // debug stuff
  1034. bool m_bPainting;
  1035. };
  1036. Vector FindClosestPointToTriangle( const Vector& p, const Vector& a, const Vector& b, const Vector& c )
  1037. {
  1038. Vector ab = b - a;
  1039. Vector ac = c - a;
  1040. Vector bc = c - b;
  1041. // project p onto ab,
  1042. // p' = a + projAB * ab, projAB = projABnom/(projABnom+projABdenom)
  1043. float projABnom = DotProduct( p - a, ab ), projABdenom = DotProduct( p - b, a - b );
  1044. // project p onto ac,
  1045. // p' = a + projAC * ac, = projACnom/(projACnom+projACdenom)
  1046. float projACnom = DotProduct( p - a, ac ), projACdenom = DotProduct( p - c, a - c );
  1047. if ( projABnom <= 0.0f && projACnom <= 0.0f )
  1048. {
  1049. return a; // Vertex region early out
  1050. }
  1051. // project p onto bc,
  1052. // p' = b + projBC * bc, projBC = projBCnom/(projBCnom+projBCdenom)
  1053. float projBCnom = DotProduct( p - b, bc ), projBCdenom = DotProduct( p - c, b - c );
  1054. if ( projABdenom <= 0.0f && projBCnom <= 0.0f )
  1055. {
  1056. return b; // Vertex region early out
  1057. }
  1058. if ( projACdenom <= 0.0f && projBCdenom <= 0.0f )
  1059. {
  1060. return c; // Vertex region early out
  1061. }
  1062. Vector n = CrossProduct( ab, ac );
  1063. // P is outside (or on) AB if the triple product [N PA PB] = N.(PAxPB) <= 0
  1064. float tpC = DotProduct( n, CrossProduct( a - p, b - p ) );
  1065. // If P outside AB and within feature region of AB, return projection of P onto AB
  1066. if ( tpC <= 0.0f && projABnom >= 0.0f && projABdenom >= 0.0f )
  1067. {
  1068. return a + projABnom / ( projABnom + projABdenom ) * ab;
  1069. }
  1070. // P is outside (or on) BC if the triple product [N PB PC] = N.(PBxPC) <= 0
  1071. float tpA = DotProduct( n, CrossProduct(b - p, c - p) );
  1072. // If P outside BC and within feature region of BC, return projection of P onto BC
  1073. if ( tpA <= 0.0f && projBCnom >= 0.0f && projBCdenom >= 0.0f )
  1074. {
  1075. return b + projBCnom / (projBCnom + projBCdenom) * bc;
  1076. }
  1077. // P is outside (or on) CA if the triple product [N PC PA] = N.(PCxPA) <= 0
  1078. float tpB = DotProduct( n, CrossProduct(c - p, a - p) );
  1079. // If P outside CA and within feature region of CA, return projection of P onto CA
  1080. if ( tpB <= 0.0f && projACnom >= 0.0f && projACdenom >= 0.0f )
  1081. {
  1082. return a + projACnom / ( projACnom + projACdenom ) * ac;
  1083. }
  1084. // P must project inside the triangle. Compute the position using barycentric coordinates
  1085. float u = tpA / ( tpA + tpB + tpC );
  1086. float v = tpB / ( tpA + tpB + tpC );
  1087. float w = 1.0f - u - v; // = tpC / ( tpA + tpB + tpC )
  1088. return u * a + v * b + w * c;
  1089. }
  1090. bool IsSphereIntersectForwardFacingTriangle( const Vector& vCenter, const VPlane& plane, float flRadius, const Vector& a, const Vector& b, const Vector& c, Vector& vClosestPoint )
  1091. {
  1092. vClosestPoint = FindClosestPointToTriangle( vCenter, a, b, c );
  1093. float flDistFromPlane = DotProduct( vClosestPoint, plane.m_Normal ) - plane.m_Dist;
  1094. if ( flDistFromPlane < 0.f )
  1095. {
  1096. vClosestPoint = vClosestPoint + ( flDistFromPlane + 0.1f ) * plane.m_Normal;
  1097. }
  1098. Vector vDist = vCenter - vClosestPoint;
  1099. float flDistSquare = DotProduct( vDist, vDist );
  1100. return flDistSquare < Square( flRadius );
  1101. }
  1102. void R_AddPaintToSurface( SurfaceHandle_t surfID, paintinfo_t *paintinfo )
  1103. {
  1104. const unsigned int ignoreFlags = SURFDRAW_NOPAINT | SURFDRAW_TRANS;
  1105. if ( MSurf_Flags( surfID ) & ignoreFlags )
  1106. return;
  1107. // Displacement surfaces get decals in R_DecalLeaf.
  1108. if ( SurfaceHasDispInfo( surfID ) )
  1109. return;
  1110. if ( paintinfo->m_aApplySurfs.Find( surfID ) != -1 )
  1111. return;
  1112. // in multiplayer, materialSortInfoArray is initialized after paint power user objects are active
  1113. if ( materialSortInfoArray == NULL )
  1114. return;
  1115. // don't do it if it's full bright
  1116. if ( materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID < 0 )
  1117. return;
  1118. VPlane plane = MSurf_GetForwardFacingPlane( surfID );
  1119. float flDistFromPlane = DotProduct( paintinfo->m_vPosition, plane.m_Normal ) - plane.m_Dist;
  1120. // ignore if point is behind the plane or too far from the forward facing plane
  1121. if ( flDistFromPlane > paintinfo->m_flSize || flDistFromPlane < 0.f )
  1122. return;
  1123. // check if the sphere actually intersecting with the surface using barycentric test
  1124. int nFirstVertex = MSurf_FirstVertIndex( surfID );
  1125. int numVert = MSurf_VertCount( surfID );
  1126. int vertIndex = host_state.worldbrush->vertindices[nFirstVertex];
  1127. Vector vOrigin = host_state.worldbrush->vertexes[vertIndex].position;
  1128. bool bIntersect = false;
  1129. Vector vClosestPoint;
  1130. for (int v = 1; v < numVert - 1; ++v )
  1131. {
  1132. vertIndex = host_state.worldbrush->vertindices[nFirstVertex+v];
  1133. Vector v1 = host_state.worldbrush->vertexes[vertIndex].position;
  1134. vertIndex = host_state.worldbrush->vertindices[nFirstVertex+v+1];
  1135. Vector v2 = host_state.worldbrush->vertexes[vertIndex].position;
  1136. Vector vIntersectPoint;
  1137. if ( IsSphereIntersectForwardFacingTriangle( paintinfo->m_vPosition, plane, paintinfo->m_flSize, vOrigin, v1, v2, vIntersectPoint ) )
  1138. {
  1139. if ( !bIntersect )
  1140. {
  1141. vClosestPoint = vIntersectPoint;
  1142. }
  1143. else
  1144. {
  1145. Vector vecA = vClosestPoint - paintinfo->m_vPosition;
  1146. Vector vecB = vIntersectPoint - paintinfo->m_vPosition;
  1147. if ( DotProduct( vecB, vecB ) < DotProduct( vecA, vecA ) )
  1148. {
  1149. vClosestPoint = vIntersectPoint;
  1150. }
  1151. }
  1152. bIntersect = true;
  1153. }
  1154. }
  1155. if ( bIntersect )
  1156. {
  1157. Vector vDist = vClosestPoint - paintinfo->m_vPosition;
  1158. float flDistFromClosestPointSquare = DotProduct( vDist, vDist );
  1159. if ( flDistFromClosestPointSquare < paintinfo->m_flCurrentDistance )
  1160. {
  1161. paintinfo->m_flCurrentDistance = flDistFromClosestPointSquare;
  1162. paintinfo->m_plane = plane;
  1163. }
  1164. paintinfo->m_aApplySurfs.AddToTail( surfID );
  1165. }
  1166. }
  1167. //-----------------------------------------------------------------------------
  1168. // iterate over all surfaces on a model, looking for surfaces to paint
  1169. //-----------------------------------------------------------------------------
  1170. void R_PaintLeaf( mleaf_t *pLeaf, paintinfo_t *paintinfo )
  1171. {
  1172. SurfaceHandle_t *pHandle = &host_state.worldbrush->marksurfaces[pLeaf->firstmarksurface];
  1173. for ( int i = 0; i < pLeaf->nummarksurfaces; i++ )
  1174. {
  1175. SurfaceHandle_t surfID = pHandle[i];
  1176. R_AddPaintToSurface( surfID, paintinfo );
  1177. }
  1178. }
  1179. void R_PaintNode( mnode_t *node, paintinfo_t* paintinfo )
  1180. {
  1181. cplane_t *splitplane;
  1182. float dist;
  1183. if (!node )
  1184. return;
  1185. if ( node->contents >= 0 )
  1186. {
  1187. R_PaintLeaf( (mleaf_t *)node, paintinfo );
  1188. return;
  1189. }
  1190. splitplane = node->plane;
  1191. dist = DotProduct (paintinfo->m_vPosition, splitplane->normal) - splitplane->dist;
  1192. if (dist > paintinfo->m_flSize)
  1193. {
  1194. R_PaintNode (node->children[0], paintinfo);
  1195. }
  1196. else if (dist < -paintinfo->m_flSize)
  1197. {
  1198. R_PaintNode (node->children[1], paintinfo);
  1199. }
  1200. else
  1201. {
  1202. R_PaintNode (node->children[0], paintinfo);
  1203. R_PaintNode (node->children[1], paintinfo);
  1204. }
  1205. }
  1206. bool IsSurfaceInFrontOfPlane( SurfaceHandle_t surfID, const VPlane& plane )
  1207. {
  1208. // check if the surface is on the same plane as the main plane
  1209. VPlane trianglePlane = MSurf_GetForwardFacingPlane( surfID );
  1210. if ( AlmostEqual( DotProduct( plane.m_Normal, trianglePlane.m_Normal ), 1.f ) && AlmostEqual( plane.m_Dist, trianglePlane.m_Dist) )
  1211. {
  1212. return true;
  1213. }
  1214. // if center of the first triangle is behind the main plane, ignore it.
  1215. int numVert = MSurf_VertCount( surfID );
  1216. if ( numVert < 3 )
  1217. {
  1218. return false;
  1219. }
  1220. int nFirstVertex = MSurf_FirstVertIndex( surfID );
  1221. int vertIndex = host_state.worldbrush->vertindices[nFirstVertex];
  1222. Vector vOrigin = host_state.worldbrush->vertexes[vertIndex].position;
  1223. vertIndex = host_state.worldbrush->vertindices[nFirstVertex + 1];
  1224. Vector v1 = host_state.worldbrush->vertexes[vertIndex].position;
  1225. vertIndex = host_state.worldbrush->vertindices[nFirstVertex + 2];
  1226. Vector v2 = host_state.worldbrush->vertexes[vertIndex].position;
  1227. Vector vCenter = vOrigin + 0.25f * ( ( v1 - vOrigin ) + ( v2 - vOrigin ) );
  1228. vCenter += 0.1f * trianglePlane.m_Normal; // push it a bit off the triangle plane to account for floating point error
  1229. if ( plane.DistTo( vCenter ) < 0.f )
  1230. {
  1231. return false;
  1232. }
  1233. // debug paint rejection
  1234. /*{
  1235. CDebugOverlay::AddTriangleOverlay( vOrigin, v1, v2, 255, 0,0,128, true, 10.0f );
  1236. CDebugOverlay::AddBoxOverlay( vCenter, Vector(-1,-1,-1), Vector(1,1,1), vec3_angle, 255, 255,0,128, 10.0f );
  1237. }*/
  1238. return true;
  1239. }
  1240. bool ShootPaintSphere( const model_t *pModel, const Vector& vPosition, BYTE colorIndex, float flSphereRadius, float flPaintCoatPercent )
  1241. {
  1242. if ( !g_PaintManager.m_bShouldRegister || g_PaintManager.m_pPaintTextureDataArray == NULL )
  1243. {
  1244. return false;
  1245. }
  1246. Assert( pModel );
  1247. if ( !pModel )
  1248. return false;
  1249. paintinfo_t paintinfo;
  1250. paintinfo.m_vPosition = vPosition;
  1251. paintinfo.m_flSize = flSphereRadius;
  1252. paintinfo.m_flCurrentDistance = FLT_MAX;
  1253. paintinfo.m_pBrush = pModel->brush.pShared;
  1254. paintinfo.m_aApplySurfs.RemoveAll();
  1255. paintinfo.m_bPainting = true;
  1256. mnode_t *pnodes = paintinfo.m_pBrush->nodes + pModel->brush.firstnode;
  1257. R_PaintNode( pnodes, &paintinfo );
  1258. //do the actual painting
  1259. bool bChangedPaint = false;
  1260. int numSurf = paintinfo.m_aApplySurfs.Count();
  1261. for ( int i=0; i<numSurf; ++i )
  1262. {
  1263. // check if surface is behind main surface
  1264. if ( IsSurfaceInFrontOfPlane( paintinfo.m_aApplySurfs[i], paintinfo.m_plane ) )
  1265. {
  1266. if ( R_PaintSurface( paintinfo.m_aApplySurfs[i], vPosition, flSphereRadius, paintinfo.m_plane, colorIndex, flPaintCoatPercent ) )
  1267. {
  1268. bChangedPaint = true;
  1269. }
  1270. }
  1271. }
  1272. return bChangedPaint;
  1273. }
  1274. //-----------------------------------------------------------------------------
  1275. // iterate over all surfaces on a model, to find painted surface at the collision point
  1276. //-----------------------------------------------------------------------------
  1277. void GetPaintColorFromSurface( SurfaceHandle_t surfID, const Vector& vPosition, const Vector& vContactNormal, float flSphereRadius, const VPlane& basePlane, CUtlVector<BYTE>& surfColors )
  1278. {
  1279. if ( (MSurf_Flags( surfID ) & SURFDRAW_PAINTED) == 0 )
  1280. return;
  1281. // only detects paint from surface that has similar normal
  1282. if ( DotProduct( vContactNormal, MSurf_GetForwardFacingPlane( surfID ).m_Normal ) < 0.9f )
  1283. return;
  1284. int lightmapID = materialSortInfoArray[MSurf_MaterialSortID( surfID )].lightmapPageID;
  1285. PaintRect_t paintRect;
  1286. if ( ComputePaintRect( surfID, vPosition, flSphereRadius, paintRect ) )
  1287. {
  1288. g_PaintManager.m_pPaintTextureDataArray[ lightmapID ].GetPixels( paintRect.rect, surfColors );
  1289. }
  1290. }
  1291. void TracePaintSphere( const model_t *pModel, const Vector& vPosition, const Vector& vContactNormal, float flSphereRadius, CUtlVector<BYTE>& surfColors )
  1292. {
  1293. if ( !g_PaintManager.m_bShouldRegister || g_PaintManager.m_pPaintTextureDataArray == NULL )
  1294. {
  1295. return;
  1296. }
  1297. Assert( pModel );
  1298. if ( !pModel )
  1299. return;
  1300. //clear paint
  1301. surfColors.RemoveAll();
  1302. paintinfo_t paintinfo;
  1303. paintinfo.m_vPosition = vPosition;
  1304. paintinfo.m_flSize = flSphereRadius;
  1305. paintinfo.m_flCurrentDistance = FLT_MAX;
  1306. paintinfo.m_pBrush = pModel->brush.pShared;
  1307. paintinfo.m_aApplySurfs.RemoveAll();
  1308. paintinfo.m_bPainting = false;
  1309. mnode_t *pnodes = paintinfo.m_pBrush->nodes + pModel->brush.firstnode;
  1310. R_PaintNode( pnodes, &paintinfo );
  1311. int numSurf = paintinfo.m_aApplySurfs.Count();
  1312. for ( int i=0; i<numSurf; ++i )
  1313. {
  1314. GetPaintColorFromSurface( paintinfo.m_aApplySurfs[i], vPosition, vContactNormal, flSphereRadius, paintinfo.m_plane, surfColors );
  1315. }
  1316. }
  1317. void R_RedownloadAllPaintmaps()
  1318. {
  1319. if ( g_PaintManager.m_bShouldRegister )
  1320. {
  1321. for ( int i=0; i<g_PaintManager.m_iPaintmaps; ++i )
  1322. {
  1323. g_PaintManager.m_pPaintTextureDataArray[i].MarkAsDirty();
  1324. }
  1325. g_PaintManager.UpdatePaintmapTextures();
  1326. }
  1327. }
  1328. #if 0
  1329. CON_COMMAND_F( dump_paintmaps, "dump paintmap data to \"paintmap_#.txt\"", FCVAR_CHEAT )
  1330. {
  1331. for ( int i=0; i<g_PaintManager.m_iPaintmaps; ++i )
  1332. {
  1333. char filename[64];
  1334. V_snprintf( filename, sizeof(filename), "paintmap_%i.txt", i );
  1335. CUtlBuffer buf;
  1336. const BYTE *pData = g_PaintManager.GetPaintmapData(i);
  1337. int w,h;
  1338. g_PaintManager.GetPaintmapSize( i, w, h );
  1339. int size = w*h;
  1340. for ( int b=0; b<size; ++b)
  1341. {
  1342. buf.PutChar( pData[b] );
  1343. }
  1344. g_pFullFileSystem->WriteFile( filename, NULL, buf );
  1345. buf.Purge();
  1346. }
  1347. }
  1348. #endif