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.
329 lines
10 KiB
329 lines
10 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "decal_clip.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
// --------------------------------------------------------------------------- // |
|
// Template classes for the clipper. |
|
// --------------------------------------------------------------------------- // |
|
class CPlane_Top |
|
{ |
|
public: |
|
static inline bool Inside( CDecalVert *pVert ) {return pVert->m_ctCoords.y < 1;} |
|
static inline float Clip( CDecalVert *one, CDecalVert *two ) {return (1 - one->m_ctCoords.y) / (two->m_ctCoords.y - one->m_ctCoords.y);} |
|
}; |
|
|
|
class CPlane_Left |
|
{ |
|
public: |
|
static inline bool Inside( CDecalVert *pVert ) {return pVert->m_ctCoords.x > 0;} |
|
static inline float Clip( CDecalVert *one, CDecalVert *two ) {return one->m_ctCoords.x / (one->m_ctCoords.x - two->m_ctCoords.x);} |
|
}; |
|
|
|
class CPlane_Right |
|
{ |
|
public: |
|
static inline bool Inside( CDecalVert *pVert ) {return pVert->m_ctCoords.x < 1;} |
|
static inline float Clip( CDecalVert *one, CDecalVert *two ) {return (1 - one->m_ctCoords.x) / (two->m_ctCoords.x - one->m_ctCoords.x);} |
|
}; |
|
|
|
class CPlane_Bottom |
|
{ |
|
public: |
|
static inline bool Inside( CDecalVert *pVert ) {return pVert->m_ctCoords.y > 0;} |
|
static inline float Clip( CDecalVert *one, CDecalVert *two ) {return one->m_ctCoords.y / (one->m_ctCoords.y - two->m_ctCoords.y);} |
|
}; |
|
|
|
|
|
|
|
// --------------------------------------------------------------------------- // |
|
// Globals. |
|
// --------------------------------------------------------------------------- // |
|
CDecalVert ALIGN16 g_DecalClipVerts[MAX_DECALCLIPVERT] ALIGN16_POST; |
|
static CDecalVert ALIGN16 g_DecalClipVerts2[MAX_DECALCLIPVERT] ALIGN16_POST; |
|
|
|
|
|
|
|
|
|
template< class Clipper > |
|
static inline void Intersect( Clipper &clip, CDecalVert *one, CDecalVert *two, CDecalVert *out ) |
|
{ |
|
float t = Clipper::Clip( one, two ); |
|
|
|
VectorLerp( one->m_vPos, two->m_vPos, t, out->m_vPos ); |
|
Vector2DLerp( one->m_cLMCoords, two->m_cLMCoords, t, out->m_cLMCoords ); |
|
Vector2DLerp( one->m_ctCoords, two->m_ctCoords, t, out->m_ctCoords ); |
|
} |
|
|
|
|
|
template< class Clipper > |
|
static inline int SHClip( CDecalVert *pDecalClipVerts, int vertCount, CDecalVert *out, Clipper &clip ) |
|
{ |
|
int j, outCount; |
|
CDecalVert *s, *p; |
|
|
|
Assert( vertCount <= MAX_DECALCLIPVERT ); |
|
|
|
outCount = 0; |
|
|
|
s = &pDecalClipVerts[ vertCount-1 ]; |
|
for ( j = 0; j < vertCount; j++ ) |
|
{ |
|
p = &pDecalClipVerts[ j ]; |
|
if ( Clipper::Inside( p ) ) |
|
{ |
|
if ( Clipper::Inside( s ) ) |
|
{ |
|
*out = *p; |
|
outCount++; |
|
out++; |
|
} |
|
else |
|
{ |
|
Intersect( clip, s, p, out ); |
|
out++; |
|
outCount++; |
|
|
|
*out = *p; |
|
outCount++; |
|
out++; |
|
} |
|
} |
|
else |
|
{ |
|
if ( Clipper::Inside( s ) ) |
|
{ |
|
Intersect( clip, p, s, out ); |
|
out++; |
|
outCount++; |
|
} |
|
} |
|
s = p; |
|
} |
|
|
|
return outCount; |
|
} |
|
|
|
const float DECAL_CLIP_EPSILON = 0.01f; |
|
|
|
CDecalVert* R_DoDecalSHClip( CDecalVert *pInVerts, CDecalVert *pOutVerts, decal_t *pDecal, int nStartVerts, const Vector &vecNormal ) |
|
{ |
|
if ( pOutVerts == NULL ) |
|
pOutVerts = &g_DecalClipVerts[0]; |
|
|
|
CPlane_Top top; |
|
CPlane_Left left; |
|
CPlane_Right right; |
|
CPlane_Bottom bottom; |
|
|
|
// Clip the polygon to the decal texture space |
|
int outCount = SHClip( pInVerts, nStartVerts, &g_DecalClipVerts2[0], top ); |
|
outCount = SHClip( &g_DecalClipVerts2[0], outCount, &g_DecalClipVerts[0], left ); |
|
outCount = SHClip( &g_DecalClipVerts[0], outCount, &g_DecalClipVerts2[0], right ); |
|
outCount = SHClip( &g_DecalClipVerts2[0], outCount, pOutVerts, bottom ); |
|
|
|
pDecal->clippedVertCount = outCount; |
|
|
|
if ( !outCount ) |
|
return NULL; |
|
|
|
// FIXME: This is a brutally hack workaround for the fact that we get massive decal flicker |
|
// when looking at a decal at a glancing angle while standing right next to it. |
|
|
|
for ( int i = 0; i < outCount; ++i ) |
|
{ |
|
VectorMA( pOutVerts[i].m_vPos, OVERLAY_AVOID_FLICKER_NORMAL_OFFSET, vecNormal, pOutVerts[i].m_vPos ); |
|
} |
|
if ( outCount && pDecal->material->InMaterialPage() ) |
|
{ |
|
float offset[2], scale[2]; |
|
pDecal->material->GetMaterialOffset( offset ); |
|
pDecal->material->GetMaterialScale( scale ); |
|
for ( int i = 0; i < outCount; ++i ) |
|
{ |
|
pOutVerts[i].m_ctCoords.x = offset[0] + (pOutVerts[i].m_ctCoords.x * scale[0]); |
|
pOutVerts[i].m_ctCoords.y = offset[1] + (pOutVerts[i].m_ctCoords.y * scale[1]); |
|
} |
|
} |
|
|
|
return pOutVerts; |
|
} |
|
|
|
// Build the initial list of vertices from the surface verts into the global array, 'verts'. |
|
void R_SetupDecalVertsForMSurface( |
|
decal_t * RESTRICT pDecal, |
|
SurfaceHandle_t surfID, |
|
Vector * RESTRICT pTextureSpaceBasis, |
|
CDecalVert * RESTRICT pVerts ) |
|
{ |
|
unsigned short * RESTRICT pIndices = &host_state.worldbrush->vertindices[MSurf_FirstVertIndex( surfID )]; |
|
int count = MSurf_VertCount( surfID ); |
|
float uOffset = 0.5f - pDecal->dx; |
|
float vOffset = 0.5f - pDecal->dy; |
|
|
|
for ( int j = 0; j < count; j++ ) |
|
{ |
|
int vertIndex = pIndices[j]; |
|
|
|
pVerts[j].m_vPos = host_state.worldbrush->vertexes[vertIndex].position; // Copy model space coordinates |
|
// garymcthack - what about m_ParentTexCoords? |
|
pVerts[j].m_ctCoords.x = DotProduct( pVerts[j].m_vPos, pTextureSpaceBasis[0] ) + uOffset; |
|
pVerts[j].m_ctCoords.y = DotProduct( pVerts[j].m_vPos, pTextureSpaceBasis[1] ) + vOffset; |
|
pVerts[j].m_cLMCoords.Init(); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// compute the decal basis based on surface normal, and preferred saxis |
|
//----------------------------------------------------------------------------- |
|
|
|
#define SIN_45_DEGREES ( 0.70710678118654752440084436210485f ) |
|
|
|
void R_DecalComputeBasis( Vector const& surfaceNormal, Vector const* pSAxis, |
|
Vector* textureSpaceBasis ) |
|
{ |
|
/* |
|
// s, t, textureSpaceNormal (T cross S = textureSpaceNormal(N)) |
|
// N |
|
// \ |
|
// \ |
|
// \ |
|
// |---->S |
|
// | |
|
// | |
|
// |T |
|
// S = textureSpaceBasis[0] |
|
// T = textureSpaceBasis[1] |
|
// N = textureSpaceBasis[2] |
|
*/ |
|
|
|
// Get the surface normal. |
|
VectorCopy( surfaceNormal, textureSpaceBasis[2] ); |
|
|
|
if (pSAxis) |
|
{ |
|
// T = S cross N |
|
CrossProduct( *pSAxis, textureSpaceBasis[2], textureSpaceBasis[1] ); |
|
|
|
// Name sure they aren't parallel or antiparallel |
|
// In that case, fall back to the normal algorithm. |
|
if ( DotProduct( textureSpaceBasis[1], textureSpaceBasis[1] ) > 1e-6 ) |
|
{ |
|
// S = N cross T |
|
CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] ); |
|
|
|
VectorNormalizeFast( textureSpaceBasis[0] ); |
|
VectorNormalizeFast( textureSpaceBasis[1] ); |
|
return; |
|
} |
|
|
|
// Fall through to the standard algorithm for parallel or antiparallel |
|
} |
|
|
|
// floor/ceiling? |
|
if( fabs( surfaceNormal[2] ) > SIN_45_DEGREES ) |
|
{ |
|
textureSpaceBasis[0][0] = 1.0f; |
|
textureSpaceBasis[0][1] = 0.0f; |
|
textureSpaceBasis[0][2] = 0.0f; |
|
|
|
// T = S cross N |
|
CrossProduct( textureSpaceBasis[0], textureSpaceBasis[2], textureSpaceBasis[1] ); |
|
|
|
// S = N cross T |
|
CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] ); |
|
} |
|
// wall |
|
else |
|
{ |
|
textureSpaceBasis[1][0] = 0.0f; |
|
textureSpaceBasis[1][1] = 0.0f; |
|
textureSpaceBasis[1][2] = -1.0f; |
|
|
|
// S = N cross T |
|
CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] ); |
|
// T = S cross N |
|
CrossProduct( textureSpaceBasis[0], textureSpaceBasis[2], textureSpaceBasis[1] ); |
|
} |
|
|
|
VectorNormalizeFast( textureSpaceBasis[0] ); |
|
VectorNormalizeFast( textureSpaceBasis[1] ); |
|
} |
|
|
|
#define MAX_PLAYERSPRAY_SIZE 64 |
|
|
|
void R_SetupDecalTextureSpaceBasis( decal_t *pDecal, Vector &vSurfNormal, IMaterial *pMaterial, Vector textureSpaceBasis[3], float decalWorldScale[2] ) |
|
{ |
|
// Compute the non-scaled decal basis |
|
R_DecalComputeBasis( vSurfNormal, (pDecal->flags & FDECAL_USESAXIS) ? &pDecal->saxis : 0, textureSpaceBasis ); |
|
|
|
// world width of decal = ptexture->width / pDecal->scale |
|
// world height of decal = ptexture->height / pDecal->scale |
|
// scale is inverse, scales world space to decal u/v space [0,1] |
|
// OPTIMIZE: Get rid of these divides |
|
if ( pDecal->flags & FDECAL_PLAYERSPRAY ) |
|
{ |
|
int nWidthScale = pMaterial->GetMappingWidth() / MAX_PLAYERSPRAY_SIZE; |
|
int nHeightScale = pMaterial->GetMappingHeight() / MAX_PLAYERSPRAY_SIZE; |
|
float flScale = static_cast<float>( max( nWidthScale, nHeightScale ) ); |
|
|
|
decalWorldScale[0] = pDecal->scale / pMaterial->GetMappingWidth(); |
|
decalWorldScale[1] = pDecal->scale / pMaterial->GetMappingHeight(); |
|
|
|
if ( flScale > 1.0f ) |
|
{ |
|
decalWorldScale[0] *= flScale; |
|
decalWorldScale[1] *= flScale; |
|
} |
|
} |
|
else |
|
{ |
|
decalWorldScale[0] = pDecal->scale / pMaterial->GetMappingWidth(); |
|
decalWorldScale[1] = pDecal->scale / pMaterial->GetMappingHeight(); |
|
} |
|
|
|
VectorScale( textureSpaceBasis[0], decalWorldScale[0], textureSpaceBasis[0] ); |
|
VectorScale( textureSpaceBasis[1], decalWorldScale[1], textureSpaceBasis[1] ); |
|
} |
|
|
|
|
|
// Figure out where the decal maps onto the surface. |
|
void R_SetupDecalClip( CDecalVert* &pOutVerts, decal_t *pDecal, Vector &vSurfNormal, IMaterial *pMaterial, Vector textureSpaceBasis[3], float decalWorldScale[2] ) |
|
{ |
|
// if ( pOutVerts == NULL ) |
|
// pOutVerts = &g_DecalClipVerts[0]; |
|
|
|
R_SetupDecalTextureSpaceBasis( pDecal, vSurfNormal, pMaterial, textureSpaceBasis, decalWorldScale ); |
|
|
|
// Generate texture coordinates for each vertex in decal s,t space |
|
// probably should pre-generate this, store it and use it for decal-decal collisions |
|
// as in R_DecalsIntersect() |
|
pDecal->dx = DotProduct( pDecal->position, textureSpaceBasis[0] ); |
|
pDecal->dy = DotProduct( pDecal->position, textureSpaceBasis[1] ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Generate clipped vertex list for decal pdecal projected onto polygon psurf |
|
//----------------------------------------------------------------------------- |
|
CDecalVert* R_DecalVertsClip( CDecalVert *pOutVerts, decal_t *pDecal, SurfaceHandle_t surfID, IMaterial *pMaterial ) |
|
{ |
|
float decalWorldScale[2]; |
|
Vector textureSpaceBasis[3]; |
|
|
|
// Figure out where the decal maps onto the surface. |
|
R_SetupDecalClip( pOutVerts, pDecal, MSurf_Plane( surfID ).normal, pMaterial, textureSpaceBasis, decalWorldScale ); |
|
|
|
// Build the initial list of vertices from the surface verts. |
|
R_SetupDecalVertsForMSurface( pDecal, surfID, textureSpaceBasis, g_DecalClipVerts ); |
|
|
|
return R_DoDecalSHClip( g_DecalClipVerts, pOutVerts, pDecal, MSurf_VertCount( surfID ), MSurf_Plane( surfID ).normal ); |
|
}
|
|
|