diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /engine/decal_clip.cpp | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'engine/decal_clip.cpp')
| -rw-r--r-- | engine/decal_clip.cpp | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/engine/decal_clip.cpp b/engine/decal_clip.cpp new file mode 100644 index 0000000..b2bfa2c --- /dev/null +++ b/engine/decal_clip.cpp @@ -0,0 +1,329 @@ +//========= 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 ); +} |