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.

457 lines
13 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Contains 2D clipping routines
  4. //
  5. // $Revision: $
  6. // $NoKeywords: $
  7. //=============================================================================//
  8. #include <vgui/ISurface.h>
  9. #include "Clip2D.h"
  10. #include "tier0/dbg.h"
  11. #include "utlvector.h"
  12. #if defined( _X360 )
  13. #include "materialsystem/imaterialsystem.h"
  14. #endif
  15. // memdbgon must be the last include file in a .cpp file!!!
  16. #include "tier0/memdbgon.h"
  17. //-----------------------------------------------------------------------------
  18. // Stretch texture to fit window ( before scissoring )
  19. //-----------------------------------------------------------------------------
  20. static bool g_bStretchTexture = false;
  21. //-----------------------------------------------------------------------------
  22. // Max # of vertices for clipping
  23. //-----------------------------------------------------------------------------
  24. enum
  25. {
  26. VGUI_VERTEX_TEMP_COUNT = 48,
  27. };
  28. //-----------------------------------------------------------------------------
  29. // For simulated scissor tests...
  30. //-----------------------------------------------------------------------------
  31. struct ScissorRect_t
  32. {
  33. int left;
  34. int top;
  35. int right;
  36. int bottom;
  37. };
  38. static ScissorRect_t g_ScissorRect;
  39. static bool g_bScissor = false;
  40. static bool g_bFullScreenScissor = false;
  41. //-----------------------------------------------------------------------------
  42. // Enable/disable scissoring...
  43. //-----------------------------------------------------------------------------
  44. void EnableScissor( bool enable )
  45. {
  46. g_bScissor = enable;
  47. }
  48. void SetScissorRect( int left, int top, int right, int bottom )
  49. {
  50. // Check for a valid rectangle...
  51. Assert( left <= right );
  52. Assert( top <= bottom );
  53. if ( g_ScissorRect.left == left && g_ScissorRect.right == right &&
  54. g_ScissorRect.top == top && g_ScissorRect.bottom == bottom )
  55. return;
  56. g_ScissorRect.left = left;
  57. g_ScissorRect.top = top;
  58. g_ScissorRect.right = right;
  59. g_ScissorRect.bottom = bottom;
  60. #if defined( _X360 )
  61. // no reason to waste cpu on full screen scissor, gpu does it
  62. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  63. int vx, vy, vw, vh;
  64. pRenderContext->GetViewport( vx, vy, vw, vh );
  65. g_bFullScreenScissor = (left <= vx && top <= vy && right >= vw && bottom >= vh );
  66. #endif
  67. }
  68. void GetScissorRect( int &left, int &top, int &right, int &bottom, bool &enabled )
  69. {
  70. left = g_ScissorRect.left;
  71. top = g_ScissorRect.top;
  72. right = g_ScissorRect.right;
  73. bottom = g_ScissorRect.bottom;
  74. enabled = g_bScissor;
  75. }
  76. //-----------------------------------------------------------------------------
  77. // Used to clip the shadow decals
  78. //-----------------------------------------------------------------------------
  79. struct PolygonClipState_t
  80. {
  81. int m_CurrVert;
  82. int m_TempCount;
  83. int m_ClipCount;
  84. vgui::Vertex_t m_pTempVertices[VGUI_VERTEX_TEMP_COUNT];
  85. vgui::Vertex_t* m_ppClipVertices[2][VGUI_VERTEX_TEMP_COUNT];
  86. };
  87. //-----------------------------------------------------------------------------
  88. // Clipping methods for 2D
  89. //-----------------------------------------------------------------------------
  90. class CClipTop
  91. {
  92. public:
  93. static inline bool Inside( vgui::Vertex_t const& vert )
  94. {
  95. return vert.m_Position.y >= g_ScissorRect.top;
  96. }
  97. static inline float Clip( const Vector2D& one, const Vector2D& two )
  98. {
  99. return (g_ScissorRect.top - one.y) / (two.y - one.y);
  100. }
  101. };
  102. class CClipLeft
  103. {
  104. public:
  105. static inline bool Inside( vgui::Vertex_t const& vert )
  106. {
  107. return vert.m_Position.x >= g_ScissorRect.left;
  108. }
  109. static inline float Clip( const Vector2D& one, const Vector2D& two )
  110. {
  111. return (one.x - g_ScissorRect.left) / (one.x - two.x);
  112. }
  113. };
  114. class CClipRight
  115. {
  116. public:
  117. static inline bool Inside( vgui::Vertex_t const& vert )
  118. {
  119. return vert.m_Position.x < g_ScissorRect.right;
  120. }
  121. static inline float Clip( const Vector2D& one, const Vector2D& two )
  122. {
  123. return (g_ScissorRect.right - one.x) / (two.x - one.x);
  124. }
  125. };
  126. class CClipBottom
  127. {
  128. public:
  129. static inline bool Inside( vgui::Vertex_t const& vert )
  130. {
  131. return vert.m_Position.y < g_ScissorRect.bottom;
  132. }
  133. static inline float Clip( const Vector2D& one, const Vector2D& two )
  134. {
  135. return (one.y - g_ScissorRect.bottom) / (one.y - two.y);
  136. }
  137. };
  138. template <class Clipper>
  139. static inline void Intersect( const vgui::Vertex_t& start, const vgui::Vertex_t& end, vgui::Vertex_t* pOut, Clipper& clipper )
  140. {
  141. // Clip to the scissor rectangle
  142. float t = Clipper::Clip( start.m_Position, end.m_Position );
  143. Vector2DLerp( start.m_Position, end.m_Position, t, pOut->m_Position );
  144. Vector2DLerp( start.m_TexCoord, end.m_TexCoord, t, pOut->m_TexCoord );
  145. }
  146. //-----------------------------------------------------------------------------
  147. // Clips a line segment to a single plane
  148. //-----------------------------------------------------------------------------
  149. template< class Clipper >
  150. bool ClipLineToPlane( Clipper &clipper, const vgui::Vertex_t *pInVerts, vgui::Vertex_t* pOutVerts )
  151. {
  152. bool startInside = Clipper::Inside( pInVerts[0] );
  153. bool endInside = Clipper::Inside( pInVerts[1] );
  154. // Cull
  155. if (!startInside && !endInside)
  156. return false;
  157. if (startInside && endInside)
  158. {
  159. pOutVerts[0] = pInVerts[0];
  160. pOutVerts[1] = pInVerts[1];
  161. }
  162. else
  163. {
  164. int inIndex = startInside ? 0 : 1;
  165. pOutVerts[inIndex] = pInVerts[inIndex];
  166. Intersect( pInVerts[0], pInVerts[1], &pOutVerts[1 - inIndex], clipper );
  167. }
  168. return true;
  169. }
  170. //-----------------------------------------------------------------------------
  171. // Clips a line segment to the current scissor rectangle
  172. //-----------------------------------------------------------------------------
  173. bool ClipLine( const vgui::Vertex_t *pInVerts, vgui::Vertex_t* pOutVerts )
  174. {
  175. if ( g_bScissor && !g_bFullScreenScissor )
  176. {
  177. // Clippers...
  178. CClipTop top;
  179. CClipBottom bottom;
  180. CClipLeft left;
  181. CClipRight right;
  182. // Sutherland-hodgman clip, not particularly efficient but that's ok for now
  183. vgui::Vertex_t tempVerts[2];
  184. if (!ClipLineToPlane( top, pInVerts, tempVerts ))
  185. return false;
  186. if (!ClipLineToPlane( bottom, tempVerts, pOutVerts ))
  187. return false;
  188. if (!ClipLineToPlane( left, pOutVerts, tempVerts ))
  189. return false;
  190. if (!ClipLineToPlane( right, tempVerts, pOutVerts ))
  191. return false;
  192. return true;
  193. }
  194. else
  195. {
  196. pOutVerts[0] = pInVerts[0];
  197. pOutVerts[1] = pInVerts[1];
  198. return true;
  199. }
  200. }
  201. //-----------------------------------------------------------------------------
  202. // Methods associated with clipping 2D polygons
  203. //-----------------------------------------------------------------------------
  204. struct ScreenClipState_t
  205. {
  206. int m_iCurrVert;
  207. int m_iTempCount;
  208. int m_iClipCount;
  209. CUtlVector<vgui::Vertex_t> m_pTempVertices;
  210. CUtlVector<vgui::Vertex_t*> m_ppClipVertices[2];
  211. };
  212. template <class Clipper>
  213. static void ScreenClip( ScreenClipState_t& clip, Clipper& clipper )
  214. {
  215. if (clip.m_iClipCount < 3)
  216. return;
  217. // Ye Olde Sutherland-Hodgman clipping algorithm
  218. int numOutVerts = 0;
  219. vgui::Vertex_t** pSrcVert = clip.m_ppClipVertices[clip.m_iCurrVert].Base();
  220. vgui::Vertex_t** pDestVert = clip.m_ppClipVertices[!clip.m_iCurrVert].Base();
  221. int numVerts = clip.m_iClipCount;
  222. vgui::Vertex_t* pStart = pSrcVert[numVerts-1];
  223. bool startInside = Clipper::Inside( *pStart );
  224. for (int i = 0; i < numVerts; ++i)
  225. {
  226. vgui::Vertex_t* pEnd = pSrcVert[i];
  227. bool endInside = Clipper::Inside( *pEnd );
  228. if (endInside)
  229. {
  230. if (!startInside)
  231. {
  232. // Started outside, ended inside, need to clip the edge
  233. Assert( clip.m_iTempCount <= clip.m_pTempVertices.Count() );
  234. // Allocate a new clipped vertex
  235. pDestVert[numOutVerts] = &clip.m_pTempVertices[clip.m_iTempCount++];
  236. // Clip the edge to the clip plane
  237. Intersect( *pStart, *pEnd, pDestVert[numOutVerts], clipper );
  238. ++numOutVerts;
  239. }
  240. pDestVert[numOutVerts++] = pEnd;
  241. }
  242. else
  243. {
  244. if (startInside)
  245. {
  246. // Started inside, ended outside, need to clip the edge
  247. Assert( clip.m_iTempCount <= clip.m_pTempVertices.Count() );
  248. // Allocate a new clipped vertex
  249. pDestVert[numOutVerts] = &clip.m_pTempVertices[clip.m_iTempCount++];
  250. // Clip the edge to the clip plane
  251. Intersect( *pStart, *pEnd, pDestVert[numOutVerts], clipper );
  252. ++numOutVerts;
  253. }
  254. }
  255. pStart = pEnd;
  256. startInside = endInside;
  257. }
  258. // Switch source lists
  259. clip.m_iCurrVert = 1 - clip.m_iCurrVert;
  260. clip.m_iClipCount = numOutVerts;
  261. }
  262. //-----------------------------------------------------------------------------
  263. // Clips a polygon to the screen area
  264. //-----------------------------------------------------------------------------
  265. int ClipPolygon( int iCount, vgui::Vertex_t *pVerts, int iTranslateX, int iTranslateY, vgui::Vertex_t ***pppOutVertex )
  266. {
  267. static ScreenClipState_t clip;
  268. // Allocate enough room in the clip state...
  269. // Having no reallocations during clipping
  270. clip.m_pTempVertices.EnsureCount( iCount * 4 );
  271. clip.m_ppClipVertices[0].EnsureCount( iCount * 4 );
  272. clip.m_ppClipVertices[1].EnsureCount( iCount * 4 );
  273. // Copy the initial verts in...
  274. for (int i = 0; i < iCount; ++i)
  275. {
  276. // NOTE: This only works because we EnsuredCount above
  277. clip.m_pTempVertices[i] = pVerts[i];
  278. clip.m_pTempVertices[i].m_Position.x += iTranslateX;
  279. clip.m_pTempVertices[i].m_Position.y += iTranslateY;
  280. clip.m_ppClipVertices[0][i] = &clip.m_pTempVertices[i];
  281. }
  282. if ( !g_bScissor || g_bFullScreenScissor )
  283. {
  284. Assert(pppOutVertex);
  285. *pppOutVertex = clip.m_ppClipVertices[0].Base();
  286. return iCount;
  287. }
  288. clip.m_iClipCount = iCount;
  289. clip.m_iTempCount = iCount;
  290. clip.m_iCurrVert = 0;
  291. // Clippers...
  292. CClipTop top;
  293. CClipBottom bottom;
  294. CClipLeft left;
  295. CClipRight right;
  296. // Sutherland-hodgman clip
  297. ScreenClip( clip, top );
  298. ScreenClip( clip, bottom );
  299. ScreenClip( clip, left );
  300. ScreenClip( clip, right );
  301. if (clip.m_iClipCount < 3)
  302. return 0;
  303. // Return a pointer to the array of clipped vertices...
  304. Assert(pppOutVertex);
  305. *pppOutVertex = clip.m_ppClipVertices[clip.m_iCurrVert].Base();
  306. return clip.m_iClipCount;
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose: Used for clipping, produces an interpolated texture coordinate
  310. //-----------------------------------------------------------------------------
  311. inline float InterpTCoord(float val, float mins, float maxs, float tMin, float tMax)
  312. {
  313. float flPercent;
  314. if (mins != maxs)
  315. flPercent = (float)(val - mins) / (maxs - mins);
  316. else
  317. flPercent = 0.5f;
  318. return tMin + (tMax - tMin) * flPercent;
  319. }
  320. //-----------------------------------------------------------------------------
  321. // Purpose: Does a scissor clip of the input rectangle.
  322. // Returns false if it is completely clipped off.
  323. //-----------------------------------------------------------------------------
  324. bool ClipRect( const vgui::Vertex_t &inUL, const vgui::Vertex_t &inLR,
  325. vgui::Vertex_t *pOutUL, vgui::Vertex_t *pOutLR )
  326. {
  327. // Check for a valid rectangle...
  328. // Assert( inUL.m_Position.x <= inLR.m_Position.x );
  329. // Assert( inUL.m_Position.y <= inLR.m_Position.y );
  330. if ( IsX360() && ( !g_bScissor || g_bFullScreenScissor ||
  331. ( inUL.m_Position.x >= g_ScissorRect.left && inLR.m_Position.x <= g_ScissorRect.right && inUL.m_Position.y >= g_ScissorRect.top && inLR.m_Position.y <= g_ScissorRect.bottom ) ) )
  332. {
  333. // clipping is not needed
  334. // either full screen, and hw will do it or rect is inscribed, and operation is meaningless
  335. *pOutUL = inUL;
  336. *pOutLR = inLR;
  337. return true;
  338. }
  339. if ( g_bScissor )
  340. {
  341. // Pick whichever left side is larger
  342. if (g_ScissorRect.left > inUL.m_Position.x)
  343. pOutUL->m_Position.x = g_ScissorRect.left;
  344. else
  345. pOutUL->m_Position.x = inUL.m_Position.x;
  346. // Pick whichever right side is smaller
  347. if (g_ScissorRect.right <= inLR.m_Position.x)
  348. pOutLR->m_Position.x = g_ScissorRect.right;
  349. else
  350. pOutLR->m_Position.x = inLR.m_Position.x;
  351. // Pick whichever top side is larger
  352. if (g_ScissorRect.top > inUL.m_Position.y)
  353. pOutUL->m_Position.y = g_ScissorRect.top;
  354. else
  355. pOutUL->m_Position.y = inUL.m_Position.y;
  356. // Pick whichever bottom side is smaller
  357. if (g_ScissorRect.bottom <= inLR.m_Position.y)
  358. pOutLR->m_Position.y = g_ScissorRect.bottom;
  359. else
  360. pOutLR->m_Position.y = inLR.m_Position.y;
  361. // Check for non-intersecting
  362. if ( (pOutUL->m_Position.x > pOutLR->m_Position.x) ||
  363. (pOutUL->m_Position.y > pOutLR->m_Position.y) )
  364. {
  365. return false;
  366. }
  367. if ( !g_bStretchTexture )
  368. {
  369. pOutUL->m_TexCoord.x = InterpTCoord(pOutUL->m_Position.x,
  370. inUL.m_Position.x, inLR.m_Position.x, inUL.m_TexCoord.x, inLR.m_TexCoord.x);
  371. pOutLR->m_TexCoord.x = InterpTCoord(pOutLR->m_Position.x,
  372. inUL.m_Position.x, inLR.m_Position.x, inUL.m_TexCoord.x, inLR.m_TexCoord.x);
  373. pOutUL->m_TexCoord.y = InterpTCoord(pOutUL->m_Position.y,
  374. inUL.m_Position.y, inLR.m_Position.y, inUL.m_TexCoord.y, inLR.m_TexCoord.y);
  375. pOutLR->m_TexCoord.y = InterpTCoord(pOutLR->m_Position.y,
  376. inUL.m_Position.y, inLR.m_Position.y, inUL.m_TexCoord.y, inLR.m_TexCoord.y);
  377. }
  378. else
  379. {
  380. // FIXME, this isn't right
  381. pOutUL->m_TexCoord = inUL.m_TexCoord;
  382. pOutLR->m_TexCoord = inLR.m_TexCoord;
  383. }
  384. }
  385. else
  386. {
  387. *pOutUL = inUL;
  388. *pOutLR = inLR;
  389. }
  390. return true;
  391. }