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.

500 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "cbase.h"
  8. #include "c_sprite.h"
  9. #include "model_types.h"
  10. #include "iviewrender.h"
  11. #include "view.h"
  12. #include "enginesprite.h"
  13. #include "engine/ivmodelinfo.h"
  14. #include "util_shared.h"
  15. #include "tier0/vprof.h"
  16. #include "materialsystem/imaterial.h"
  17. #include "materialsystem/imaterialvar.h"
  18. #include "view_shared.h"
  19. #include "viewrender.h"
  20. #include "tier1/KeyValues.h"
  21. #include "toolframework/itoolframework.h"
  22. #include "toolframework_client.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include "tier0/memdbgon.h"
  25. ConVar r_drawsprites( "r_drawsprites", "1", FCVAR_CHEAT );
  26. //-----------------------------------------------------------------------------
  27. // Purpose: Generic sprite model renderer
  28. // Input : *baseentity -
  29. // *psprite -
  30. // fscale -
  31. // frame -
  32. // rendermode -
  33. // r -
  34. // g -
  35. // b -
  36. // a -
  37. // forward -
  38. // right -
  39. // up -
  40. //-----------------------------------------------------------------------------
  41. static unsigned int s_nHDRColorScaleCache = 0;
  42. void DrawSpriteModel( IClientEntity *baseentity, CEngineSprite *psprite, const Vector &origin, float fscale, float frame,
  43. int rendermode, int r, int g, int b, int a, const Vector& forward, const Vector& right, const Vector& up, float flHDRColorScale )
  44. {
  45. float scale;
  46. IMaterial *material;
  47. // don't even bother culling, because it's just a single
  48. // polygon without a surface cache
  49. if ( fscale > 0 )
  50. scale = fscale;
  51. else
  52. scale = 1.0f;
  53. if ( rendermode == kRenderNormal )
  54. {
  55. render->SetBlend( 1.0f );
  56. }
  57. material = psprite->GetMaterial( (RenderMode_t)rendermode, frame );
  58. if ( !material )
  59. return;
  60. CMatRenderContextPtr pRenderContext( materials );
  61. if ( ShouldDrawInWireFrameMode() || r_drawsprites.GetInt() == 2 )
  62. {
  63. IMaterial *pMaterial = materials->FindMaterial( "debug/debugspritewireframe", TEXTURE_GROUP_OTHER );
  64. pRenderContext->Bind( pMaterial, NULL );
  65. }
  66. else
  67. {
  68. pRenderContext->Bind( material, (IClientRenderable*)baseentity );
  69. }
  70. unsigned char color[4];
  71. color[0] = r;
  72. color[1] = g;
  73. color[2] = b;
  74. color[3] = a;
  75. IMaterialVar *pHDRColorScaleVar = material->FindVarFast( "$HDRCOLORSCALE", &s_nHDRColorScaleCache );
  76. if( pHDRColorScaleVar )
  77. {
  78. pHDRColorScaleVar->SetVecValue( flHDRColorScale, flHDRColorScale, flHDRColorScale );
  79. }
  80. Vector point;
  81. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  82. CMeshBuilder meshBuilder;
  83. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  84. Vector vec_a;
  85. Vector vec_b;
  86. Vector vec_c;
  87. Vector vec_d;
  88. // isolate common terms
  89. VectorMA( origin, psprite->GetDown() * scale, up, vec_a );
  90. VectorScale( right, psprite->GetLeft() * scale, vec_b );
  91. VectorMA( origin, psprite->GetUp() * scale, up, vec_c );
  92. VectorScale( right, psprite->GetRight() * scale, vec_d );
  93. float flMinU, flMinV, flMaxU, flMaxV;
  94. psprite->GetTexCoordRange( &flMinU, &flMinV, &flMaxU, &flMaxV );
  95. meshBuilder.Color4ubv( color );
  96. meshBuilder.TexCoord2f( 0, flMinU, flMaxV );
  97. VectorAdd( vec_a, vec_b, point );
  98. meshBuilder.Position3fv( point.Base() );
  99. meshBuilder.AdvanceVertex();
  100. meshBuilder.Color4ubv( color );
  101. meshBuilder.TexCoord2f( 0, flMinU, flMinV );
  102. VectorAdd( vec_c, vec_b, point );
  103. meshBuilder.Position3fv( point.Base() );
  104. meshBuilder.AdvanceVertex();
  105. meshBuilder.Color4ubv( color );
  106. meshBuilder.TexCoord2f( 0, flMaxU, flMinV );
  107. VectorAdd( vec_c, vec_d, point );
  108. meshBuilder.Position3fv( point.Base() );
  109. meshBuilder.AdvanceVertex();
  110. meshBuilder.Color4ubv( color );
  111. meshBuilder.TexCoord2f( 0, flMaxU, flMaxV );
  112. VectorAdd( vec_a, vec_d, point );
  113. meshBuilder.Position3fv( point.Base() );
  114. meshBuilder.AdvanceVertex();
  115. meshBuilder.End();
  116. pMesh->Draw();
  117. }
  118. //-----------------------------------------------------------------------------
  119. // Purpose: Determine glow brightness/scale based on distance to render origin and trace results
  120. // Input : entorigin -
  121. // rendermode -
  122. // renderfx -
  123. // alpha -
  124. // pscale - Pointer to the value for scale, will be changed based on distance and rendermode.
  125. //-----------------------------------------------------------------------------
  126. float StandardGlowBlend( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle, int rendermode, int renderfx, int alpha, float *pscale )
  127. {
  128. float dist;
  129. float brightness;
  130. brightness = PixelVisibility_FractionVisible( params, queryHandle );
  131. if ( brightness <= 0.0f )
  132. {
  133. return 0.0f;
  134. }
  135. dist = GlowSightDistance( params.position, false );
  136. if ( dist <= 0.0f )
  137. {
  138. return 0.0f;
  139. }
  140. if ( renderfx == kRenderFxNoDissipation )
  141. {
  142. return (float)alpha * (1.0f/255.0f) * brightness;
  143. }
  144. // UNDONE: Tweak these magic numbers (1200 - distance at full brightness)
  145. float fadeOut = (1200.0f*1200.0f) / (dist*dist);
  146. fadeOut = clamp( fadeOut, 0.0f, 1.0f );
  147. if (rendermode != kRenderWorldGlow)
  148. {
  149. // Make the glow fixed size in screen space, taking into consideration the scale setting.
  150. if ( *pscale == 0.0f )
  151. {
  152. *pscale = 1.0f;
  153. }
  154. *pscale *= dist * (1.0f/200.0f);
  155. }
  156. return fadeOut * brightness;
  157. }
  158. static float SpriteAspect( CEngineSprite *pSprite )
  159. {
  160. if ( pSprite )
  161. {
  162. float x = fabsf(pSprite->GetRight() - pSprite->GetLeft());
  163. float y = fabsf(pSprite->GetDown() - pSprite->GetUp());
  164. if ( y != 0 && x != 0 )
  165. {
  166. return x / y;
  167. }
  168. }
  169. return 1.0f;
  170. }
  171. float C_SpriteRenderer::GlowBlend( CEngineSprite *psprite, const Vector& entorigin, int rendermode, int renderfx, int alpha, float *pscale )
  172. {
  173. pixelvis_queryparams_t params;
  174. float aspect = SpriteAspect(psprite);
  175. params.Init( entorigin, PIXELVIS_DEFAULT_PROXY_SIZE, aspect );
  176. return StandardGlowBlend( params, &m_queryHandle, rendermode, renderfx, alpha, pscale );
  177. }
  178. // since sprites can network down a glow proxy size, handle that here
  179. float CSprite::GlowBlend( CEngineSprite *psprite, const Vector& entorigin, int rendermode, int renderfx, int alpha, float *pscale )
  180. {
  181. pixelvis_queryparams_t params;
  182. float aspect = SpriteAspect(psprite);
  183. params.Init( entorigin, m_flGlowProxySize, aspect );
  184. return StandardGlowBlend( params, &m_queryHandle, rendermode, renderfx, alpha, pscale );
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Purpose: Determine sprite orientation axes
  188. // Input : type -
  189. // forward -
  190. // right -
  191. // up -
  192. //-----------------------------------------------------------------------------
  193. void C_SpriteRenderer::GetSpriteAxes( SPRITETYPE type,
  194. const Vector& origin,
  195. const QAngle& angles,
  196. Vector& forward,
  197. Vector& right,
  198. Vector& up )
  199. {
  200. int i;
  201. float dot, angle, sr, cr;
  202. Vector tvec;
  203. // Automatically roll parallel sprites if requested
  204. if ( angles[2] != 0 && type == SPR_VP_PARALLEL )
  205. {
  206. type = SPR_VP_PARALLEL_ORIENTED;
  207. }
  208. switch( type )
  209. {
  210. case SPR_FACING_UPRIGHT:
  211. {
  212. // generate the sprite's axes, with vup straight up in worldspace, and
  213. // r_spritedesc.vright perpendicular to modelorg.
  214. // This will not work if the view direction is very close to straight up or
  215. // down, because the cross product will be between two nearly parallel
  216. // vectors and starts to approach an undefined state, so we don't draw if
  217. // the two vectors are less than 1 degree apart
  218. tvec[0] = -origin[0];
  219. tvec[1] = -origin[1];
  220. tvec[2] = -origin[2];
  221. VectorNormalize (tvec);
  222. dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because
  223. // r_spritedesc.vup is 0, 0, 1
  224. if ((dot > 0.999848f) || (dot < -0.999848f)) // cos(1 degree) = 0.999848
  225. return;
  226. up[0] = 0;
  227. up[1] = 0;
  228. up[2] = 1;
  229. right[0] = tvec[1];
  230. // CrossProduct(r_spritedesc.vup, -modelorg,
  231. right[1] = -tvec[0];
  232. // r_spritedesc.vright)
  233. right[2] = 0;
  234. VectorNormalize (right);
  235. forward[0] = -right[1];
  236. forward[1] = right[0];
  237. forward[2] = 0;
  238. // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
  239. // r_spritedesc.vpn)
  240. }
  241. break;
  242. case SPR_VP_PARALLEL:
  243. {
  244. // generate the sprite's axes, completely parallel to the viewplane. There
  245. // are no problem situations, because the sprite is always in the same
  246. // position relative to the viewer
  247. for (i=0 ; i<3 ; i++)
  248. {
  249. up[i] = CurrentViewUp()[i];
  250. right[i] = CurrentViewRight()[i];
  251. forward[i] = CurrentViewForward()[i];
  252. }
  253. }
  254. break;
  255. case SPR_VP_PARALLEL_UPRIGHT:
  256. {
  257. // generate the sprite's axes, with g_vecVUp straight up in worldspace, and
  258. // r_spritedesc.vright parallel to the viewplane.
  259. // This will not work if the view direction is very close to straight up or
  260. // down, because the cross product will be between two nearly parallel
  261. // vectors and starts to approach an undefined state, so we don't draw if
  262. // the two vectors are less than 1 degree apart
  263. dot = CurrentViewForward()[2]; // same as DotProduct (vpn, r_spritedesc.g_vecVUp) because
  264. // r_spritedesc.vup is 0, 0, 1
  265. if ((dot > 0.999848f) || (dot < -0.999848f)) // cos(1 degree) = 0.999848
  266. return;
  267. up[0] = 0;
  268. up[1] = 0;
  269. up[2] = 1;
  270. right[0] = CurrentViewForward()[1];
  271. // CrossProduct (r_spritedesc.vup, vpn,
  272. right[1] = -CurrentViewForward()[0]; // r_spritedesc.vright)
  273. right[2] = 0;
  274. VectorNormalize (right);
  275. forward[0] = -right[1];
  276. forward[1] = right[0];
  277. forward[2] = 0;
  278. // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
  279. // r_spritedesc.vpn)
  280. }
  281. break;
  282. case SPR_ORIENTED:
  283. {
  284. // generate the sprite's axes, according to the sprite's world orientation
  285. AngleVectors( angles, &forward, &right, &up );
  286. }
  287. break;
  288. case SPR_VP_PARALLEL_ORIENTED:
  289. {
  290. // generate the sprite's axes, parallel to the viewplane, but rotated in
  291. // that plane around the center according to the sprite entity's roll
  292. // angle. So vpn stays the same, but vright and vup rotate
  293. angle = angles[ROLL] * (M_PI*2.0f/360.0f);
  294. SinCos( angle, &sr, &cr );
  295. for (i=0 ; i<3 ; i++)
  296. {
  297. forward[i] = CurrentViewForward()[i];
  298. right[i] = CurrentViewRight()[i] * cr + CurrentViewUp()[i] * sr;
  299. up[i] = CurrentViewRight()[i] * -sr + CurrentViewUp()[i] * cr;
  300. }
  301. }
  302. break;
  303. default:
  304. Warning( "GetSpriteAxes: Bad sprite type %d\n", type );
  305. break;
  306. }
  307. }
  308. //-----------------------------------------------------------------------------
  309. // Purpose:
  310. // Output : int
  311. //-----------------------------------------------------------------------------
  312. int C_SpriteRenderer::DrawSprite(
  313. IClientEntity *entity,
  314. const model_t *model,
  315. const Vector& origin,
  316. const QAngle& angles,
  317. float frame,
  318. IClientEntity *attachedto,
  319. int attachmentindex,
  320. int rendermode,
  321. int renderfx,
  322. int alpha,
  323. int r,
  324. int g,
  325. int b,
  326. float scale,
  327. float flHDRColorScale
  328. )
  329. {
  330. VPROF_BUDGET( "C_SpriteRenderer::DrawSprite", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  331. if ( !r_drawsprites.GetBool() || !model || modelinfo->GetModelType( model ) != mod_sprite )
  332. {
  333. return 0;
  334. }
  335. // Get extra data
  336. CEngineSprite *psprite = (CEngineSprite *)modelinfo->GetModelExtraData( model );
  337. if ( !psprite )
  338. {
  339. return 0;
  340. }
  341. Vector effect_origin;
  342. VectorCopy( origin, effect_origin );
  343. // Use attachment point
  344. if ( attachedto )
  345. {
  346. C_BaseEntity *ent = attachedto->GetBaseEntity();
  347. if ( ent )
  348. {
  349. // don't draw viewmodel effects in reflections
  350. if ( CurrentViewID() == VIEW_REFLECTION )
  351. {
  352. int group = ent->GetRenderGroup();
  353. if ( group == RENDER_GROUP_VIEW_MODEL_TRANSLUCENT || group == RENDER_GROUP_VIEW_MODEL_OPAQUE )
  354. return 0;
  355. }
  356. QAngle temp;
  357. ent->GetAttachment( attachmentindex, effect_origin, temp );
  358. }
  359. }
  360. if ( rendermode != kRenderNormal )
  361. {
  362. float blend = render->GetBlend();
  363. // kRenderGlow and kRenderWorldGlow have a special blending function
  364. if (( rendermode == kRenderGlow ) || ( rendermode == kRenderWorldGlow ))
  365. {
  366. blend *= GlowBlend( psprite, effect_origin, rendermode, renderfx, alpha, &scale );
  367. // Fade out the sprite depending on distance from the view origin.
  368. r *= blend;
  369. g *= blend;
  370. b *= blend;
  371. }
  372. render->SetBlend( blend );
  373. if ( blend <= 0.0f )
  374. {
  375. return 0;
  376. }
  377. }
  378. // Get orthonormal basis
  379. Vector forward, right, up;
  380. GetSpriteAxes( (SPRITETYPE)psprite->GetOrientation(), origin, angles, forward, right, up );
  381. // Draw
  382. DrawSpriteModel(
  383. entity,
  384. psprite,
  385. effect_origin,
  386. scale,
  387. frame,
  388. rendermode,
  389. r,
  390. g,
  391. b,
  392. alpha,
  393. forward, right, up, flHDRColorScale );
  394. return 1;
  395. }
  396. //-----------------------------------------------------------------------------
  397. // Purpose:
  398. //-----------------------------------------------------------------------------
  399. void CSprite::GetToolRecordingState( KeyValues *msg )
  400. {
  401. if ( !ToolsEnabled() )
  402. return;
  403. VPROF_BUDGET( "CSprite::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
  404. BaseClass::GetToolRecordingState( msg );
  405. // Use attachment point
  406. if ( m_hAttachedToEntity )
  407. {
  408. C_BaseEntity *ent = m_hAttachedToEntity->GetBaseEntity();
  409. if ( ent )
  410. {
  411. BaseEntityRecordingState_t *pState = (BaseEntityRecordingState_t*)msg->GetPtr( "baseentity" );
  412. // override position if we're driven by an attachment
  413. QAngle temp;
  414. pState->m_vecRenderOrigin = GetAbsOrigin();
  415. ent->GetAttachment( m_nAttachment, pState->m_vecRenderOrigin, temp );
  416. // override viewmodel if we're driven by an attachment
  417. bool bViewModel = dynamic_cast< C_BaseViewModel* >( ent ) != NULL;
  418. msg->SetInt( "viewmodel", bViewModel );
  419. }
  420. }
  421. float renderscale = GetRenderScale();
  422. if ( m_bWorldSpaceScale )
  423. {
  424. CEngineSprite *psprite = ( CEngineSprite * )modelinfo->GetModelExtraData( GetModel() );
  425. float flMinSize = MIN( psprite->GetWidth(), psprite->GetHeight() );
  426. renderscale /= flMinSize;
  427. }
  428. // sprite params
  429. static SpriteRecordingState_t state;
  430. state.m_flRenderScale = renderscale;
  431. state.m_flFrame = m_flFrame;
  432. state.m_flProxyRadius = m_flGlowProxySize;
  433. state.m_nRenderMode = GetRenderMode();
  434. state.m_nRenderFX = m_nRenderFX;
  435. state.m_Color.SetColor( m_clrRender.GetR(), m_clrRender.GetG(), m_clrRender.GetB(), GetRenderBrightness() );
  436. msg->SetPtr( "sprite", &state );
  437. }