summaryrefslogtreecommitdiff
path: root/engine/Overlay.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/Overlay.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'engine/Overlay.cpp')
-rw-r--r--engine/Overlay.cpp2358
1 files changed, 2358 insertions, 0 deletions
diff --git a/engine/Overlay.cpp b/engine/Overlay.cpp
new file mode 100644
index 0000000..8842bc8
--- /dev/null
+++ b/engine/Overlay.cpp
@@ -0,0 +1,2358 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Model loading / unloading interface
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "render_pch.h"
+#include "Overlay.h"
+#include "bspfile.h"
+#include "modelloader.h"
+#include "materialsystem/imesh.h"
+#include "disp.h"
+#include "collisionutils.h"
+#include "tier0/vprof.h"
+#include "render.h"
+#include "r_decal.h"
+#include "fmtstr.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+//-----------------------------------------------------------------------------
+// Externs
+//-----------------------------------------------------------------------------
+int g_OverlayRenderFrameID;
+
+//-----------------------------------------------------------------------------
+// Convars
+//-----------------------------------------------------------------------------
+static ConVar r_renderoverlayfragment("r_renderoverlayfragment", "1");
+static ConVar r_overlaywireframe( "r_overlaywireframe", "0" );
+static ConVar r_overlayfadeenable( "r_overlayfadeenable", "0" );
+static ConVar r_overlayfademin( "r_overlayfademin", "1750.0f" );
+static ConVar r_overlayfademax( "r_overlayfademax", "2000.0f" );
+
+//-----------------------------------------------------------------------------
+// Structures used to represent the overlay
+//-----------------------------------------------------------------------------
+typedef unsigned short OverlayFragmentList_t;
+
+enum
+{
+ OVERLAY_FRAGMENT_LIST_INVALID = (OverlayFragmentList_t)~0,
+};
+
+enum
+{
+ NUM_OVERLAY_TEXCOORDS = 2,
+};
+
+struct overlayvert_t
+{
+ Vector pos;
+ Vector normal;
+ Vector2D texCoord[NUM_OVERLAY_TEXCOORDS]; // texcoord 0 = the mapped tex coord from worldcraft
+ // texcoord 1 is used for alpha and maps the whole texture into the whole overlay
+ float lightCoord[2];
+ float flAlpha;
+
+ overlayvert_t()
+ {
+ pos.Init();
+ normal.Init();
+ texCoord[0].Init();
+ texCoord[1].Init();
+ lightCoord[0] = lightCoord[1] = 0.0f;
+ flAlpha = 1.0f;
+ }
+};
+
+struct moverlayfragment_t
+{
+ int m_nRenderFrameID; // So we only render a fragment once a frame!
+ SurfaceHandle_t m_SurfId; // Surface Id
+ int m_iOverlay; // Overlay Id
+ OverlayFragmentHandle_t m_hNextRender;
+ unsigned short m_nMaterialSortID;
+ CUtlVector<overlayvert_t> m_aPrimVerts;
+};
+
+struct moverlay_t
+{
+ int m_nId;
+ short m_nTexInfo;
+ short m_nRenderOrder; // 0 - MAX_OVERLAY_RENDER_ORDERS
+ OverlayFragmentList_t m_hFirstFragment;
+ CUtlVector<SurfaceHandle_t> m_aFaces;
+ float m_flU[2];
+ float m_flV[2];
+ Vector m_vecUVPoints[4];
+ Vector m_vecOrigin;
+ Vector m_vecBasis[3]; // 0 = u, 1 = v, 2 = normal
+ void *m_pBindProxy; // client renderable for an overlay's material proxy to bind to
+ float m_flFadeDistMinSq; // Distance from the overlay's origin at which we start fading (-1 = use max dist)
+ float m_flFadeDistMaxSq; // Distance from the overlay's origin at which we fade out completely
+ float m_flInvFadeRangeSq; // Precomputed 1.0f / ( m_flFadeDistMaxSq - m_flFadeDistMinSq )
+};
+
+// Going away!
+void Overlay_BuildBasisOrigin( Vector &vecBasisOrigin, SurfaceHandle_t surfID );
+void Overlay_BuildBasis( const Vector &vecBasisNormal, Vector &vecBasisU, Vector &vecBasisV, bool bFlip );
+void Overlay_OverlayUVToOverlayPlane( const Vector &vecBasisOrigin, const Vector &vecBasisU,
+ const Vector &vecBasisV, const Vector &vecUVPoint,
+ Vector &vecPlanePoint );
+void Overlay_WorldToOverlayPlane( const Vector &vecBasisOrigin, const Vector &vecBasisNormal,
+ const Vector &vecWorldPoint, Vector &vecPlanePoint );
+void Overlay_OverlayPlaneToWorld( const Vector &vecBasisNormal, SurfaceHandle_t surfID,
+ const Vector &vecPlanePoint, Vector &vecWorldPoint );
+void Overlay_DispUVToWorld( CDispInfo *pDisp, CMeshReader *pReader, const Vector2D &vecUV, Vector &vecWorld, moverlayfragment_t &surfaceFrag );
+
+
+void Overlay_TriTLToBR( CDispInfo *pDisp, Vector &vecWorld, float flU, float flV,
+ int nSnapU, int nSnapV, int nWidth, int nHeight );
+void Overlay_TriBLToTR( CDispInfo *pDisp, Vector &vecWorld, float flU, float flV,
+ int nSnapU, int nSnapV, int nWidth, int nHeight );
+
+//-----------------------------------------------------------------------------
+// Overlay manager class
+//-----------------------------------------------------------------------------
+class COverlayMgr : public IOverlayMgr
+{
+public:
+ typedef CUtlVector<moverlayfragment_t*> OverlayFragmentVector_t;
+
+
+
+public:
+ COverlayMgr();
+ ~COverlayMgr();
+
+ // Implementation of IOverlayMgr interface
+ virtual bool LoadOverlays( );
+ virtual void UnloadOverlays( );
+
+ virtual void CreateFragments( void );
+ virtual void ReSortMaterials( void );
+ virtual void ClearRenderLists();
+ virtual void ClearRenderLists( int nSortGroup );
+ virtual void AddFragmentListToRenderList( int nSortGroup, OverlayFragmentHandle_t iFragment, bool bDisp );
+ virtual void RenderOverlays( int nSortGroup );
+
+ virtual void SetOverlayBindProxy( int iOverlayID, void *pBindProxy );
+
+private:
+ // Create, destroy material sort order ids...
+ int GetMaterialSortID( IMaterial* pMaterial, int nLightmapPage );
+ void CleanupMaterial( unsigned short nSortOrder );
+
+ moverlay_t *GetOverlay( int iOverlay );
+ moverlayfragment_t *GetOverlayFragment( OverlayFragmentHandle_t iFragment );
+
+ // Surfaces
+ void Surf_CreateFragments( moverlay_t *pOverlay, SurfaceHandle_t surfID );
+ bool Surf_PreClipFragment( moverlay_t *pOverlay, moverlayfragment_t &overlayFrag, SurfaceHandle_t surfID, moverlayfragment_t &surfaceFrag );
+ void Surf_PostClipFragment( moverlay_t *pOverlay, moverlayfragment_t &overlayFrag, SurfaceHandle_t surfID );
+ void Surf_ClipFragment( moverlay_t *pOverlay, moverlayfragment_t &overlayFrag, SurfaceHandle_t surfID, moverlayfragment_t &surfaceFrag );
+
+ // Displacements
+ void Disp_CreateFragments( moverlay_t *pOverlay, SurfaceHandle_t surfID );
+ bool Disp_PreClipFragment( moverlay_t *pOverlay, OverlayFragmentVector_t &aDispFragments, SurfaceHandle_t surfID );
+ void Disp_PostClipFragment( CDispInfo *pDisp, CMeshReader *pReader, moverlay_t *pOverlay, OverlayFragmentVector_t &aDispFragments, SurfaceHandle_t surfID );
+ void Disp_ClipFragment( CDispInfo *pDisp, OverlayFragmentVector_t &aDispFragments );
+ void Disp_DoClip( CDispInfo *pDisp, OverlayFragmentVector_t &aCurrentFragments, cplane_t &clipPlane,
+ float clipDistStart, int nInterval, int nLoopStart, int nLoopEnd, int nLoopInc );
+
+ // Utility
+ OverlayFragmentHandle_t AddFragmentToFragmentList( int nSize );
+ OverlayFragmentHandle_t AddFragmentToFragmentList( moverlayfragment_t *pSrc );
+
+ bool FadeOverlayFragmentGlobal( moverlayfragment_t *pFragment );
+ bool FadeOverlayFragment( moverlay_t *pOverlay, moverlayfragment_t *pFragment );
+
+ moverlayfragment_t *CreateTempFragment( int nSize );
+ moverlayfragment_t *CopyTempFragment( moverlayfragment_t *pSrc );
+ void DestroyTempFragment( moverlayfragment_t *pFragment );
+
+ void BuildClipPlanes( SurfaceHandle_t surfID, moverlayfragment_t &surfaceFrag, const Vector &vecBasisNormal, CUtlVector<cplane_t> &m_ClipPlanes );
+ void DoClipFragment( moverlayfragment_t *pFragment, cplane_t *pClipPlane, moverlayfragment_t **ppFront, moverlayfragment_t **ppBack );
+
+ void InitTexCoords( moverlay_t *pOverlay, moverlayfragment_t &overlayFrag );
+
+private:
+ enum
+ {
+ RENDER_QUEUE_INVALID = 0xFFFF
+ };
+
+ // Structures used to assign sort order handles
+ struct RenderQueueInfo_t
+ {
+ OverlayFragmentHandle_t m_hFirstFragment;
+ unsigned short m_nNextRenderQueue; // Index of next queue that has stuff to render
+ unsigned short m_nVertexCount;
+ unsigned short m_nIndexCount;
+ };
+
+ struct RenderQueueHead_t
+ {
+ IMaterial *m_pMaterial;
+ int m_nLightmapPage;
+
+ RenderQueueInfo_t m_Queue[MAX_MAT_SORT_GROUPS];
+
+ unsigned short m_nRefCount;
+ };
+
+ // First render queue to render
+ unsigned short m_nFirstRenderQueue[MAX_MAT_SORT_GROUPS];
+
+ // Used to assign sort order handles
+ CUtlLinkedList<RenderQueueHead_t, unsigned short> m_RenderQueue;
+
+ // All overlays
+ CUtlVector<moverlay_t> m_aOverlays;
+
+ // List of all overlay fragments. prev/next links point to the next fragment on a *surface*
+ CUtlLinkedList< moverlayfragment_t, unsigned short, true > m_aFragments;
+
+ // Used to find all fragments associated with a particular overlay
+ CUtlLinkedList< OverlayFragmentHandle_t, unsigned short, true > m_OverlayFragments;
+
+ // Fade parameters.
+ float m_flFadeMin2;
+ float m_flFadeMax2;
+ float m_flFadeDelta2;
+};
+
+
+//-----------------------------------------------------------------------------
+// Singleton accessor
+//-----------------------------------------------------------------------------
+static COverlayMgr g_OverlayMgr;
+IOverlayMgr *OverlayMgr( void )
+{
+ return &g_OverlayMgr;
+}
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+COverlayMgr::COverlayMgr()
+{
+ for ( int i = 0; i < MAX_MAT_SORT_GROUPS; ++i )
+ {
+ m_nFirstRenderQueue[i] = RENDER_QUEUE_INVALID;
+ }
+
+ m_flFadeMin2 = 0.0f;
+ m_flFadeMax2 = 0.0f;
+ m_flFadeDelta2 = 0.0f;
+}
+
+//-----------------------------------------------------------------------------
+// Destructor
+//-----------------------------------------------------------------------------
+COverlayMgr::~COverlayMgr()
+{
+ UnloadOverlays();
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns a particular overlay
+//-----------------------------------------------------------------------------
+inline moverlay_t *COverlayMgr::GetOverlay( int iOverlay )
+{
+ return &m_aOverlays[iOverlay];
+}
+
+//-----------------------------------------------------------------------------
+// Returns a particular overlay fragment
+//-----------------------------------------------------------------------------
+inline moverlayfragment_t *COverlayMgr::GetOverlayFragment( OverlayFragmentHandle_t iFragment )
+{
+ return &m_aFragments[iFragment];
+}
+
+
+//-----------------------------------------------------------------------------
+// Cleanup overlays
+//-----------------------------------------------------------------------------
+void COverlayMgr::UnloadOverlays( )
+{
+ FOR_EACH_LL( m_RenderQueue, i )
+ {
+ m_RenderQueue[i].m_pMaterial->DecrementReferenceCount();
+ }
+
+ int nOverlayCount = m_aOverlays.Count();
+ for ( int iOverlay = 0; iOverlay < nOverlayCount; ++iOverlay )
+ {
+ moverlay_t *pOverlay = &m_aOverlays.Element( iOverlay );
+ int hFrag = pOverlay->m_hFirstFragment;
+ while ( hFrag != OVERLAY_FRAGMENT_INVALID )
+ {
+ int iFrag = m_OverlayFragments[hFrag];
+ m_aFragments.Free( iFrag );
+ hFrag = m_OverlayFragments.Next( hFrag );
+ }
+ }
+
+ m_aOverlays.Purge();
+ m_aFragments.Purge();
+ m_OverlayFragments.Purge();
+ m_RenderQueue.Purge();
+
+ for ( int i = 0; i < MAX_MAT_SORT_GROUPS; ++i )
+ {
+ m_nFirstRenderQueue[i] = RENDER_QUEUE_INVALID;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Create, destroy material sort order ids...
+//-----------------------------------------------------------------------------
+int COverlayMgr::GetMaterialSortID( IMaterial* pMaterial, int nLightmapPage )
+{
+ // Search the sort order handles for an enumeration id match (means materials + lightmaps match)
+ unsigned short i;
+ for ( i = m_RenderQueue.Head(); i != m_RenderQueue.InvalidIndex();
+ i = m_RenderQueue.Next(i) )
+ {
+ // Found a match, lets increment the refcount of this sort order id
+ if ((m_RenderQueue[i].m_pMaterial == pMaterial) && (m_RenderQueue[i].m_nLightmapPage == nLightmapPage))
+ {
+ ++m_RenderQueue[i].m_nRefCount;
+ return i;
+ }
+ }
+
+ // Didn't find it, lets assign a new sort order ID, with a refcount of 1
+ i = m_RenderQueue.AddToTail();
+ RenderQueueHead_t &renderQueue = m_RenderQueue[i];
+
+ renderQueue.m_pMaterial = pMaterial;
+ renderQueue.m_nLightmapPage = nLightmapPage;
+ renderQueue.m_nRefCount = 1;
+
+ for ( int j = 0; j < MAX_MAT_SORT_GROUPS; ++j )
+ {
+ RenderQueueInfo_t &info = renderQueue.m_Queue[j];
+
+ info.m_hFirstFragment = OVERLAY_FRAGMENT_INVALID;
+ info.m_nNextRenderQueue = RENDER_QUEUE_INVALID;
+ info.m_nVertexCount = 0;
+ info.m_nIndexCount = 0;
+ }
+
+ pMaterial->IncrementReferenceCount();
+
+ return i;
+}
+
+void COverlayMgr::CleanupMaterial( unsigned short nSortOrder )
+{
+ RenderQueueHead_t &renderQueue = m_RenderQueue[nSortOrder];
+
+#ifdef _DEBUG
+ for ( int i = 0; i < MAX_MAT_SORT_GROUPS; ++i )
+ {
+ // Shouldn't be cleaning up while we've got a render list
+ Assert( renderQueue.m_Queue[i].m_nVertexCount == 0 );
+ }
+#endif
+
+ // Decrease the sort order reference count
+ if (--renderQueue.m_nRefCount <= 0)
+ {
+ renderQueue.m_pMaterial->DecrementReferenceCount();
+
+ // No one referencing the sort order number?
+ // Then lets clean up the sort order id
+ m_RenderQueue.Remove(nSortOrder);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Clears the render lists
+//-----------------------------------------------------------------------------
+void COverlayMgr::ClearRenderLists()
+{
+ for ( int i = 0; i < MAX_MAT_SORT_GROUPS; ++i )
+ {
+ ClearRenderLists( i );
+ }
+
+ if ( r_overlayfadeenable.GetBool() )
+ {
+ float flFadeMin = r_overlayfademin.GetFloat();
+ float flFadeMax = r_overlayfademax.GetFloat();
+ m_flFadeMin2 = flFadeMin * flFadeMin;
+ m_flFadeMax2 = flFadeMax * flFadeMax;
+ m_flFadeDelta2 = 1.0f / ( m_flFadeMax2 - m_flFadeMin2 );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Calculate the fade using the global convars.
+//-----------------------------------------------------------------------------
+bool COverlayMgr::FadeOverlayFragmentGlobal( moverlayfragment_t *pFragment )
+{
+ // Test the overlay distance and set alpha values.
+ int iVert;
+ bool bInRange = false;
+
+ int nVertexCount = pFragment->m_aPrimVerts.Count();
+ for ( iVert = 0; iVert < nVertexCount; ++iVert )
+ {
+ Vector vecSegment;
+ VectorSubtract( MainViewOrigin(), pFragment->m_aPrimVerts.Element( iVert ).pos, vecSegment );
+ float flLength2 = vecSegment.LengthSqr();
+
+ // min dist of -1 means use max dist for fading
+ if ( flLength2 < m_flFadeMin2 )
+ {
+ pFragment->m_aPrimVerts.Element( iVert ).flAlpha = 1.0f;
+ bInRange = true;
+ }
+ else if ( flLength2 > m_flFadeMax2 )
+ {
+ // Set vertex alpha to off.
+ pFragment->m_aPrimVerts.Element( iVert ).flAlpha = 0.0f;
+ }
+ else
+ {
+ // Set the alpha based on distance inside of fadeMin and fadeMax
+ float flAlpha = flLength2 - m_flFadeMin2;
+ flAlpha *= m_flFadeDelta2;
+ pFragment->m_aPrimVerts.Element( iVert ).flAlpha = ( 1.0f - flAlpha );
+
+ bInRange = true;
+ }
+ }
+
+ return bInRange;
+}
+
+
+//-----------------------------------------------------------------------------
+// Calculate the fade using per-overlay fade distances.
+//-----------------------------------------------------------------------------
+bool COverlayMgr::FadeOverlayFragment( moverlay_t *pOverlay, moverlayfragment_t *pFragment )
+{
+ // min dist of -1 means use max dist for fading
+ float flFadeDistMinSq = pOverlay->m_flFadeDistMinSq;
+ float flFadeDistMaxSq = pOverlay->m_flFadeDistMaxSq;
+
+ Vector vecSegment;
+ VectorSubtract( MainViewOrigin(), pOverlay->m_vecOrigin, vecSegment );
+ float flLength2 = vecSegment.LengthSqr();
+
+ float flAlpha = 0.0f;
+ bool bInRange = false;
+ if ( flLength2 < flFadeDistMaxSq )
+ {
+ if ( ( flFadeDistMinSq >= 0 ) && ( flLength2 > flFadeDistMinSq ) )
+ {
+ flAlpha = pOverlay->m_flInvFadeRangeSq * ( flFadeDistMaxSq - flLength2 );
+ flAlpha = clamp( flAlpha, 0.0f, 1.0f );
+ bInRange = true;
+ }
+ else
+ {
+ flAlpha = 1.0f;
+ bInRange = true;
+ }
+ }
+
+ int nVertexCount = pFragment->m_aPrimVerts.Count();
+ for ( int iVert = 0; iVert < nVertexCount; ++iVert )
+ {
+ pFragment->m_aPrimVerts.Element( iVert ).flAlpha = flAlpha;
+ }
+
+ return bInRange;
+}
+
+
+//-----------------------------------------------------------------------------
+// Adds the fragment list to the list of fragments to render when RenderOverlays is called
+//-----------------------------------------------------------------------------
+void COverlayMgr::AddFragmentListToRenderList( int nSortGroup, OverlayFragmentHandle_t iFragment, bool bDisp )
+{
+ OverlayFragmentHandle_t i;
+ for ( i = iFragment; i != OVERLAY_FRAGMENT_INVALID; i = m_aFragments.Next(i) )
+ {
+ // Make sure we don't add the fragment twice...
+ // FIXME: I currently have no way of ensuring a fragment doesn't end up in 2 sort groups
+ // which would cause all manner of nastiness.
+ moverlayfragment_t *pFragment = GetOverlayFragment(i);
+ if ( !bDisp && pFragment->m_nRenderFrameID == g_OverlayRenderFrameID )
+ continue;
+
+ // Triangle count too low? Skip it...
+ int nVertexCount = pFragment->m_aPrimVerts.Count();
+ if ( nVertexCount < 3 )
+ continue;
+
+ moverlay_t *pOverlay = &m_aOverlays[ pFragment->m_iOverlay ];
+
+ // See if we should fade the overlay.
+ if ( r_overlayfadeenable.GetBool() )
+ {
+ // Fade using the convars that control distance.
+ if ( !FadeOverlayFragmentGlobal( pFragment ) )
+ continue;
+ }
+ else if ( pOverlay->m_flFadeDistMaxSq > 0 )
+ {
+ // Fade using per-overlay fade distances, configured by the level designer.
+ if ( !FadeOverlayFragment( pOverlay, pFragment ) )
+ continue;
+ }
+
+ // Update the frame count.
+ pFragment->m_nRenderFrameID = g_OverlayRenderFrameID;
+
+ // Determine the material associated with the fragment...
+ int nMaterialSortID = pFragment->m_nMaterialSortID;
+
+ // Insert the render queue into the list of render queues to render
+ RenderQueueHead_t &renderQueue = m_RenderQueue[nMaterialSortID];
+ RenderQueueInfo_t &info = renderQueue.m_Queue[nSortGroup];
+
+ if ( info.m_hFirstFragment == OVERLAY_FRAGMENT_INVALID )
+ {
+ info.m_nNextRenderQueue = m_nFirstRenderQueue[nSortGroup];
+ m_nFirstRenderQueue[nSortGroup] = nMaterialSortID;
+ }
+
+ // Add to list of fragments for this surface
+ // NOTE: Render them in *reverse* order in which they appeared in the list
+ // because they are stored in the list in *reverse* order in which they should be rendered.
+
+ // Add the fragment to the bucket of fragments to render...
+ pFragment->m_hNextRender = info.m_hFirstFragment;
+ info.m_hFirstFragment = i;
+
+ Assert( info.m_nVertexCount + nVertexCount < 65535 );
+ info.m_nVertexCount += nVertexCount;
+ info.m_nIndexCount += 3 * (nVertexCount - 2);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders all queued up overlays
+//-----------------------------------------------------------------------------
+void COverlayMgr::ClearRenderLists( int nSortGroup )
+{
+ g_OverlayRenderFrameID++;
+ int nNextRenderQueue;
+ for( int i = m_nFirstRenderQueue[nSortGroup]; i != RENDER_QUEUE_INVALID; i = nNextRenderQueue )
+ {
+ RenderQueueInfo_t &renderQueue = m_RenderQueue[i].m_Queue[nSortGroup];
+ nNextRenderQueue = renderQueue.m_nNextRenderQueue;
+
+ // Clean up the render queue for next time...
+ renderQueue.m_nVertexCount = 0;
+ renderQueue.m_nIndexCount = 0;
+ renderQueue.m_hFirstFragment = OVERLAY_FRAGMENT_INVALID;
+ renderQueue.m_nNextRenderQueue = RENDER_QUEUE_INVALID;
+ }
+
+ m_nFirstRenderQueue[nSortGroup] = RENDER_QUEUE_INVALID;
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders all queued up overlays
+//-----------------------------------------------------------------------------
+void COverlayMgr::RenderOverlays( int nSortGroup )
+{
+#ifndef SWDS
+ VPROF_BUDGET( "COverlayMgr::RenderOverlays", VPROF_BUDGETGROUP_OVERLAYS );
+
+ if (r_renderoverlayfragment.GetInt() == 0)
+ {
+ ClearRenderLists( nSortGroup );
+ return;
+ }
+
+ CMatRenderContextPtr pRenderContext( materials );
+
+ bool bWireframeFragments = ( r_overlaywireframe.GetInt() != 0 );
+ if ( bWireframeFragments )
+ {
+ pRenderContext->Bind( g_materialWorldWireframe );
+ }
+
+ // Render sorted by material + lightmap...
+ // Render them in order of their m_nRenderOrder parameter (set in the entity).
+ int iCurrentRenderOrder = 0;
+ int iHighestRenderOrder = 0;
+ bool bLightmappedMaterial = false;
+ int nMaxIndices = pRenderContext->GetMaxIndicesToRender();
+ while ( iCurrentRenderOrder <= iHighestRenderOrder )
+ {
+ int nNextRenderQueue;
+ for( int i = m_nFirstRenderQueue[nSortGroup]; i != RENDER_QUEUE_INVALID; i = nNextRenderQueue )
+ {
+ RenderQueueHead_t &renderQueueHead = m_RenderQueue[i];
+ RenderQueueInfo_t &renderQueue = renderQueueHead.m_Queue[nSortGroup];
+ nNextRenderQueue = renderQueue.m_nNextRenderQueue;
+
+ Assert( renderQueue.m_nVertexCount > 0 );
+
+ int nMaxVertices = pRenderContext->GetMaxVerticesToRender( !bWireframeFragments ? renderQueueHead.m_pMaterial : g_materialWorldWireframe );
+ if ( nMaxVertices == 0 )
+ continue;
+
+ // Run this list for each bind proxy
+ OverlayFragmentHandle_t hStartFragment = renderQueue.m_hFirstFragment;
+ while ( hStartFragment != OVERLAY_FRAGMENT_INVALID )
+ {
+ void *pCurrentBindProxy = m_aOverlays[ m_aFragments[ hStartFragment ].m_iOverlay ].m_pBindProxy;
+
+ IMesh* pMesh = 0; // only init when we actually have something
+ CMeshBuilder meshBuilder;
+ CUtlVectorFixedGrowable<int,256> polyList;
+ int nCurrVertexCount = 0;
+ int nCurrIndexCount = 0;
+ bool bBoundMaterial = false;
+
+ // We just need to make sure there's a unique sort ID for that. Then we bind once per queue
+ OverlayFragmentHandle_t hFragment = hStartFragment;
+ hStartFragment = OVERLAY_FRAGMENT_INVALID;
+
+ for ( ; hFragment != OVERLAY_FRAGMENT_INVALID; hFragment = m_aFragments[hFragment].m_hNextRender )
+ {
+ moverlayfragment_t *pFragment = &m_aFragments[hFragment];
+ moverlay_t *pOverlay = &m_aOverlays[pFragment->m_iOverlay];
+
+ if ( pOverlay->m_pBindProxy != pCurrentBindProxy )
+ {
+ // This is from a different bind proxy
+ if ( hStartFragment == OVERLAY_FRAGMENT_INVALID )
+ {
+ // Start at the first different bind proxy when we rerun the fragment list
+ hStartFragment = hFragment;
+ }
+ continue;
+ }
+
+ // Only render the current render order.
+ int iThisOverlayRenderOrder = pOverlay->m_nRenderOrder;
+ iHighestRenderOrder = max( iThisOverlayRenderOrder, iHighestRenderOrder );
+ if ( iThisOverlayRenderOrder != iCurrentRenderOrder )
+ continue;
+
+ int nVertCount = pFragment->m_aPrimVerts.Count();
+ int nIndexCount = 3 * ( nVertCount - 2 );
+
+ if ( pMesh )
+ {
+ // Would this cause an overflow? Flush!
+ if ( ( ( nCurrVertexCount + nVertCount ) > nMaxVertices ) ||
+ ( ( nCurrIndexCount + nIndexCount ) > nMaxIndices ) )
+ {
+ CIndexBuilder &indexBuilder = meshBuilder;
+ indexBuilder.FastPolygonList( 0, polyList.Base(), polyList.Count() );
+ meshBuilder.End();
+ pMesh->Draw();
+ pMesh = NULL;
+ polyList.RemoveAll();
+ nCurrIndexCount = nCurrVertexCount = 0;
+ }
+ }
+
+ nCurrVertexCount += nVertCount;
+ nCurrIndexCount += nIndexCount;
+
+ const overlayvert_t *pVert = &(pFragment->m_aPrimVerts[0]);
+
+ int iVert;
+ if ( !pMesh ) // have we output any vertices yet? if first verts, init material and meshbuilder
+ {
+ if ( !bWireframeFragments && !bBoundMaterial )
+ {
+ pRenderContext->Bind( renderQueueHead.m_pMaterial, pOverlay->m_pBindProxy /*proxy*/ );
+ pRenderContext->BindLightmapPage( renderQueueHead.m_nLightmapPage );
+ bLightmappedMaterial = renderQueueHead.m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP ) ||
+ renderQueueHead.m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS );
+ bBoundMaterial = true;
+ }
+ // Create the mesh/mesh builder.
+ pMesh = pRenderContext->GetDynamicMesh();
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, MIN( renderQueue.m_nVertexCount, nMaxVertices ),
+ MIN( renderQueue.m_nIndexCount, nMaxIndices ) );
+ }
+
+ if ( bLightmappedMaterial )
+ {
+ float flOffset = ComputeDecalLightmapOffset( pFragment->m_SurfId );
+ for ( iVert = 0; iVert < nVertCount; ++iVert, ++pVert )
+ {
+ unsigned char nAlpha = FastFToC( pVert->flAlpha );
+ meshBuilder.Position3fv( pVert->pos.Base() );
+ meshBuilder.Normal3fv( pVert->normal.Base() );
+ meshBuilder.Color4ub( 255, 255, 255, nAlpha );
+ meshBuilder.TexCoord2fv( 0, pVert->texCoord[0].Base() );
+ meshBuilder.TexCoord2fv( 1, pVert->lightCoord );
+ meshBuilder.TexCoord1f( 2, flOffset );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+ else
+ {
+ for ( iVert = 0; iVert < nVertCount; ++iVert, ++pVert )
+ {
+ unsigned char nAlpha = FastFToC( pVert->flAlpha );
+ meshBuilder.Position3fv( pVert->pos.Base() );
+ meshBuilder.Normal3fv( pVert->normal.Base() );
+ meshBuilder.Color4ub( 255, 255, 255, nAlpha );
+ meshBuilder.TexCoord2fv( 0, pVert->texCoord[0].Base() );
+ meshBuilder.TexCoord2fv( 1, pVert->lightCoord );
+ meshBuilder.TexCoord2fv( 2, pVert->texCoord[1].Base() );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+ polyList.AddToTail( nVertCount );
+ }
+
+ if (pMesh)
+ {
+ CIndexBuilder &indexBuilder = meshBuilder;
+ indexBuilder.FastPolygonList( 0, polyList.Base(), polyList.Count() );
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+ }
+ }
+
+ ++iCurrentRenderOrder;
+ }
+#endif
+}
+
+void COverlayMgr::SetOverlayBindProxy( int iOverlayID, void *pBindProxy )
+{
+ moverlay_t *pOverlay = GetOverlay( iOverlayID );
+ if ( pOverlay )
+ pOverlay->m_pBindProxy = pBindProxy;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool COverlayMgr::Surf_PreClipFragment( moverlay_t *pOverlay, moverlayfragment_t &overlayFrag,
+ SurfaceHandle_t surfID, moverlayfragment_t &surfaceFrag )
+{
+ MEM_ALLOC_CREDIT();
+ // Convert the overlay uv points to overlay plane points.
+ overlayFrag.m_aPrimVerts.SetCount( 4 );
+ for( int iVert = 0; iVert < 4; ++iVert )
+ {
+ Overlay_OverlayUVToOverlayPlane( pOverlay->m_vecOrigin, pOverlay->m_vecBasis[0],
+ pOverlay->m_vecBasis[1], pOverlay->m_vecUVPoints[iVert],
+ overlayFrag.m_aPrimVerts[iVert].pos );
+ }
+
+ // Overlay texture coordinates.
+ InitTexCoords( pOverlay, overlayFrag );
+
+ // Surface
+ int nVertCount = surfaceFrag.m_aPrimVerts.Count();
+ for ( int iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ // Position.
+ Overlay_WorldToOverlayPlane( pOverlay->m_vecOrigin, pOverlay->m_vecBasis[2],
+ surfaceFrag.m_aPrimVerts[iVert].pos, surfaceFrag.m_aPrimVerts[iVert].pos );
+ }
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void COverlayMgr::Surf_PostClipFragment( moverlay_t *pOverlay, moverlayfragment_t &overlayFrag,
+ SurfaceHandle_t surfID )
+{
+#ifndef SWDS
+ // Get fragment vertex count.
+ int nVertCount = overlayFrag.m_aPrimVerts.Count();
+
+ if ( nVertCount == 0 )
+ return;
+
+ // Create fragment.
+ OverlayFragmentHandle_t hFragment = AddFragmentToFragmentList( nVertCount );
+ moverlayfragment_t *pFragment = GetOverlayFragment( hFragment );
+
+ // Get surface context.
+ SurfaceCtx_t ctx;
+ SurfSetupSurfaceContext( ctx, surfID );
+
+ pFragment->m_iOverlay = pOverlay->m_nId;
+ pFragment->m_SurfId = surfID;
+
+ const Vector &vNormal = MSurf_Plane( surfID ).normal;
+
+ moverlayfragment_t origOverlay;
+ origOverlay.m_aPrimVerts.SetSize( 4 );
+ for ( int iPoint = 0; iPoint < 4; ++iPoint )
+ {
+ Overlay_OverlayUVToOverlayPlane( pOverlay->m_vecOrigin, pOverlay->m_vecBasis[0],
+ pOverlay->m_vecBasis[1], pOverlay->m_vecUVPoints[iPoint],
+ origOverlay.m_aPrimVerts[iPoint].pos );
+ }
+ InitTexCoords( pOverlay, origOverlay );
+
+ for ( int iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ Vector2D vecUV;
+ PointInQuadToBarycentric( origOverlay.m_aPrimVerts[0].pos,
+ origOverlay.m_aPrimVerts[3].pos,
+ origOverlay.m_aPrimVerts[2].pos,
+ origOverlay.m_aPrimVerts[1].pos,
+ overlayFrag.m_aPrimVerts[iVert].pos, vecUV );
+
+ Overlay_OverlayPlaneToWorld( pOverlay->m_vecBasis[2], surfID,
+ overlayFrag.m_aPrimVerts[iVert].pos,
+ pFragment->m_aPrimVerts[iVert].pos );
+
+ // Texture coordinates.
+ Vector2D vecTexCoord;
+ for ( int iTexCoord=0; iTexCoord < NUM_OVERLAY_TEXCOORDS; iTexCoord++ )
+ {
+ TexCoordInQuadFromBarycentric( origOverlay.m_aPrimVerts[0].texCoord[iTexCoord], origOverlay.m_aPrimVerts[3].texCoord[iTexCoord],
+ origOverlay.m_aPrimVerts[2].texCoord[iTexCoord], origOverlay.m_aPrimVerts[1].texCoord[iTexCoord],
+ vecUV, vecTexCoord );
+
+ pFragment->m_aPrimVerts[iVert].texCoord[iTexCoord][0] = vecTexCoord.x;
+ pFragment->m_aPrimVerts[iVert].texCoord[iTexCoord][1] = vecTexCoord.y;
+ }
+
+ // Normals : FIXME this isn't an interpolated normal.
+ pFragment->m_aPrimVerts[iVert].normal = vNormal;
+
+ // Lightmap coordinates.
+ Vector2D uv;
+ SurfComputeLightmapCoordinate( ctx, surfID, pFragment->m_aPrimVerts[iVert].pos, uv );
+ pFragment->m_aPrimVerts[iVert].lightCoord[0] = uv.x;
+ pFragment->m_aPrimVerts[iVert].lightCoord[1] = uv.y;
+
+ // Push -just- off the surface to avoid z-clipping errors.
+ pFragment->m_aPrimVerts[iVert].pos += vNormal * OVERLAY_AVOID_FLICKER_NORMAL_OFFSET;
+ }
+
+ // Create the sort ID for this fragment
+ const MaterialSystem_SortInfo_t &sortInfo = materialSortInfoArray[MSurf_MaterialSortID( surfID )];
+ mtexinfo_t *pTexInfo = &host_state.worldbrush->texinfo[pOverlay->m_nTexInfo];
+ pFragment->m_nMaterialSortID = GetMaterialSortID( pTexInfo->material, sortInfo.lightmapPageID );
+
+ // Add to list of fragments for this overlay
+ MEM_ALLOC_CREDIT();
+ OverlayFragmentList_t i = m_OverlayFragments.Alloc( true );
+ m_OverlayFragments[i] = hFragment;
+ m_OverlayFragments.LinkBefore( pOverlay->m_hFirstFragment, i );
+ pOverlay->m_hFirstFragment = i;
+
+ // Add to list of fragments for this surface
+ // NOTE: Store them in *reverse* order so that when we pull them off for
+ // rendering, we can do *that* in reverse order too? Reduces the amount of iteration necessary
+ // Therefore, we need to add to the head of the list
+ m_aFragments.LinkBefore( MSurf_OverlayFragmentList( surfID ), hFragment );
+ MSurf_OverlayFragmentList( surfID ) = hFragment;
+#endif // !SWDS
+}
+
+
+//-----------------------------------------------------------------------------
+// Clips an overlay to a surface
+//-----------------------------------------------------------------------------
+void COverlayMgr::Surf_ClipFragment( moverlay_t *pOverlay, moverlayfragment_t &overlayFrag,
+ SurfaceHandle_t surfID, moverlayfragment_t &surfaceFrag )
+{
+ MEM_ALLOC_CREDIT();
+ // Create the clip planes.
+ CUtlVector<cplane_t> m_ClipPlanes;
+ BuildClipPlanes( surfID, surfaceFrag, pOverlay->m_vecBasis[2], m_ClipPlanes );
+
+ // Copy the overlay fragment (initial clipped fragment).
+ moverlayfragment_t *pClippedFrag = CopyTempFragment( &overlayFrag );
+
+ for( int iPlane = 0; iPlane < m_ClipPlanes.Count(); ++iPlane )
+ {
+ moverlayfragment_t *pFront = NULL, *pBack = NULL;
+ DoClipFragment( pClippedFrag, &m_ClipPlanes[iPlane], &pFront, &pBack );
+ DestroyTempFragment( pClippedFrag );
+ pClippedFrag = NULL;
+
+ // Keep the backside and continue clipping.
+ if ( pBack )
+ {
+ pClippedFrag = pBack;
+ }
+
+ if ( pFront )
+ {
+ DestroyTempFragment( pFront );
+ }
+ }
+
+ m_ClipPlanes.Purge();
+
+ // Copy the clipped polygon back to the overlay frag.
+ overlayFrag.m_aPrimVerts.RemoveAll();
+ if ( pClippedFrag )
+ {
+ overlayFrag.m_aPrimVerts.SetCount( pClippedFrag->m_aPrimVerts.Count() );
+ for ( int iVert = 0; iVert < pClippedFrag->m_aPrimVerts.Count(); ++iVert )
+ {
+ overlayFrag.m_aPrimVerts[iVert].pos = pClippedFrag->m_aPrimVerts[iVert].pos;
+ memcpy( overlayFrag.m_aPrimVerts[iVert].texCoord, pClippedFrag->m_aPrimVerts[iVert].texCoord, sizeof( overlayFrag.m_aPrimVerts[iVert].texCoord ) );
+ }
+ }
+
+ DestroyTempFragment( pClippedFrag );
+}
+
+inline float TriangleArea( const Vector &v0, const Vector &v1, const Vector &v2 )
+{
+ Vector vecEdge0, vecEdge1, vecCross;
+ VectorSubtract( v1, v0, vecEdge0 );
+ VectorSubtract( v2, v0, vecEdge1 );
+ CrossProduct( vecEdge0, vecEdge1, vecCross );
+ return ( VectorLength( vecCross ) * 0.5f );
+}
+
+//-----------------------------------------------------------------------------
+// Creates overlay fragments for a particular surface
+//-----------------------------------------------------------------------------
+void COverlayMgr::Surf_CreateFragments( moverlay_t *pOverlay, SurfaceHandle_t surfID )
+{
+ moverlayfragment_t overlayFrag, surfaceFrag;
+
+ // The faces get fan tesselated into triangles when rendered - do the same to
+ // create the fragments!
+ int iFirstVert = MSurf_FirstVertIndex( surfID );
+
+ int nSurfTriangleCount = MSurf_VertCount( surfID ) - 2;
+ for( int iTri = 0; iTri < nSurfTriangleCount; ++iTri )
+ {
+ // 3 Points in a triangle.
+ surfaceFrag.m_aPrimVerts.SetCount( 3 );
+
+ int iVert = host_state.worldbrush->vertindices[(iFirstVert)];
+ mvertex_t *pVert = &host_state.worldbrush->vertexes[iVert];
+ surfaceFrag.m_aPrimVerts[0].pos = pVert->position;
+
+ iVert = host_state.worldbrush->vertindices[(iFirstVert+iTri+1)];
+ pVert = &host_state.worldbrush->vertexes[iVert];
+ surfaceFrag.m_aPrimVerts[1].pos = pVert->position;
+
+ iVert = host_state.worldbrush->vertindices[(iFirstVert+iTri+2)];
+ pVert = &host_state.worldbrush->vertexes[iVert];
+ surfaceFrag.m_aPrimVerts[2].pos = pVert->position;
+
+ if ( TriangleArea( surfaceFrag.m_aPrimVerts[0].pos, surfaceFrag.m_aPrimVerts[1].pos, surfaceFrag.m_aPrimVerts[2].pos ) > 1.0f )
+ {
+ if ( Surf_PreClipFragment( pOverlay, overlayFrag, surfID, surfaceFrag ) )
+ {
+ Surf_ClipFragment( pOverlay, overlayFrag, surfID, surfaceFrag );
+ Surf_PostClipFragment( pOverlay, overlayFrag, surfID );
+ }
+ }
+
+ // Clean up!
+ surfaceFrag.m_aPrimVerts.RemoveAll();
+ overlayFrag.m_aPrimVerts.RemoveAll();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Creates fragments from the overlays loaded in from file
+//-----------------------------------------------------------------------------
+void COverlayMgr::CreateFragments( void )
+{
+ int nOverlayCount = m_aOverlays.Count();
+ for ( int iOverlay = 0; iOverlay < nOverlayCount; ++iOverlay )
+ {
+ moverlay_t *pOverlay = &m_aOverlays.Element( iOverlay );
+ int nFaceCount = pOverlay->m_aFaces.Count();
+ if ( nFaceCount == 0 )
+ continue;
+
+ // Build the overlay basis.
+ bool bFlip = ( pOverlay->m_vecUVPoints[3].z == 1.0f );
+ pOverlay->m_vecUVPoints[3].z = 0.0f;
+ Overlay_BuildBasis( pOverlay->m_vecBasis[2], pOverlay->m_vecBasis[0], pOverlay->m_vecBasis[1], bFlip );
+
+ // Clip against each face in the face list.
+ for( int iFace = 0; iFace < nFaceCount; ++iFace )
+ {
+ SurfaceHandle_t surfID = pOverlay->m_aFaces[iFace];
+
+ if ( SurfaceHasDispInfo( surfID ) )
+ {
+ Disp_CreateFragments( pOverlay, surfID );
+ }
+ else
+ {
+ Surf_CreateFragments( pOverlay, surfID );
+ }
+ }
+ }
+
+ // Overlay checking!
+ for ( int iOverlay = 0; iOverlay < nOverlayCount; ++iOverlay )
+ {
+ moverlay_t *pOverlay = &m_aOverlays.Element( iOverlay );
+ int hFrag = pOverlay->m_hFirstFragment;
+ while ( hFrag != OVERLAY_FRAGMENT_INVALID )
+ {
+ int iFrag = m_OverlayFragments[hFrag];
+ moverlayfragment_t *pFrag = &m_aFragments[iFrag];
+ int nVertCount = pFrag->m_aPrimVerts.Count();
+ for ( int iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ overlayvert_t *pVert = &pFrag->m_aPrimVerts[iVert];
+ if ( !pVert->pos.IsValid() )
+ {
+ Assert( 0 );
+ mtexinfo_t *pTexInfo = &host_state.worldbrush->texinfo[pOverlay->m_nTexInfo];
+ DevMsg( 1, "Bad overlay vert - %d at (%f, %f, %f) with material '%s'\n", iOverlay,
+ pOverlay->m_vecOrigin.x, pOverlay->m_vecOrigin.y, pOverlay->m_vecOrigin.z,
+ ( pTexInfo && pTexInfo->material ) ? pTexInfo->material->GetName() : "" );
+ }
+
+ if ( !pVert->normal.IsValid() )
+ {
+ Assert( 0 );
+ mtexinfo_t *pTexInfo = &host_state.worldbrush->texinfo[pOverlay->m_nTexInfo];
+ DevMsg( 1, "Bad overlay normal - %d at (%f, %f, %f) with material '%s'\n", iOverlay,
+ pOverlay->m_vecOrigin.x, pOverlay->m_vecOrigin.y, pOverlay->m_vecOrigin.z,
+ ( pTexInfo && pTexInfo->material ) ? pTexInfo->material->GetName() : "" );
+ }
+
+ if ( !pVert->texCoord[0].IsValid() || !pVert->texCoord[1].IsValid() )
+ {
+ Assert( 0 );
+ mtexinfo_t *pTexInfo = &host_state.worldbrush->texinfo[pOverlay->m_nTexInfo];
+ DevMsg( 1, "Bad overlay texture coords - %d at (%f, %f, %f) with material '%s'\n", iOverlay,
+ pOverlay->m_vecOrigin.x, pOverlay->m_vecOrigin.y, pOverlay->m_vecOrigin.z,
+ ( pTexInfo && pTexInfo->material ) ? pTexInfo->material->GetName() : "" );
+ }
+ }
+ hFrag = m_OverlayFragments.Next( hFrag );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void COverlayMgr::ReSortMaterials( void )
+{
+#ifndef SWDS
+ // Clear the old render queue.
+ m_RenderQueue.Purge();
+ for ( int iSort = 0; iSort < MAX_MAT_SORT_GROUPS; ++iSort )
+ {
+ m_nFirstRenderQueue[iSort] = RENDER_QUEUE_INVALID;
+ }
+
+ // Update all fragments.
+ int nOverlayCount = m_aOverlays.Count();
+ for ( int iOverlay = 0; iOverlay < nOverlayCount; ++iOverlay )
+ {
+ moverlay_t *pOverlay = &m_aOverlays.Element( iOverlay );
+ if ( !pOverlay )
+ continue;
+
+ mtexinfo_t *pTexInfo = &host_state.worldbrush->texinfo[pOverlay->m_nTexInfo];
+ if ( !pTexInfo )
+ continue;
+
+ int hFrag = pOverlay->m_hFirstFragment;
+ while ( hFrag != OVERLAY_FRAGMENT_INVALID )
+ {
+ int iFrag = m_OverlayFragments[hFrag];
+ moverlayfragment_t *pFrag = &m_aFragments[iFrag];
+ if ( pFrag )
+ {
+ const MaterialSystem_SortInfo_t &sortInfo = materialSortInfoArray[MSurf_MaterialSortID( pFrag->m_SurfId )];
+ pFrag->m_nMaterialSortID = GetMaterialSortID( pTexInfo->material, sortInfo.lightmapPageID );
+
+ // Get surface context.
+ SurfaceCtx_t ctx;
+ SurfSetupSurfaceContext( ctx, pFrag->m_SurfId );
+
+ int nVertCount = pFrag->m_aPrimVerts.Count();
+ for ( int iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ // Lightmap coordinates.
+ Vector2D uv;
+ SurfComputeLightmapCoordinate( ctx, pFrag->m_SurfId, pFrag->m_aPrimVerts[iVert].pos, uv );
+ pFrag->m_aPrimVerts[iVert].lightCoord[0] = uv.x;
+ pFrag->m_aPrimVerts[iVert].lightCoord[1] = uv.y;
+ }
+ }
+ hFrag = m_OverlayFragments.Next( hFrag );
+ }
+ }
+#endif // !SWDS
+}
+
+//-----------------------------------------------------------------------------
+// Loads overlays from the lump
+//-----------------------------------------------------------------------------
+bool COverlayMgr::LoadOverlays( )
+{
+ CMapLoadHelper lh( LUMP_OVERLAYS );
+ CMapLoadHelper lh2( LUMP_WATEROVERLAYS );
+ CMapLoadHelper lhOverlayFades( LUMP_OVERLAY_FADES );
+
+ doverlay_t *pOverlayIn;
+ dwateroverlay_t *pWaterOverlayIn;
+
+ pOverlayIn = ( doverlay_t* )lh.LumpBase();
+ if ( lh.LumpSize() % sizeof( doverlay_t ) )
+ return false;
+
+ pWaterOverlayIn = ( dwateroverlay_t* )lh2.LumpBase();
+ if ( lh2.LumpSize() % sizeof( dwateroverlay_t ) )
+ return false;
+
+ // Fade distances are in a parallel lump
+ doverlayfade_t *pOverlayFadesIn = (doverlayfade_t *)lhOverlayFades.LumpBase();
+ if ( lhOverlayFades.LumpSize() % sizeof( doverlayfade_t ) )
+ return false;
+
+ int nOverlayCount = lh.LumpSize() / sizeof( doverlay_t );
+ int nWaterOverlayCount = lh2.LumpSize() / sizeof( dwateroverlay_t );
+
+ // Memory allocation!
+ m_aOverlays.SetSize( nOverlayCount + nWaterOverlayCount );
+
+ for( int iOverlay = 0; iOverlay < nOverlayCount; ++iOverlay, ++pOverlayIn )
+ {
+ moverlay_t *pOverlayOut = &m_aOverlays.Element( iOverlay );
+
+ pOverlayOut->m_nId = iOverlay;
+ pOverlayOut->m_nTexInfo = pOverlayIn->nTexInfo;
+ pOverlayOut->m_nRenderOrder = pOverlayIn->GetRenderOrder();
+ if ( pOverlayOut->m_nRenderOrder >= OVERLAY_NUM_RENDER_ORDERS )
+ Error( "COverlayMgr::LoadOverlays: invalid render order (%d) for an overlay.", pOverlayOut->m_nRenderOrder );
+
+ pOverlayOut->m_flU[0] = pOverlayIn->flU[0];
+ pOverlayOut->m_flU[1] = pOverlayIn->flU[1];
+ pOverlayOut->m_flV[0] = pOverlayIn->flV[0];
+ pOverlayOut->m_flV[1] = pOverlayIn->flV[1];
+
+ if ( pOverlayFadesIn )
+ {
+ pOverlayOut->m_flFadeDistMinSq = pOverlayFadesIn->flFadeDistMinSq;
+ pOverlayOut->m_flFadeDistMaxSq = pOverlayFadesIn->flFadeDistMaxSq;
+ pOverlayOut->m_flInvFadeRangeSq = 1.0f / ( pOverlayFadesIn->flFadeDistMaxSq - pOverlayFadesIn->flFadeDistMinSq );
+
+ pOverlayFadesIn++;
+ }
+ else
+ {
+ pOverlayOut->m_flFadeDistMinSq = -1.0f;
+ pOverlayOut->m_flFadeDistMaxSq = 0;
+ pOverlayOut->m_flInvFadeRangeSq = 1.0f;
+ }
+
+ VectorCopy( pOverlayIn->vecOrigin, pOverlayOut->m_vecOrigin );
+
+ VectorCopy( pOverlayIn->vecUVPoints[0], pOverlayOut->m_vecUVPoints[0] );
+ VectorCopy( pOverlayIn->vecUVPoints[1], pOverlayOut->m_vecUVPoints[1] );
+ VectorCopy( pOverlayIn->vecUVPoints[2], pOverlayOut->m_vecUVPoints[2] );
+ VectorCopy( pOverlayIn->vecUVPoints[3], pOverlayOut->m_vecUVPoints[3] );
+
+ VectorCopy( pOverlayIn->vecBasisNormal, pOverlayOut->m_vecBasis[2] );
+
+ // Basis U is encoded in the z components of the UVPoints 0, 1, 2
+ pOverlayOut->m_vecBasis[0].x = pOverlayOut->m_vecUVPoints[0].z;
+ pOverlayOut->m_vecBasis[0].y = pOverlayOut->m_vecUVPoints[1].z;
+ pOverlayOut->m_vecBasis[0].z = pOverlayOut->m_vecUVPoints[2].z;
+
+ if ( pOverlayOut->m_vecBasis[0].x == 0.0f && pOverlayOut->m_vecBasis[0].y == 0.0f && pOverlayOut->m_vecBasis[0].z == 0.0f )
+ {
+ Warning( "Bad overlay basis at (%f %f %f)!\n", pOverlayOut->m_vecOrigin.x, pOverlayOut->m_vecOrigin.y, pOverlayOut->m_vecOrigin.z );
+ }
+
+ CrossProduct( pOverlayOut->m_vecBasis[2], pOverlayOut->m_vecBasis[0], pOverlayOut->m_vecBasis[1] );
+ VectorNormalize( pOverlayOut->m_vecBasis[1] );
+
+ pOverlayOut->m_vecUVPoints[0].z = 0.0f;
+ pOverlayOut->m_vecUVPoints[1].z = 0.0f;
+ pOverlayOut->m_vecUVPoints[2].z = 0.0f;
+
+ pOverlayOut->m_aFaces.SetSize( pOverlayIn->GetFaceCount() );
+ for( int iFace = 0; iFace < pOverlayIn->GetFaceCount(); ++iFace )
+ {
+ pOverlayOut->m_aFaces[iFace] = SurfaceHandleFromIndex( pOverlayIn->aFaces[iFace], lh.GetMap() );
+ }
+
+ pOverlayOut->m_hFirstFragment = OVERLAY_FRAGMENT_LIST_INVALID;
+ pOverlayOut->m_pBindProxy = NULL;
+ }
+
+ for( int iWaterOverlay = 0; iWaterOverlay < nWaterOverlayCount; ++iWaterOverlay, ++pWaterOverlayIn )
+ {
+ moverlay_t *pOverlayOut = &m_aOverlays.Element( nOverlayCount + iWaterOverlay );
+
+ pOverlayOut->m_nId = nOverlayCount + iWaterOverlay;
+ pOverlayOut->m_nTexInfo = pWaterOverlayIn->nTexInfo;
+ pOverlayOut->m_nRenderOrder = pWaterOverlayIn->GetRenderOrder();
+ if ( pOverlayOut->m_nRenderOrder >= OVERLAY_NUM_RENDER_ORDERS )
+ Error( "COverlayMgr::LoadOverlays: invalid render order (%d) for an overlay.", pOverlayOut->m_nRenderOrder );
+
+ pOverlayOut->m_flU[0] = pWaterOverlayIn->flU[0];
+ pOverlayOut->m_flU[1] = pWaterOverlayIn->flU[1];
+ pOverlayOut->m_flV[0] = pWaterOverlayIn->flV[0];
+ pOverlayOut->m_flV[1] = pWaterOverlayIn->flV[1];
+
+ VectorCopy( pWaterOverlayIn->vecOrigin, pOverlayOut->m_vecOrigin );
+
+ VectorCopy( pWaterOverlayIn->vecUVPoints[0], pOverlayOut->m_vecUVPoints[0] );
+ VectorCopy( pWaterOverlayIn->vecUVPoints[1], pOverlayOut->m_vecUVPoints[1] );
+ VectorCopy( pWaterOverlayIn->vecUVPoints[2], pOverlayOut->m_vecUVPoints[2] );
+ VectorCopy( pWaterOverlayIn->vecUVPoints[3], pOverlayOut->m_vecUVPoints[3] );
+
+ VectorCopy( pWaterOverlayIn->vecBasisNormal, pOverlayOut->m_vecBasis[2] );
+
+ // Basis U is encoded in the z components of the UVPoints 0, 1, 2
+ pOverlayOut->m_vecBasis[0].x = pOverlayOut->m_vecUVPoints[0].z;
+ pOverlayOut->m_vecBasis[0].y = pOverlayOut->m_vecUVPoints[1].z;
+ pOverlayOut->m_vecBasis[0].z = pOverlayOut->m_vecUVPoints[2].z;
+
+ if ( pOverlayOut->m_vecBasis[0].x == 0.0f && pOverlayOut->m_vecBasis[0].y == 0.0f && pOverlayOut->m_vecBasis[0].z == 0.0f )
+ {
+ Warning( "Bad overlay basis at (%f %f %f)!\n", pOverlayOut->m_vecOrigin.x, pOverlayOut->m_vecOrigin.y, pOverlayOut->m_vecOrigin.z );
+ }
+
+ CrossProduct( pOverlayOut->m_vecBasis[2], pOverlayOut->m_vecBasis[0], pOverlayOut->m_vecBasis[1] );
+ VectorNormalize( pOverlayOut->m_vecBasis[1] );
+
+ pOverlayOut->m_vecUVPoints[0].z = 0.0f;
+ pOverlayOut->m_vecUVPoints[1].z = 0.0f;
+ pOverlayOut->m_vecUVPoints[2].z = 0.0f;
+
+ pOverlayOut->m_aFaces.SetSize( pWaterOverlayIn->GetFaceCount() );
+ for( int iFace = 0; iFace < pWaterOverlayIn->GetFaceCount(); ++iFace )
+ {
+ pOverlayOut->m_aFaces[iFace] = SurfaceHandleFromIndex( pWaterOverlayIn->aFaces[iFace], lh2.GetMap() );
+ }
+
+ pOverlayOut->m_hFirstFragment = OVERLAY_FRAGMENT_LIST_INVALID;
+ pOverlayOut->m_pBindProxy = NULL;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void COverlayMgr::Disp_CreateFragments( moverlay_t *pOverlay, SurfaceHandle_t surfID )
+{
+ OverlayFragmentVector_t aDispFragments;
+
+ if ( Disp_PreClipFragment( pOverlay, aDispFragments, surfID ) )
+ {
+ IDispInfo *pIDisp = MSurf_DispInfo( surfID );
+ CDispInfo *pDisp = static_cast<CDispInfo*>( pIDisp );
+ if ( pDisp )
+ {
+ Disp_ClipFragment( pDisp, aDispFragments );
+ Disp_PostClipFragment( pDisp, &pDisp->m_MeshReader, pOverlay, aDispFragments, surfID );
+ }
+ }
+
+ for ( int i = aDispFragments.Count(); --i >= 0; )
+ {
+ DestroyTempFragment( aDispFragments[i] );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool COverlayMgr::Disp_PreClipFragment( moverlay_t *pOverlay, OverlayFragmentVector_t &aDispFragments,
+ SurfaceHandle_t surfID )
+{
+ MEM_ALLOC_CREDIT();
+
+ // The faces are not tesselated when they are displaced faces.
+ int iFirstVert = MSurf_FirstVertIndex( surfID );
+
+ // Displaced faces are quads.
+ moverlayfragment_t surfaceFrag;
+ surfaceFrag.m_aPrimVerts.SetCount( 4 );
+ for( int iVert = 0; iVert < 4; ++iVert )
+ {
+ int iVertex = host_state.worldbrush->vertindices[(iFirstVert+iVert)];
+ mvertex_t *pVert = &host_state.worldbrush->vertexes[iVertex];
+ surfaceFrag.m_aPrimVerts[iVert].pos = pVert->position;
+ }
+
+ // Setup the base fragment to be clipped by the base surface previous to the
+ // displaced surface.
+ moverlayfragment_t overlayFrag;
+ if ( !Surf_PreClipFragment( pOverlay, overlayFrag, surfID, surfaceFrag ) )
+ return false;
+
+ Surf_ClipFragment( pOverlay, overlayFrag, surfID, surfaceFrag );
+
+ // Get fragment vertex count.
+ int nVertCount = overlayFrag.m_aPrimVerts.Count();
+ if ( nVertCount == 0 )
+ return false;
+
+ // Setup
+ moverlayfragment_t *pFragment = CopyTempFragment( &overlayFrag );
+ aDispFragments.AddToTail( pFragment );
+
+ IDispInfo *pIDispInfo = MSurf_DispInfo( surfID );
+ CDispInfo *pDispInfo = static_cast<CDispInfo*>( pIDispInfo );
+ int iPointStart = pDispInfo->m_iPointStart;
+
+ Vector2D vecTmpUV;
+ for ( int iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ PointInQuadToBarycentric( surfaceFrag.m_aPrimVerts[iPointStart].pos,
+ surfaceFrag.m_aPrimVerts[(iPointStart+3)%4].pos,
+ surfaceFrag.m_aPrimVerts[(iPointStart+2)%4].pos,
+ surfaceFrag.m_aPrimVerts[(iPointStart+1)%4].pos,
+ overlayFrag.m_aPrimVerts[iVert].pos,
+ vecTmpUV );
+ if ( !vecTmpUV.IsValid() )
+ {
+ mtexinfo_t *pTexInfo = &host_state.worldbrush->texinfo[pOverlay->m_nTexInfo];
+ DevWarning( 1, "Bad overlay geometry at %s with material '%s'\n", VecToString(pOverlay->m_vecOrigin),
+ ( pTexInfo && pTexInfo->material ) ? pTexInfo->material->GetName() : "" );
+ return false;
+ }
+
+ pFragment->m_aPrimVerts[iVert].pos.x = vecTmpUV.x;
+ pFragment->m_aPrimVerts[iVert].pos.y = vecTmpUV.y;
+ pFragment->m_aPrimVerts[iVert].pos.z = 0.0f;
+ }
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void COverlayMgr::Disp_PostClipFragment( CDispInfo *pDisp, CMeshReader *pReader, moverlay_t *pOverlay,
+ OverlayFragmentVector_t &aDispFragments, SurfaceHandle_t surfID )
+{
+#ifndef SWDS
+ if ( aDispFragments.Count() == 0 )
+ return;
+
+ // Get surface context.
+ SurfaceCtx_t ctx;
+ SurfSetupSurfaceContext( ctx, surfID );
+
+ // The faces are not tesselated when they are displaced faces.
+ int iFirstVert = MSurf_FirstVertIndex( surfID );
+
+ // Displaced faces are quads.
+ moverlayfragment_t surfaceFrag;
+ surfaceFrag.m_aPrimVerts.SetCount( 4 );
+ for( int iVert = 0; iVert < 4; ++iVert )
+ {
+ int iVertex = host_state.worldbrush->vertindices[(iFirstVert+iVert)];
+ mvertex_t *pVert = &host_state.worldbrush->vertexes[iVertex];
+ surfaceFrag.m_aPrimVerts[iVert].pos = pVert->position;
+ }
+
+ Vector2D lightCoords[4];
+ int nInterval = pDisp->GetSideLength();
+
+ pReader->TexCoord2f( 0, DISP_LMCOORDS_STAGE, lightCoords[0].x, lightCoords[0].y );
+ pReader->TexCoord2f( nInterval - 1, DISP_LMCOORDS_STAGE, lightCoords[1].x, lightCoords[1].y );
+ pReader->TexCoord2f( ( nInterval * nInterval ) - 1, DISP_LMCOORDS_STAGE, lightCoords[2].x, lightCoords[2].y );
+ pReader->TexCoord2f( nInterval * ( nInterval - 1 ), DISP_LMCOORDS_STAGE, lightCoords[3].x, lightCoords[3].y );
+
+ // Get the number of displacement fragments.
+ int nFragCount = aDispFragments.Count();
+ for ( int iFrag = 0; iFrag < nFragCount; ++iFrag )
+ {
+ moverlayfragment_t *pDispFragment = aDispFragments[iFrag];
+ if ( !pDispFragment )
+ continue;
+
+ int nVertCount = pDispFragment->m_aPrimVerts.Count();
+ if ( nVertCount < 3 )
+ continue;
+
+ // Create fragment.
+ OverlayFragmentHandle_t hFragment = AddFragmentToFragmentList( nVertCount );
+ moverlayfragment_t *pFragment = GetOverlayFragment( hFragment );
+
+ pFragment->m_iOverlay = pOverlay->m_nId;
+ pFragment->m_SurfId = surfID;
+
+ Vector2D vecTmpUV;
+ Vector vecTmp;
+ for ( int iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ vecTmpUV.x = pDispFragment->m_aPrimVerts[iVert].pos.x;
+ vecTmpUV.y = pDispFragment->m_aPrimVerts[iVert].pos.y;
+
+ vecTmpUV.x = clamp( vecTmpUV.x, 0.0f, 1.0f );
+ vecTmpUV.y = clamp( vecTmpUV.y, 0.0f, 1.0f );
+
+ Overlay_DispUVToWorld( pDisp, pReader, vecTmpUV, pFragment->m_aPrimVerts[iVert].pos, surfaceFrag );
+
+ // Texture coordinates.
+ pFragment->m_aPrimVerts[iVert].texCoord[0] = pDispFragment->m_aPrimVerts[iVert].texCoord[0];
+ pFragment->m_aPrimVerts[iVert].texCoord[1] = pDispFragment->m_aPrimVerts[iVert].texCoord[1];
+
+ // Lightmap coordinates.
+ Vector2D uv;
+ TexCoordInQuadFromBarycentric( lightCoords[0], lightCoords[1], lightCoords[2], lightCoords[3], vecTmpUV, uv );
+ pFragment->m_aPrimVerts[iVert].lightCoord[0] = uv.x;
+ pFragment->m_aPrimVerts[iVert].lightCoord[1] = uv.y;
+ }
+
+ // Calculate the normal for this fragment.
+ Vector vecFragmentNormal;
+ Vector vecEdges[2];
+ VectorSubtract( pFragment->m_aPrimVerts[1].pos, pFragment->m_aPrimVerts[0].pos, vecEdges[0] );
+ VectorSubtract( pFragment->m_aPrimVerts[2].pos, pFragment->m_aPrimVerts[0].pos, vecEdges[1] );
+ vecFragmentNormal = CrossProduct( vecEdges[1], vecEdges[0] );
+ if ( VectorNormalize( vecFragmentNormal ) < 1e-3 )
+ {
+ vecFragmentNormal.Init( -vecEdges[1].y, vecEdges[1].x, 0.0f );
+ if ( VectorNormalize( vecFragmentNormal ) < 1e-3 )
+ {
+ vecFragmentNormal.Init( 0.0f, 0.0f, 1.0f );
+ }
+ }
+ for ( int iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ pFragment->m_aPrimVerts[iVert].normal = vecFragmentNormal;
+ }
+
+ // Create the sort ID for this fragment
+ const MaterialSystem_SortInfo_t &sortInfo = materialSortInfoArray[MSurf_MaterialSortID( surfID )];
+ mtexinfo_t *pTexInfo = &host_state.worldbrush->texinfo[pOverlay->m_nTexInfo];
+ pFragment->m_nMaterialSortID = GetMaterialSortID( pTexInfo->material, sortInfo.lightmapPageID );
+
+ // Add to list of fragments for this overlay
+ MEM_ALLOC_CREDIT();
+
+ OverlayFragmentList_t i = m_OverlayFragments.Alloc( true );
+ m_OverlayFragments[i] = hFragment;
+ m_OverlayFragments.LinkBefore( pOverlay->m_hFirstFragment, i );
+ pOverlay->m_hFirstFragment = i;
+
+ // Add to list of fragments for this surface
+ // NOTE: Store them in *reverse* order so that when we pull them off for
+ // rendering, we can do *that* in reverse order too? Reduces the amount of iteration necessary
+ // Therefore, we need to add to the head of the list
+ m_aFragments.LinkBefore( MSurf_OverlayFragmentList( surfID ), hFragment );
+ MSurf_OverlayFragmentList( surfID ) = hFragment;
+ }
+#endif // !SWDS
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void COverlayMgr::Disp_ClipFragment( CDispInfo *pDisp, OverlayFragmentVector_t &aDispFragments )
+{
+ cplane_t clipPlane;
+
+ // Cache the displacement interval.
+ const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo();
+ int nInterval = ( 1 << pPowerInfo->GetPower() );
+
+ // Displacement-space clipping in V.
+ clipPlane.normal.Init( 1.0f, 0.0f, 0.0f );
+ Disp_DoClip( pDisp, aDispFragments, clipPlane, 1.0f, nInterval, 1, nInterval, 1 );
+
+ // Displacement-space clipping in U.
+ clipPlane.normal.Init( 0.0f, 1.0f, 0.0f );
+ Disp_DoClip( pDisp, aDispFragments, clipPlane, 1.0f, nInterval, 1, nInterval, 1 );
+
+ // Displacement-space clipping UV from top-left to bottom-right.
+ clipPlane.normal.Init( 0.707f, 0.707f, 0.0f ); // 45 degrees
+ Disp_DoClip( pDisp, aDispFragments, clipPlane, 0.707f, nInterval, 2, ( nInterval * 2 - 1 ), 2 );
+
+ // Displacement-space clipping UV from bottom-left to top-right.
+ clipPlane.normal.Init( -0.707f, 0.707f, 0.0f ); // 135 degrees
+ Disp_DoClip( pDisp, aDispFragments, clipPlane, 0.707f, nInterval, -( nInterval - 2 ), ( nInterval - 1 ), 2 );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void COverlayMgr::Disp_DoClip( CDispInfo *pDisp, OverlayFragmentVector_t &aDispFragments, cplane_t &clipPlane,
+ float clipDistStart, int nInterval,
+ int nLoopStart, int nLoopEnd, int nLoopInc )
+{
+ // Setup interval information.
+ float flInterval = static_cast<float>( nInterval );
+ float flOOInterval = 1.0f / flInterval;
+
+ // Holds the current set of clipped faces.
+ OverlayFragmentVector_t aClippedFragments;
+
+ for ( int iInterval = nLoopStart; iInterval < nLoopEnd; iInterval += nLoopInc )
+ {
+ // Copy the current list to clipped face list.
+ aClippedFragments.CopyArray( aDispFragments.Base(), aDispFragments.Count() );
+ aDispFragments.Purge();
+
+ // Clip in V.
+ int nFragCount = aClippedFragments.Count();
+ for ( int iFrag = 0; iFrag < nFragCount; iFrag++ )
+ {
+ moverlayfragment_t *pClipFrag = aClippedFragments[iFrag];
+ if ( pClipFrag )
+ {
+ moverlayfragment_t *pFront = NULL, *pBack = NULL;
+
+ clipPlane.dist = clipDistStart * ( ( float )iInterval * flOOInterval );
+ DoClipFragment( pClipFrag, &clipPlane, &pFront, &pBack );
+ DestroyTempFragment( pClipFrag );
+ pClipFrag = NULL;
+
+ if ( pFront )
+ {
+ aDispFragments.AddToTail( pFront );
+ }
+
+ if ( pBack )
+ {
+ aDispFragments.AddToTail( pBack );
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void COverlayMgr::InitTexCoords( moverlay_t *pOverlay, moverlayfragment_t &overlayFrag )
+{
+ // Overlay texture coordinates.
+ overlayFrag.m_aPrimVerts[0].texCoord[0].Init( pOverlay->m_flU[0], pOverlay->m_flV[0] );
+ overlayFrag.m_aPrimVerts[1].texCoord[0].Init( pOverlay->m_flU[0], pOverlay->m_flV[1] );
+ overlayFrag.m_aPrimVerts[2].texCoord[0].Init( pOverlay->m_flU[1], pOverlay->m_flV[1] );
+ overlayFrag.m_aPrimVerts[3].texCoord[0].Init( pOverlay->m_flU[1], pOverlay->m_flV[0] );
+
+ overlayFrag.m_aPrimVerts[0].texCoord[1].Init( 0, 0 );
+ overlayFrag.m_aPrimVerts[1].texCoord[1].Init( 0, 1 );
+ overlayFrag.m_aPrimVerts[2].texCoord[1].Init( 1, 1 );
+ overlayFrag.m_aPrimVerts[3].texCoord[1].Init( 1, 0 );
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void COverlayMgr::DoClipFragment( moverlayfragment_t *pFragment, cplane_t *pClipPlane,
+ moverlayfragment_t **ppFront, moverlayfragment_t **ppBack )
+{
+ const float OVERLAY_EPSILON = 0.0001f;
+
+ // Verify.
+ if ( !pFragment )
+ return;
+
+ float flDists[128] = {};
+ int nSides[128] = {};
+ int nSideCounts[3] = {};
+
+ //
+ // Determine "sidedness" of all the polygon points.
+ //
+ nSideCounts[0] = nSideCounts[1] = nSideCounts[2] = 0;
+ int iVert = 0;
+ for ( ; iVert < pFragment->m_aPrimVerts.Count(); ++iVert )
+ {
+ flDists[iVert] = pClipPlane->normal.Dot( pFragment->m_aPrimVerts[iVert].pos ) - pClipPlane->dist;
+
+ if ( flDists[iVert] > OVERLAY_EPSILON )
+ {
+ nSides[iVert] = SIDE_FRONT;
+ }
+ else if ( flDists[iVert] < -OVERLAY_EPSILON )
+ {
+ nSides[iVert] = SIDE_BACK;
+ }
+ else
+ {
+ nSides[iVert] = SIDE_ON;
+ }
+
+ nSideCounts[nSides[iVert]]++;
+ }
+
+ // Wrap around (close the polygon).
+ nSides[iVert] = nSides[0];
+ flDists[iVert] = flDists[0];
+
+ // All points in back - no split (copy face to back).
+ if( !nSideCounts[SIDE_FRONT] )
+ {
+ *ppBack = CopyTempFragment( pFragment );
+ return;
+ }
+
+ // All points in front - no split (copy face to front).
+ if( !nSideCounts[SIDE_BACK] )
+ {
+ *ppFront = CopyTempFragment( pFragment );
+ return;
+ }
+
+ // Build new front and back faces.
+ // NOTE: Gotta create them first
+ moverlayfragment_t *pFront = CreateTempFragment( 0 );
+ moverlayfragment_t *pBack = CreateTempFragment( 0 );
+ if ( !pFront || !pBack )
+ {
+ DestroyTempFragment( pFront );
+ DestroyTempFragment( pBack );
+ return;
+ }
+
+ MEM_ALLOC_CREDIT();
+
+ int nVertCount = pFragment->m_aPrimVerts.Count();
+ for ( iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ // "On" clip plane.
+ if ( nSides[iVert] == SIDE_ON )
+ {
+ pFront->m_aPrimVerts.AddToTail( pFragment->m_aPrimVerts[iVert] );
+ pBack->m_aPrimVerts.AddToTail( pFragment->m_aPrimVerts[iVert] );
+ continue;
+ }
+
+ // "In back" of clip plane.
+ if ( nSides[iVert] == SIDE_BACK )
+ {
+ pBack->m_aPrimVerts.AddToTail( pFragment->m_aPrimVerts[iVert] );
+ }
+
+ // "In front" of clip plane.
+ if ( nSides[iVert] == SIDE_FRONT )
+ {
+ pFront->m_aPrimVerts.AddToTail( pFragment->m_aPrimVerts[iVert] );
+ }
+
+ if ( nSides[iVert+1] == SIDE_ON || nSides[iVert+1] == nSides[iVert] )
+ continue;
+
+ // Split!
+ float fraction = flDists[iVert] / ( flDists[iVert] - flDists[iVert+1] );
+
+ overlayvert_t vert;
+ vert.pos = pFragment->m_aPrimVerts[iVert].pos + fraction * ( pFragment->m_aPrimVerts[(iVert+1)%nVertCount].pos -
+ pFragment->m_aPrimVerts[iVert].pos );
+ for ( int iTexCoord=0; iTexCoord < NUM_OVERLAY_TEXCOORDS; iTexCoord++ )
+ {
+ vert.texCoord[iTexCoord][0] = pFragment->m_aPrimVerts[iVert].texCoord[iTexCoord][0] + fraction * ( pFragment->m_aPrimVerts[(iVert+1)%nVertCount].texCoord[iTexCoord][0] -
+ pFragment->m_aPrimVerts[iVert].texCoord[iTexCoord][0] );
+ vert.texCoord[iTexCoord][1] = pFragment->m_aPrimVerts[iVert].texCoord[iTexCoord][1] + fraction * ( pFragment->m_aPrimVerts[(iVert+1)%nVertCount].texCoord[iTexCoord][1] -
+ pFragment->m_aPrimVerts[iVert].texCoord[iTexCoord][1] );
+ }
+
+ pFront->m_aPrimVerts.AddToTail( vert );
+ pBack->m_aPrimVerts.AddToTail( vert );
+ }
+
+ *ppFront = pFront;
+ *ppBack = pBack;
+}
+
+
+//-----------------------------------------------------------------------------
+// Copies a fragment into the main fragment list
+//-----------------------------------------------------------------------------
+OverlayFragmentHandle_t COverlayMgr::AddFragmentToFragmentList( int nSize )
+{
+ MEM_ALLOC_CREDIT();
+
+ // Add to list of fragments.
+ int iFragment = m_aFragments.Alloc( true );
+
+ moverlayfragment_t &frag = m_aFragments[iFragment];
+
+ frag.m_SurfId = SURFACE_HANDLE_INVALID;
+ frag.m_iOverlay = -1;
+ frag.m_nRenderFrameID = -1;
+ frag.m_nMaterialSortID = 0xFFFF;
+ frag.m_hNextRender = OVERLAY_FRAGMENT_INVALID;
+
+ if ( nSize > 0 )
+ {
+ frag.m_aPrimVerts.SetSize( nSize );
+ }
+
+ return iFragment;
+}
+
+
+//-----------------------------------------------------------------------------
+// Copies a fragment into the main fragment list
+//-----------------------------------------------------------------------------
+OverlayFragmentHandle_t COverlayMgr::AddFragmentToFragmentList( moverlayfragment_t *pSrc )
+{
+ MEM_ALLOC_CREDIT();
+
+ // Add to list of fragments.
+ int iFragment = m_aFragments.Alloc( true );
+
+ moverlayfragment_t &frag = m_aFragments[iFragment];
+
+ frag.m_SurfId = pSrc->m_SurfId;
+ frag.m_iOverlay = pSrc->m_iOverlay;
+ frag.m_aPrimVerts.CopyArray( pSrc->m_aPrimVerts.Base(), pSrc->m_aPrimVerts.Count() );
+
+ return iFragment;
+}
+
+
+//-----------------------------------------------------------------------------
+// Temp fragments for clipping algorithms
+//-----------------------------------------------------------------------------
+moverlayfragment_t *COverlayMgr::CreateTempFragment( int nSize )
+{
+ MEM_ALLOC_CREDIT();
+ moverlayfragment_t *pDst = new moverlayfragment_t;
+ if ( pDst )
+ {
+ pDst->m_SurfId = SURFACE_HANDLE_INVALID;
+ pDst->m_iOverlay = -1;
+ if ( nSize > 0 )
+ {
+ pDst->m_aPrimVerts.SetSize( nSize );
+ }
+ }
+
+ return pDst;
+}
+
+
+//-----------------------------------------------------------------------------
+// Temp fragments for clipping algorithms
+//-----------------------------------------------------------------------------
+moverlayfragment_t *COverlayMgr::CopyTempFragment( moverlayfragment_t *pSrc )
+{
+ MEM_ALLOC_CREDIT();
+ moverlayfragment_t *pDst = new moverlayfragment_t;
+ if ( pDst )
+ {
+ pDst->m_SurfId = pSrc->m_SurfId;
+ pDst->m_iOverlay = pSrc->m_iOverlay;
+ pDst->m_aPrimVerts.CopyArray( pSrc->m_aPrimVerts.Base(), pSrc->m_aPrimVerts.Count() );
+ }
+
+ return pDst;
+}
+
+//-----------------------------------------------------------------------------
+// Temp fragments for clipping algorithms
+//-----------------------------------------------------------------------------
+void COverlayMgr::DestroyTempFragment( moverlayfragment_t *pFragment )
+{
+ if ( pFragment )
+ {
+ delete pFragment;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void COverlayMgr::BuildClipPlanes( SurfaceHandle_t surfID, moverlayfragment_t &surfaceFrag,
+ const Vector &vecBasisNormal,
+ CUtlVector<cplane_t> &m_ClipPlanes )
+{
+ int nVertCount = surfaceFrag.m_aPrimVerts.Count();
+ for ( int iVert = 0; iVert < nVertCount; ++iVert )
+ {
+ Vector vecEdge;
+ vecEdge = surfaceFrag.m_aPrimVerts[(iVert+1)%nVertCount].pos - surfaceFrag.m_aPrimVerts[iVert].pos;
+ VectorNormalize( vecEdge );
+
+ int iPlane = m_ClipPlanes.AddToTail();
+ cplane_t *pPlane = &m_ClipPlanes[iPlane];
+
+ pPlane->normal = vecBasisNormal.Cross( vecEdge );
+ pPlane->dist = pPlane->normal.Dot( surfaceFrag.m_aPrimVerts[iVert].pos );
+ pPlane->type = 3;
+
+ // Check normal facing.
+ float flDistance = pPlane->normal.Dot( surfaceFrag.m_aPrimVerts[(iVert+2)%nVertCount].pos ) - pPlane->dist;
+ if ( flDistance > 0.0 )
+ {
+ // Flip
+ pPlane->normal.Negate();
+ pPlane->dist = -pPlane->dist;
+ }
+ }
+}
+
+//=============================================================================
+//
+// Code below this line will get moved out into common code!!!!!!!!!!!!!
+//
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Overlay_BuildBasisOrigin( Vector &vecBasisOrigin, SurfaceHandle_t surfID )
+{
+ cplane_t surfacePlane = MSurf_Plane( surfID );
+ VectorNormalize( surfacePlane.normal );
+
+ // Get the distance from entity origin to face plane.
+ float flDist = surfacePlane.normal.Dot( vecBasisOrigin ) - surfacePlane.dist;
+
+ // Move the basis origin to the position of the entity projected into the face plane.
+ vecBasisOrigin = vecBasisOrigin - ( flDist * surfacePlane.normal );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+bool Overlay_IsBasisFlipped( int *pFlip, int iAxis, int iComponent )
+{
+ if ( iAxis < 0 || iAxis > 2 || iComponent < 0 || iComponent > 2 )
+ return false;
+
+ int nValue = ( 1 << iComponent );
+ return ( ( pFlip[iAxis] & nValue ) != 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Overlay_BuildBasis( const Vector &vecBasisNormal, Vector &vecBasisU, Vector &vecBasisV, bool bFlip )
+{
+ // Verify incoming data.
+ Assert( vecBasisNormal.IsValid() );
+ if ( !vecBasisNormal.IsValid() )
+ return;
+
+ vecBasisV = vecBasisNormal.Cross( vecBasisU );
+
+ if ( bFlip )
+ {
+ vecBasisV.Negate();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Overlay_TriTLToBR(
+ CDispInfo *pDisp,
+ CMeshReader *pReader,
+ Vector &vecWorld,
+ float flU,
+ float flV,
+ int nWidth,
+ const Vector &vecIntersectPoint )
+{
+ const float TRIEDGE_EPSILON = 0.000001f;
+
+ int nHeight = nWidth;
+
+ int nSnapU = static_cast<int>( flU );
+ int nSnapV = static_cast<int>( flV );
+ int nNextU = nSnapU + 1;
+ int nNextV = nSnapV + 1;
+ if ( nNextU == nWidth) { --nNextU; }
+ if ( nNextV == nHeight ) { --nNextV; }
+
+ float flFracU = flU - static_cast<float>( nSnapU );
+ float flFracV = flV - static_cast<float>( nSnapV );
+
+ Vector vecVerts[3], vecFlatVerts[3];
+ if( ( flFracU + flFracV ) >= ( 1.0f + TRIEDGE_EPSILON ) )
+ {
+ int nIndices[3];
+ nIndices[0] = nNextV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nNextU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ for( int iVert = 0; iVert < 3; ++iVert )
+ {
+ vecVerts[iVert] = GetOverlayPos( pReader, nIndices[iVert] );
+ vecFlatVerts[iVert] = pDisp->GetFlatVert( nIndices[iVert] );
+ }
+
+ if ( nSnapU == nNextU )
+ {
+ if ( nSnapV == nNextV )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else if ( nSnapV == nNextV )
+ {
+ if ( nSnapU == nNextU )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else
+ {
+ float flCfs[3];
+ if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) )
+ {
+ vecWorld = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
+ }
+ else
+ {
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nSnapU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ for( int iVert = 0; iVert < 3; ++iVert )
+ {
+ vecVerts[iVert] = GetOverlayPos( pReader, nIndices[iVert] );
+ vecFlatVerts[iVert] = pDisp->GetFlatVert( nIndices[iVert] );
+ }
+
+ if ( nSnapU == nNextU )
+ {
+ if ( nSnapV == nNextV )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) );
+ }
+ }
+ else if ( nSnapV == nNextV )
+ {
+ if ( nSnapU == nNextU )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else
+ {
+ CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] );
+ vecWorld = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
+ }
+ }
+ }
+ }
+ else
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nSnapU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ for( int iVert = 0; iVert < 3; ++iVert )
+ {
+ vecVerts[iVert] = GetOverlayPos( pReader, nIndices[iVert] );
+ vecFlatVerts[iVert] = pDisp->GetFlatVert( nIndices[iVert] );
+ }
+
+ if ( nSnapU == nNextU )
+ {
+ if ( nSnapV == nNextV )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) );
+ }
+ }
+ else if ( nSnapV == nNextV )
+ {
+ if ( nSnapU == nNextU )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else
+ {
+ float flCfs[3];
+ if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) )
+ {
+ vecWorld = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
+ }
+ else
+ {
+ nIndices[0] = nNextV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nNextU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ for( int iVert = 0; iVert < 3; ++iVert )
+ {
+ vecVerts[iVert] = GetOverlayPos( pReader, nIndices[iVert] );
+ vecFlatVerts[iVert] = pDisp->GetFlatVert( nIndices[iVert] );
+ }
+
+ if ( nSnapU == nNextU )
+ {
+ if ( nSnapV == nNextV )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else if ( nSnapV == nNextV )
+ {
+ if ( nSnapU == nNextU )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else
+ {
+ CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] );
+ vecWorld = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Overlay_TriBLToTR(
+ CDispInfo *pDisp,
+ CMeshReader *pReader,
+ Vector &vecWorld,
+ float flU,
+ float flV,
+ int nWidth,
+ const Vector &vecIntersectPoint )
+{
+ int nHeight = nWidth;
+
+ int nSnapU = static_cast<int>( flU );
+ int nSnapV = static_cast<int>( flV );
+ int nNextU = nSnapU + 1;
+ int nNextV = nSnapV + 1;
+ if ( nNextU == nWidth) { --nNextU; }
+ if ( nNextV == nHeight ) { --nNextV; }
+
+ float flFracU = flU - static_cast<float>( nSnapU );
+ float flFracV = flV - static_cast<float>( nSnapV );
+
+ // The fractions are not correct all the time - but they are a good first guess!
+ Vector vecVerts[3], vecFlatVerts[3];
+ if( flFracU < flFracV )
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nSnapU;
+ nIndices[2] = nNextV * nWidth + nNextU;
+
+ for( int iVert = 0; iVert < 3; ++iVert )
+ {
+ vecVerts[iVert] = GetOverlayPos( pReader, nIndices[iVert] );
+ vecFlatVerts[iVert] = pDisp->GetFlatVert( nIndices[iVert] );
+ }
+
+ if ( nSnapU == nNextU )
+ {
+ if ( nSnapV == nNextV )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else if ( nSnapV == nNextV )
+ {
+ if ( nSnapU == nNextU )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else
+ {
+ float flCfs[3];
+ if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) )
+ {
+ vecWorld = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
+ }
+ else
+ {
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nNextU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ for( int iVert = 0; iVert < 3; ++iVert )
+ {
+ vecVerts[iVert] = GetOverlayPos( pReader, nIndices[iVert] );
+ vecFlatVerts[iVert] = pDisp->GetFlatVert( nIndices[iVert] );
+ }
+
+ if ( nSnapU == nNextU )
+ {
+ if ( nSnapV == nNextV )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) );
+ }
+ }
+ else if ( nSnapV == nNextV )
+ {
+ if ( nSnapU == nNextU )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else
+ {
+ CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] );
+ vecWorld = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
+ }
+ }
+ }
+ }
+ else
+ {
+ int nIndices[3];
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nNextU;
+ nIndices[2] = nSnapV * nWidth + nNextU;
+
+ for( int iVert = 0; iVert < 3; ++iVert )
+ {
+ vecVerts[iVert] = GetOverlayPos( pReader, nIndices[iVert] );
+ vecFlatVerts[iVert] = pDisp->GetFlatVert( nIndices[iVert] );
+ }
+
+ if ( nSnapU == nNextU )
+ {
+ if ( nSnapV == nNextV )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[1] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[1] - vecVerts[0] ) );
+ }
+ }
+ else if ( nSnapV == nNextV )
+ {
+ if ( nSnapU == nNextU )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else
+ {
+ float flCfs[3];
+ if ( CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] ) )
+ {
+ vecWorld = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
+ }
+ else
+ {
+ nIndices[0] = nSnapV * nWidth + nSnapU;
+ nIndices[1] = nNextV * nWidth + nSnapU;
+ nIndices[2] = nNextV * nWidth + nNextU;
+
+ for( int iVert = 0; iVert < 3; ++iVert )
+ {
+ vecVerts[iVert] = GetOverlayPos( pReader, nIndices[iVert] );
+ vecFlatVerts[iVert] = pDisp->GetFlatVert( nIndices[iVert] );
+ }
+
+ if ( nSnapU == nNextU )
+ {
+ if ( nSnapV == nNextV )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else if ( nSnapV == nNextV )
+ {
+ if ( nSnapU == nNextU )
+ {
+ vecWorld = vecVerts[0];
+ }
+ else
+ {
+ float flFrac = ( vecIntersectPoint - vecFlatVerts[0] ).Length() / ( vecFlatVerts[2] - vecFlatVerts[0] ).Length();
+ vecWorld = vecVerts[0] + ( flFrac * ( vecVerts[2] - vecVerts[0] ) );
+ }
+ }
+ else
+ {
+ CalcBarycentricCooefs( vecFlatVerts[0], vecFlatVerts[1], vecFlatVerts[2], vecIntersectPoint, flCfs[0], flCfs[1], flCfs[2] );
+ vecWorld = ( vecVerts[0] * flCfs[0] ) + ( vecVerts[1] * flCfs[1] ) + ( vecVerts[2] * flCfs[2] );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void Overlay_DispUVToWorld( CDispInfo *pDisp, CMeshReader *pReader, const Vector2D &vecUV, Vector &vecWorld, moverlayfragment_t &surfaceFrag )
+{
+ // Get the displacement power.
+ const CPowerInfo *pPowerInfo = pDisp->GetPowerInfo();
+ int nWidth = ( ( 1 << pPowerInfo->GetPower() ) + 1 );
+ int nHeight = nWidth;
+
+ Vector vecIntersectPoint;
+ PointInQuadFromBarycentric( surfaceFrag.m_aPrimVerts[(0+pDisp->m_iPointStart)%4].pos,
+ surfaceFrag.m_aPrimVerts[(3+pDisp->m_iPointStart)%4].pos,
+ surfaceFrag.m_aPrimVerts[(2+pDisp->m_iPointStart)%4].pos,
+ surfaceFrag.m_aPrimVerts[(1+pDisp->m_iPointStart)%4].pos,
+ vecUV, vecIntersectPoint );
+
+ // Scale the U, V coordinates to the displacement grid size.
+ float flU = vecUV.x * static_cast<float>( nWidth - 1.000001f );
+ float flV = vecUV.y * static_cast<float>( nHeight - 1.000001f );
+
+ // Find the base U, V.
+ int nSnapU = static_cast<int>( flU );
+ int nSnapV = static_cast<int>( flV );
+
+ // Use this to get the triangle orientation.
+ bool bOdd = ( ( ( nSnapV * nWidth ) + nSnapU ) % 2 == 1 );
+
+ // Top Left to Bottom Right
+ if( bOdd )
+ {
+ Overlay_TriTLToBR( pDisp, pReader, vecWorld, flU, flV, nWidth, vecIntersectPoint );
+ }
+ // Bottom Left to Top Right
+ else
+ {
+ Overlay_TriBLToTR( pDisp, pReader, vecWorld, flU, flV, nWidth, vecIntersectPoint );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_OverlayUVToOverlayPlane( const Vector &vecBasisOrigin, const Vector &vecBasisU,
+ const Vector &vecBasisV, const Vector &vecUVPoint,
+ Vector &vecPlanePoint )
+{
+ vecPlanePoint = ( vecUVPoint.x * vecBasisU ) + ( vecUVPoint.y * vecBasisV );
+ vecPlanePoint += vecBasisOrigin;
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_WorldToOverlayPlane( const Vector &vecBasisOrigin, const Vector &vecBasisNormal,
+ const Vector &vecWorldPoint, Vector &vecPlanePoint )
+{
+ Vector vecDelta = vecWorldPoint - vecBasisOrigin;
+ float flDistance = vecBasisNormal.Dot( vecDelta );
+ vecPlanePoint = vecWorldPoint - ( flDistance * vecBasisNormal );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void Overlay_OverlayPlaneToWorld( const Vector &vecBasisNormal, SurfaceHandle_t surfID,
+ const Vector &vecPlanePoint, Vector &vecWorldPoint )
+{
+ cplane_t surfacePlane = MSurf_Plane( surfID );
+ VectorNormalize( surfacePlane.normal );
+ float flDistanceToSurface = surfacePlane.normal.Dot( vecPlanePoint ) - surfacePlane.dist;
+
+ float flDenom = surfacePlane.normal.Dot( vecBasisNormal );
+ float flDistance;
+ if( flDenom != 0.0f )
+ {
+ flDistance = ( 1.0f / flDenom ) * flDistanceToSurface;
+ }
+ else
+ {
+ flDistance = flDistanceToSurface;
+ }
+
+ vecWorldPoint = vecPlanePoint - ( vecBasisNormal * flDistance );
+}