diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /vguimatsurface/Clip2D.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'vguimatsurface/Clip2D.cpp')
| -rw-r--r-- | vguimatsurface/Clip2D.cpp | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/vguimatsurface/Clip2D.cpp b/vguimatsurface/Clip2D.cpp new file mode 100644 index 0000000..af4cef2 --- /dev/null +++ b/vguimatsurface/Clip2D.cpp @@ -0,0 +1,457 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: Contains 2D clipping routines +// +// $Revision: $ +// $NoKeywords: $ +//=============================================================================// + +#include <vgui/ISurface.h> +#include "Clip2D.h" +#include "tier0/dbg.h" +#include "utlvector.h" +#if defined( _X360 ) +#include "materialsystem/imaterialsystem.h" +#endif + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Stretch texture to fit window ( before scissoring ) +//----------------------------------------------------------------------------- +static bool g_bStretchTexture = false; + + +//----------------------------------------------------------------------------- +// Max # of vertices for clipping +//----------------------------------------------------------------------------- +enum +{ + VGUI_VERTEX_TEMP_COUNT = 48, +}; + +//----------------------------------------------------------------------------- +// For simulated scissor tests... +//----------------------------------------------------------------------------- +struct ScissorRect_t +{ + int left; + int top; + int right; + int bottom; +}; + +static ScissorRect_t g_ScissorRect; +static bool g_bScissor = false; +static bool g_bFullScreenScissor = false; + +//----------------------------------------------------------------------------- +// Enable/disable scissoring... +//----------------------------------------------------------------------------- +void EnableScissor( bool enable ) +{ + g_bScissor = enable; +} + + +void SetScissorRect( int left, int top, int right, int bottom ) +{ + // Check for a valid rectangle... + Assert( left <= right ); + Assert( top <= bottom ); + + if ( g_ScissorRect.left == left && g_ScissorRect.right == right && + g_ScissorRect.top == top && g_ScissorRect.bottom == bottom ) + return; + + g_ScissorRect.left = left; + g_ScissorRect.top = top; + g_ScissorRect.right = right; + g_ScissorRect.bottom = bottom; + +#if defined( _X360 ) + // no reason to waste cpu on full screen scissor, gpu does it + CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); + int vx, vy, vw, vh; + pRenderContext->GetViewport( vx, vy, vw, vh ); + g_bFullScreenScissor = (left <= vx && top <= vy && right >= vw && bottom >= vh ); +#endif +} + +void GetScissorRect( int &left, int &top, int &right, int &bottom, bool &enabled ) +{ + left = g_ScissorRect.left; + top = g_ScissorRect.top; + right = g_ScissorRect.right; + bottom = g_ScissorRect.bottom; + enabled = g_bScissor; +} + + +//----------------------------------------------------------------------------- +// Used to clip the shadow decals +//----------------------------------------------------------------------------- +struct PolygonClipState_t +{ + int m_CurrVert; + int m_TempCount; + int m_ClipCount; + vgui::Vertex_t m_pTempVertices[VGUI_VERTEX_TEMP_COUNT]; + vgui::Vertex_t* m_ppClipVertices[2][VGUI_VERTEX_TEMP_COUNT]; +}; + + +//----------------------------------------------------------------------------- +// Clipping methods for 2D +//----------------------------------------------------------------------------- +class CClipTop +{ +public: + static inline bool Inside( vgui::Vertex_t const& vert ) + { + return vert.m_Position.y >= g_ScissorRect.top; + } + static inline float Clip( const Vector2D& one, const Vector2D& two ) + { + return (g_ScissorRect.top - one.y) / (two.y - one.y); + } +}; + +class CClipLeft +{ +public: + static inline bool Inside( vgui::Vertex_t const& vert ) + { + return vert.m_Position.x >= g_ScissorRect.left; + } + static inline float Clip( const Vector2D& one, const Vector2D& two ) + { + return (one.x - g_ScissorRect.left) / (one.x - two.x); + } +}; + +class CClipRight +{ +public: + static inline bool Inside( vgui::Vertex_t const& vert ) + { + return vert.m_Position.x < g_ScissorRect.right; + } + static inline float Clip( const Vector2D& one, const Vector2D& two ) + { + return (g_ScissorRect.right - one.x) / (two.x - one.x); + } +}; + +class CClipBottom +{ +public: + static inline bool Inside( vgui::Vertex_t const& vert ) + { + return vert.m_Position.y < g_ScissorRect.bottom; + } + static inline float Clip( const Vector2D& one, const Vector2D& two ) + { + return (one.y - g_ScissorRect.bottom) / (one.y - two.y); + } +}; + +template <class Clipper> +static inline void Intersect( const vgui::Vertex_t& start, const vgui::Vertex_t& end, vgui::Vertex_t* pOut, Clipper& clipper ) +{ + // Clip to the scissor rectangle + float t = Clipper::Clip( start.m_Position, end.m_Position ); + Vector2DLerp( start.m_Position, end.m_Position, t, pOut->m_Position ); + Vector2DLerp( start.m_TexCoord, end.m_TexCoord, t, pOut->m_TexCoord ); +} + + +//----------------------------------------------------------------------------- +// Clips a line segment to a single plane +//----------------------------------------------------------------------------- +template< class Clipper > +bool ClipLineToPlane( Clipper &clipper, const vgui::Vertex_t *pInVerts, vgui::Vertex_t* pOutVerts ) +{ + bool startInside = Clipper::Inside( pInVerts[0] ); + bool endInside = Clipper::Inside( pInVerts[1] ); + + // Cull + if (!startInside && !endInside) + return false; + + if (startInside && endInside) + { + pOutVerts[0] = pInVerts[0]; + pOutVerts[1] = pInVerts[1]; + } + else + { + int inIndex = startInside ? 0 : 1; + pOutVerts[inIndex] = pInVerts[inIndex]; + Intersect( pInVerts[0], pInVerts[1], &pOutVerts[1 - inIndex], clipper ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Clips a line segment to the current scissor rectangle +//----------------------------------------------------------------------------- +bool ClipLine( const vgui::Vertex_t *pInVerts, vgui::Vertex_t* pOutVerts ) +{ + if ( g_bScissor && !g_bFullScreenScissor ) + { + // Clippers... + CClipTop top; + CClipBottom bottom; + CClipLeft left; + CClipRight right; + + // Sutherland-hodgman clip, not particularly efficient but that's ok for now + vgui::Vertex_t tempVerts[2]; + if (!ClipLineToPlane( top, pInVerts, tempVerts )) + return false; + if (!ClipLineToPlane( bottom, tempVerts, pOutVerts )) + return false; + if (!ClipLineToPlane( left, pOutVerts, tempVerts )) + return false; + if (!ClipLineToPlane( right, tempVerts, pOutVerts )) + return false; + + return true; + } + else + { + pOutVerts[0] = pInVerts[0]; + pOutVerts[1] = pInVerts[1]; + return true; + } +} + + +//----------------------------------------------------------------------------- +// Methods associated with clipping 2D polygons +//----------------------------------------------------------------------------- +struct ScreenClipState_t +{ + int m_iCurrVert; + int m_iTempCount; + int m_iClipCount; + CUtlVector<vgui::Vertex_t> m_pTempVertices; + CUtlVector<vgui::Vertex_t*> m_ppClipVertices[2]; +}; + + +template <class Clipper> +static void ScreenClip( ScreenClipState_t& clip, Clipper& clipper ) +{ + if (clip.m_iClipCount < 3) + return; + + // Ye Olde Sutherland-Hodgman clipping algorithm + int numOutVerts = 0; + vgui::Vertex_t** pSrcVert = clip.m_ppClipVertices[clip.m_iCurrVert].Base(); + vgui::Vertex_t** pDestVert = clip.m_ppClipVertices[!clip.m_iCurrVert].Base(); + + int numVerts = clip.m_iClipCount; + vgui::Vertex_t* pStart = pSrcVert[numVerts-1]; + bool startInside = Clipper::Inside( *pStart ); + for (int i = 0; i < numVerts; ++i) + { + vgui::Vertex_t* pEnd = pSrcVert[i]; + bool endInside = Clipper::Inside( *pEnd ); + if (endInside) + { + if (!startInside) + { + // Started outside, ended inside, need to clip the edge + Assert( clip.m_iTempCount <= clip.m_pTempVertices.Count() ); + + // Allocate a new clipped vertex + pDestVert[numOutVerts] = &clip.m_pTempVertices[clip.m_iTempCount++]; + + // Clip the edge to the clip plane + Intersect( *pStart, *pEnd, pDestVert[numOutVerts], clipper ); + ++numOutVerts; + } + pDestVert[numOutVerts++] = pEnd; + } + else + { + if (startInside) + { + // Started inside, ended outside, need to clip the edge + Assert( clip.m_iTempCount <= clip.m_pTempVertices.Count() ); + + // Allocate a new clipped vertex + pDestVert[numOutVerts] = &clip.m_pTempVertices[clip.m_iTempCount++]; + + // Clip the edge to the clip plane + Intersect( *pStart, *pEnd, pDestVert[numOutVerts], clipper ); + ++numOutVerts; + } + } + pStart = pEnd; + startInside = endInside; + } + + // Switch source lists + clip.m_iCurrVert = 1 - clip.m_iCurrVert; + clip.m_iClipCount = numOutVerts; +} + + +//----------------------------------------------------------------------------- +// Clips a polygon to the screen area +//----------------------------------------------------------------------------- +int ClipPolygon( int iCount, vgui::Vertex_t *pVerts, int iTranslateX, int iTranslateY, vgui::Vertex_t ***pppOutVertex ) +{ + static ScreenClipState_t clip; + + // Allocate enough room in the clip state... + // Having no reallocations during clipping + clip.m_pTempVertices.EnsureCount( iCount * 4 ); + clip.m_ppClipVertices[0].EnsureCount( iCount * 4 ); + clip.m_ppClipVertices[1].EnsureCount( iCount * 4 ); + + // Copy the initial verts in... + for (int i = 0; i < iCount; ++i) + { + // NOTE: This only works because we EnsuredCount above + clip.m_pTempVertices[i] = pVerts[i]; + clip.m_pTempVertices[i].m_Position.x += iTranslateX; + clip.m_pTempVertices[i].m_Position.y += iTranslateY; + clip.m_ppClipVertices[0][i] = &clip.m_pTempVertices[i]; + } + + if ( !g_bScissor || g_bFullScreenScissor ) + { + Assert(pppOutVertex); + *pppOutVertex = clip.m_ppClipVertices[0].Base(); + return iCount; + } + + clip.m_iClipCount = iCount; + clip.m_iTempCount = iCount; + clip.m_iCurrVert = 0; + + // Clippers... + CClipTop top; + CClipBottom bottom; + CClipLeft left; + CClipRight right; + + // Sutherland-hodgman clip + ScreenClip( clip, top ); + ScreenClip( clip, bottom ); + ScreenClip( clip, left ); + ScreenClip( clip, right ); + + if (clip.m_iClipCount < 3) + return 0; + + // Return a pointer to the array of clipped vertices... + Assert(pppOutVertex); + *pppOutVertex = clip.m_ppClipVertices[clip.m_iCurrVert].Base(); + return clip.m_iClipCount; +} + + +//----------------------------------------------------------------------------- +// Purpose: Used for clipping, produces an interpolated texture coordinate +//----------------------------------------------------------------------------- +inline float InterpTCoord(float val, float mins, float maxs, float tMin, float tMax) +{ + float flPercent; + if (mins != maxs) + flPercent = (float)(val - mins) / (maxs - mins); + else + flPercent = 0.5f; + return tMin + (tMax - tMin) * flPercent; +} + + +//----------------------------------------------------------------------------- +// Purpose: Does a scissor clip of the input rectangle. +// Returns false if it is completely clipped off. +//----------------------------------------------------------------------------- +bool ClipRect( const vgui::Vertex_t &inUL, const vgui::Vertex_t &inLR, + vgui::Vertex_t *pOutUL, vgui::Vertex_t *pOutLR ) +{ + // Check for a valid rectangle... +// Assert( inUL.m_Position.x <= inLR.m_Position.x ); +// Assert( inUL.m_Position.y <= inLR.m_Position.y ); + + if ( IsX360() && ( !g_bScissor || g_bFullScreenScissor || + ( 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 ) ) ) + { + // clipping is not needed + // either full screen, and hw will do it or rect is inscribed, and operation is meaningless + *pOutUL = inUL; + *pOutLR = inLR; + return true; + } + + if ( g_bScissor ) + { + // Pick whichever left side is larger + if (g_ScissorRect.left > inUL.m_Position.x) + pOutUL->m_Position.x = g_ScissorRect.left; + else + pOutUL->m_Position.x = inUL.m_Position.x; + + // Pick whichever right side is smaller + if (g_ScissorRect.right <= inLR.m_Position.x) + pOutLR->m_Position.x = g_ScissorRect.right; + else + pOutLR->m_Position.x = inLR.m_Position.x; + + // Pick whichever top side is larger + if (g_ScissorRect.top > inUL.m_Position.y) + pOutUL->m_Position.y = g_ScissorRect.top; + else + pOutUL->m_Position.y = inUL.m_Position.y; + + // Pick whichever bottom side is smaller + if (g_ScissorRect.bottom <= inLR.m_Position.y) + pOutLR->m_Position.y = g_ScissorRect.bottom; + else + pOutLR->m_Position.y = inLR.m_Position.y; + + // Check for non-intersecting + if ( (pOutUL->m_Position.x > pOutLR->m_Position.x) || + (pOutUL->m_Position.y > pOutLR->m_Position.y) ) + { + return false; + } + + if ( !g_bStretchTexture ) + { + pOutUL->m_TexCoord.x = InterpTCoord(pOutUL->m_Position.x, + inUL.m_Position.x, inLR.m_Position.x, inUL.m_TexCoord.x, inLR.m_TexCoord.x); + pOutLR->m_TexCoord.x = InterpTCoord(pOutLR->m_Position.x, + inUL.m_Position.x, inLR.m_Position.x, inUL.m_TexCoord.x, inLR.m_TexCoord.x); + + pOutUL->m_TexCoord.y = InterpTCoord(pOutUL->m_Position.y, + inUL.m_Position.y, inLR.m_Position.y, inUL.m_TexCoord.y, inLR.m_TexCoord.y); + pOutLR->m_TexCoord.y = InterpTCoord(pOutLR->m_Position.y, + inUL.m_Position.y, inLR.m_Position.y, inUL.m_TexCoord.y, inLR.m_TexCoord.y); + } + else + { + // FIXME, this isn't right + pOutUL->m_TexCoord = inUL.m_TexCoord; + pOutLR->m_TexCoord = inLR.m_TexCoord; + } + } + else + { + *pOutUL = inUL; + *pOutLR = inLR; + } + + return true; +} + |