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.

329 lines
10 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "decal_clip.h"
  8. // memdbgon must be the last include file in a .cpp file!!!
  9. #include "tier0/memdbgon.h"
  10. // --------------------------------------------------------------------------- //
  11. // Template classes for the clipper.
  12. // --------------------------------------------------------------------------- //
  13. class CPlane_Top
  14. {
  15. public:
  16. static inline bool Inside( CDecalVert *pVert ) {return pVert->m_ctCoords.y < 1;}
  17. static inline float Clip( CDecalVert *one, CDecalVert *two ) {return (1 - one->m_ctCoords.y) / (two->m_ctCoords.y - one->m_ctCoords.y);}
  18. };
  19. class CPlane_Left
  20. {
  21. public:
  22. static inline bool Inside( CDecalVert *pVert ) {return pVert->m_ctCoords.x > 0;}
  23. static inline float Clip( CDecalVert *one, CDecalVert *two ) {return one->m_ctCoords.x / (one->m_ctCoords.x - two->m_ctCoords.x);}
  24. };
  25. class CPlane_Right
  26. {
  27. public:
  28. static inline bool Inside( CDecalVert *pVert ) {return pVert->m_ctCoords.x < 1;}
  29. static inline float Clip( CDecalVert *one, CDecalVert *two ) {return (1 - one->m_ctCoords.x) / (two->m_ctCoords.x - one->m_ctCoords.x);}
  30. };
  31. class CPlane_Bottom
  32. {
  33. public:
  34. static inline bool Inside( CDecalVert *pVert ) {return pVert->m_ctCoords.y > 0;}
  35. static inline float Clip( CDecalVert *one, CDecalVert *two ) {return one->m_ctCoords.y / (one->m_ctCoords.y - two->m_ctCoords.y);}
  36. };
  37. // --------------------------------------------------------------------------- //
  38. // Globals.
  39. // --------------------------------------------------------------------------- //
  40. CDecalVert ALIGN16 g_DecalClipVerts[MAX_DECALCLIPVERT] ALIGN16_POST;
  41. static CDecalVert ALIGN16 g_DecalClipVerts2[MAX_DECALCLIPVERT] ALIGN16_POST;
  42. template< class Clipper >
  43. static inline void Intersect( Clipper &clip, CDecalVert *one, CDecalVert *two, CDecalVert *out )
  44. {
  45. float t = Clipper::Clip( one, two );
  46. VectorLerp( one->m_vPos, two->m_vPos, t, out->m_vPos );
  47. Vector2DLerp( one->m_cLMCoords, two->m_cLMCoords, t, out->m_cLMCoords );
  48. Vector2DLerp( one->m_ctCoords, two->m_ctCoords, t, out->m_ctCoords );
  49. }
  50. template< class Clipper >
  51. static inline int SHClip( CDecalVert *pDecalClipVerts, int vertCount, CDecalVert *out, Clipper &clip )
  52. {
  53. int j, outCount;
  54. CDecalVert *s, *p;
  55. Assert( vertCount <= MAX_DECALCLIPVERT );
  56. outCount = 0;
  57. s = &pDecalClipVerts[ vertCount-1 ];
  58. for ( j = 0; j < vertCount; j++ )
  59. {
  60. p = &pDecalClipVerts[ j ];
  61. if ( Clipper::Inside( p ) )
  62. {
  63. if ( Clipper::Inside( s ) )
  64. {
  65. *out = *p;
  66. outCount++;
  67. out++;
  68. }
  69. else
  70. {
  71. Intersect( clip, s, p, out );
  72. out++;
  73. outCount++;
  74. *out = *p;
  75. outCount++;
  76. out++;
  77. }
  78. }
  79. else
  80. {
  81. if ( Clipper::Inside( s ) )
  82. {
  83. Intersect( clip, p, s, out );
  84. out++;
  85. outCount++;
  86. }
  87. }
  88. s = p;
  89. }
  90. return outCount;
  91. }
  92. const float DECAL_CLIP_EPSILON = 0.01f;
  93. CDecalVert* R_DoDecalSHClip( CDecalVert *pInVerts, CDecalVert *pOutVerts, decal_t *pDecal, int nStartVerts, const Vector &vecNormal )
  94. {
  95. if ( pOutVerts == NULL )
  96. pOutVerts = &g_DecalClipVerts[0];
  97. CPlane_Top top;
  98. CPlane_Left left;
  99. CPlane_Right right;
  100. CPlane_Bottom bottom;
  101. // Clip the polygon to the decal texture space
  102. int outCount = SHClip( pInVerts, nStartVerts, &g_DecalClipVerts2[0], top );
  103. outCount = SHClip( &g_DecalClipVerts2[0], outCount, &g_DecalClipVerts[0], left );
  104. outCount = SHClip( &g_DecalClipVerts[0], outCount, &g_DecalClipVerts2[0], right );
  105. outCount = SHClip( &g_DecalClipVerts2[0], outCount, pOutVerts, bottom );
  106. pDecal->clippedVertCount = outCount;
  107. if ( !outCount )
  108. return NULL;
  109. // FIXME: This is a brutally hack workaround for the fact that we get massive decal flicker
  110. // when looking at a decal at a glancing angle while standing right next to it.
  111. for ( int i = 0; i < outCount; ++i )
  112. {
  113. VectorMA( pOutVerts[i].m_vPos, OVERLAY_AVOID_FLICKER_NORMAL_OFFSET, vecNormal, pOutVerts[i].m_vPos );
  114. }
  115. if ( outCount && pDecal->material->InMaterialPage() )
  116. {
  117. float offset[2], scale[2];
  118. pDecal->material->GetMaterialOffset( offset );
  119. pDecal->material->GetMaterialScale( scale );
  120. for ( int i = 0; i < outCount; ++i )
  121. {
  122. pOutVerts[i].m_ctCoords.x = offset[0] + (pOutVerts[i].m_ctCoords.x * scale[0]);
  123. pOutVerts[i].m_ctCoords.y = offset[1] + (pOutVerts[i].m_ctCoords.y * scale[1]);
  124. }
  125. }
  126. return pOutVerts;
  127. }
  128. // Build the initial list of vertices from the surface verts into the global array, 'verts'.
  129. void R_SetupDecalVertsForMSurface(
  130. decal_t * RESTRICT pDecal,
  131. SurfaceHandle_t surfID,
  132. Vector * RESTRICT pTextureSpaceBasis,
  133. CDecalVert * RESTRICT pVerts )
  134. {
  135. unsigned short * RESTRICT pIndices = &host_state.worldbrush->vertindices[MSurf_FirstVertIndex( surfID )];
  136. int count = MSurf_VertCount( surfID );
  137. float uOffset = 0.5f - pDecal->dx;
  138. float vOffset = 0.5f - pDecal->dy;
  139. for ( int j = 0; j < count; j++ )
  140. {
  141. int vertIndex = pIndices[j];
  142. pVerts[j].m_vPos = host_state.worldbrush->vertexes[vertIndex].position; // Copy model space coordinates
  143. // garymcthack - what about m_ParentTexCoords?
  144. pVerts[j].m_ctCoords.x = DotProduct( pVerts[j].m_vPos, pTextureSpaceBasis[0] ) + uOffset;
  145. pVerts[j].m_ctCoords.y = DotProduct( pVerts[j].m_vPos, pTextureSpaceBasis[1] ) + vOffset;
  146. pVerts[j].m_cLMCoords.Init();
  147. }
  148. }
  149. //-----------------------------------------------------------------------------
  150. // compute the decal basis based on surface normal, and preferred saxis
  151. //-----------------------------------------------------------------------------
  152. #define SIN_45_DEGREES ( 0.70710678118654752440084436210485f )
  153. void R_DecalComputeBasis( Vector const& surfaceNormal, Vector const* pSAxis,
  154. Vector* textureSpaceBasis )
  155. {
  156. /*
  157. // s, t, textureSpaceNormal (T cross S = textureSpaceNormal(N))
  158. // N
  159. // \
  160. // \
  161. // \
  162. // |---->S
  163. // |
  164. // |
  165. // |T
  166. // S = textureSpaceBasis[0]
  167. // T = textureSpaceBasis[1]
  168. // N = textureSpaceBasis[2]
  169. */
  170. // Get the surface normal.
  171. VectorCopy( surfaceNormal, textureSpaceBasis[2] );
  172. if (pSAxis)
  173. {
  174. // T = S cross N
  175. CrossProduct( *pSAxis, textureSpaceBasis[2], textureSpaceBasis[1] );
  176. // Name sure they aren't parallel or antiparallel
  177. // In that case, fall back to the normal algorithm.
  178. if ( DotProduct( textureSpaceBasis[1], textureSpaceBasis[1] ) > 1e-6 )
  179. {
  180. // S = N cross T
  181. CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] );
  182. VectorNormalizeFast( textureSpaceBasis[0] );
  183. VectorNormalizeFast( textureSpaceBasis[1] );
  184. return;
  185. }
  186. // Fall through to the standard algorithm for parallel or antiparallel
  187. }
  188. // floor/ceiling?
  189. if( fabs( surfaceNormal[2] ) > SIN_45_DEGREES )
  190. {
  191. textureSpaceBasis[0][0] = 1.0f;
  192. textureSpaceBasis[0][1] = 0.0f;
  193. textureSpaceBasis[0][2] = 0.0f;
  194. // T = S cross N
  195. CrossProduct( textureSpaceBasis[0], textureSpaceBasis[2], textureSpaceBasis[1] );
  196. // S = N cross T
  197. CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] );
  198. }
  199. // wall
  200. else
  201. {
  202. textureSpaceBasis[1][0] = 0.0f;
  203. textureSpaceBasis[1][1] = 0.0f;
  204. textureSpaceBasis[1][2] = -1.0f;
  205. // S = N cross T
  206. CrossProduct( textureSpaceBasis[2], textureSpaceBasis[1], textureSpaceBasis[0] );
  207. // T = S cross N
  208. CrossProduct( textureSpaceBasis[0], textureSpaceBasis[2], textureSpaceBasis[1] );
  209. }
  210. VectorNormalizeFast( textureSpaceBasis[0] );
  211. VectorNormalizeFast( textureSpaceBasis[1] );
  212. }
  213. #define MAX_PLAYERSPRAY_SIZE 64
  214. void R_SetupDecalTextureSpaceBasis( decal_t *pDecal, Vector &vSurfNormal, IMaterial *pMaterial, Vector textureSpaceBasis[3], float decalWorldScale[2] )
  215. {
  216. // Compute the non-scaled decal basis
  217. R_DecalComputeBasis( vSurfNormal, (pDecal->flags & FDECAL_USESAXIS) ? &pDecal->saxis : 0, textureSpaceBasis );
  218. // world width of decal = ptexture->width / pDecal->scale
  219. // world height of decal = ptexture->height / pDecal->scale
  220. // scale is inverse, scales world space to decal u/v space [0,1]
  221. // OPTIMIZE: Get rid of these divides
  222. if ( pDecal->flags & FDECAL_PLAYERSPRAY )
  223. {
  224. int nWidthScale = pMaterial->GetMappingWidth() / MAX_PLAYERSPRAY_SIZE;
  225. int nHeightScale = pMaterial->GetMappingHeight() / MAX_PLAYERSPRAY_SIZE;
  226. float flScale = static_cast<float>( max( nWidthScale, nHeightScale ) );
  227. decalWorldScale[0] = pDecal->scale / pMaterial->GetMappingWidth();
  228. decalWorldScale[1] = pDecal->scale / pMaterial->GetMappingHeight();
  229. if ( flScale > 1.0f )
  230. {
  231. decalWorldScale[0] *= flScale;
  232. decalWorldScale[1] *= flScale;
  233. }
  234. }
  235. else
  236. {
  237. decalWorldScale[0] = pDecal->scale / pMaterial->GetMappingWidth();
  238. decalWorldScale[1] = pDecal->scale / pMaterial->GetMappingHeight();
  239. }
  240. VectorScale( textureSpaceBasis[0], decalWorldScale[0], textureSpaceBasis[0] );
  241. VectorScale( textureSpaceBasis[1], decalWorldScale[1], textureSpaceBasis[1] );
  242. }
  243. // Figure out where the decal maps onto the surface.
  244. void R_SetupDecalClip( CDecalVert* &pOutVerts, decal_t *pDecal, Vector &vSurfNormal, IMaterial *pMaterial, Vector textureSpaceBasis[3], float decalWorldScale[2] )
  245. {
  246. // if ( pOutVerts == NULL )
  247. // pOutVerts = &g_DecalClipVerts[0];
  248. R_SetupDecalTextureSpaceBasis( pDecal, vSurfNormal, pMaterial, textureSpaceBasis, decalWorldScale );
  249. // Generate texture coordinates for each vertex in decal s,t space
  250. // probably should pre-generate this, store it and use it for decal-decal collisions
  251. // as in R_DecalsIntersect()
  252. pDecal->dx = DotProduct( pDecal->position, textureSpaceBasis[0] );
  253. pDecal->dy = DotProduct( pDecal->position, textureSpaceBasis[1] );
  254. }
  255. //-----------------------------------------------------------------------------
  256. // Generate clipped vertex list for decal pdecal projected onto polygon psurf
  257. //-----------------------------------------------------------------------------
  258. CDecalVert* R_DecalVertsClip( CDecalVert *pOutVerts, decal_t *pDecal, SurfaceHandle_t surfID, IMaterial *pMaterial )
  259. {
  260. float decalWorldScale[2];
  261. Vector textureSpaceBasis[3];
  262. // Figure out where the decal maps onto the surface.
  263. R_SetupDecalClip( pOutVerts, pDecal, MSurf_Plane( surfID ).normal, pMaterial, textureSpaceBasis, decalWorldScale );
  264. // Build the initial list of vertices from the surface verts.
  265. R_SetupDecalVertsForMSurface( pDecal, surfID, textureSpaceBasis, g_DecalClipVerts );
  266. return R_DoDecalSHClip( g_DecalClipVerts, pOutVerts, pDecal, MSurf_VertCount( surfID ), MSurf_Plane( surfID ).normal );
  267. }