Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2358 lines
78 KiB

//========= 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 );
}