Counter Strike : Global Offensive Source Code
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.

687 lines
18 KiB

  1. //===== Copyright � 1996-2005, 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. #include "mapentities_shared.h"
  24. #include "gamestringpool.h"
  25. // memdbgon must be the last include file in a .cpp file!!!
  26. #include "tier0/memdbgon.h"
  27. ConVar r_drawsprites( "r_drawsprites", "1", FCVAR_CHEAT );
  28. //-----------------------------------------------------------------------------
  29. // Purpose: Generic sprite model renderer
  30. // Input : *baseentity -
  31. // *psprite -
  32. // fscale -
  33. // frame -
  34. // rendermode -
  35. // r -
  36. // g -
  37. // b -
  38. // a -
  39. // forward -
  40. // right -
  41. // up -
  42. //-----------------------------------------------------------------------------
  43. static unsigned int s_nHDRColorScaleCache = 0;
  44. void DrawSpriteModel( IClientEntity *baseentity, CEngineSprite *psprite, const Vector &origin, float fscale, float frame,
  45. int rendermode, int r, int g, int b, int a, const Vector& forward, const Vector& right, const Vector& up, float flHDRColorScale )
  46. {
  47. float scale;
  48. IMaterial *material;
  49. // don't even bother culling, because it's just a single
  50. // polygon without a surface cache
  51. if ( fscale > 0 )
  52. scale = fscale;
  53. else
  54. scale = 1.0f;
  55. if ( rendermode == kRenderNormal )
  56. {
  57. render->SetBlend( 1.0f );
  58. }
  59. material = psprite->GetMaterial( (RenderMode_t)rendermode, frame );
  60. if ( !material )
  61. return;
  62. CMatRenderContextPtr pRenderContext( materials );
  63. if ( ShouldDrawInWireFrameMode() || r_drawsprites.GetInt() == 2 )
  64. {
  65. IMaterial *pMaterial = materials->FindMaterial( "debug/debugspritewireframe", TEXTURE_GROUP_OTHER );
  66. pRenderContext->Bind( pMaterial, NULL );
  67. }
  68. else
  69. {
  70. pRenderContext->Bind( material, (IClientRenderable*)baseentity );
  71. }
  72. unsigned char color[4];
  73. color[0] = r;
  74. color[1] = g;
  75. color[2] = b;
  76. color[3] = a;
  77. IMaterialVar *pHDRColorScaleVar = material->FindVarFast( "$HDRCOLORSCALE", &s_nHDRColorScaleCache );
  78. if( pHDRColorScaleVar )
  79. {
  80. pHDRColorScaleVar->SetVecValue( flHDRColorScale, flHDRColorScale, flHDRColorScale );
  81. }
  82. Vector point;
  83. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  84. CMeshBuilder meshBuilder;
  85. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  86. Vector vec_a;
  87. Vector vec_b;
  88. Vector vec_c;
  89. Vector vec_d;
  90. // isolate common terms
  91. VectorMA( origin, psprite->GetDown() * scale, up, vec_a );
  92. VectorScale( right, psprite->GetLeft() * scale, vec_b );
  93. VectorMA( origin, psprite->GetUp() * scale, up, vec_c );
  94. VectorScale( right, psprite->GetRight() * scale, vec_d );
  95. float flMinU, flMinV, flMaxU, flMaxV;
  96. psprite->GetTexCoordRange( &flMinU, &flMinV, &flMaxU, &flMaxV );
  97. meshBuilder.Color4ubv( color );
  98. meshBuilder.TexCoord2f( 0, flMinU, flMaxV );
  99. VectorAdd( vec_a, vec_b, point );
  100. meshBuilder.Position3fv( point.Base() );
  101. meshBuilder.AdvanceVertex();
  102. meshBuilder.Color4ubv( color );
  103. meshBuilder.TexCoord2f( 0, flMinU, flMinV );
  104. VectorAdd( vec_c, vec_b, point );
  105. meshBuilder.Position3fv( point.Base() );
  106. meshBuilder.AdvanceVertex();
  107. meshBuilder.Color4ubv( color );
  108. meshBuilder.TexCoord2f( 0, flMaxU, flMinV );
  109. VectorAdd( vec_c, vec_d, point );
  110. meshBuilder.Position3fv( point.Base() );
  111. meshBuilder.AdvanceVertex();
  112. meshBuilder.Color4ubv( color );
  113. meshBuilder.TexCoord2f( 0, flMaxU, flMaxV );
  114. VectorAdd( vec_a, vec_d, point );
  115. meshBuilder.Position3fv( point.Base() );
  116. meshBuilder.AdvanceVertex();
  117. meshBuilder.End();
  118. pMesh->Draw();
  119. }
  120. //-----------------------------------------------------------------------------
  121. // Purpose: Determine glow brightness/scale based on distance to render origin and trace results
  122. // Input : entorigin -
  123. // rendermode -
  124. // renderfx -
  125. // alpha -
  126. // pscale - Pointer to the value for scale, will be changed based on distance and rendermode.
  127. //-----------------------------------------------------------------------------
  128. float StandardGlowBlend( const pixelvis_queryparams_t &params, pixelvis_handle_t *queryHandle, int rendermode, int renderfx, int alpha, float *pscale )
  129. {
  130. float dist;
  131. float brightness;
  132. brightness = PixelVisibility_FractionVisible( params, queryHandle );
  133. if ( brightness <= 0.0f )
  134. {
  135. return 0.0f;
  136. }
  137. dist = GlowSightDistance( params.position, false );
  138. if ( dist <= 0.0f )
  139. {
  140. return 0.0f;
  141. }
  142. if ( renderfx == kRenderFxNoDissipation )
  143. {
  144. return (float)alpha * (1.0f/255.0f) * brightness;
  145. }
  146. // UNDONE: Tweak these magic numbers (1200 - distance at full brightness)
  147. float fadeOut = 1.0f;
  148. if (rendermode == kRenderWorldGlow)
  149. {
  150. fadeOut = ( 2400.0f*2400.0f ) / ( dist*dist );
  151. fadeOut = clamp( fadeOut, 0.0f, 1.0f );
  152. }
  153. else
  154. {
  155. fadeOut = ( 1200.0f*1200.0f ) / ( dist*dist );
  156. fadeOut = clamp( fadeOut, 0.0f, 1.0f );
  157. // Make the glow fixed size in screen space, taking into consideration the scale setting.
  158. if ( *pscale == 0.0f )
  159. {
  160. *pscale = 1.0f;
  161. }
  162. *pscale *= dist * (1.0f/200.0f);
  163. }
  164. return fadeOut * brightness;
  165. }
  166. static float SpriteAspect( CEngineSprite *pSprite )
  167. {
  168. if ( pSprite )
  169. {
  170. float x = fabsf(pSprite->GetRight() - pSprite->GetLeft());
  171. float y = fabsf(pSprite->GetDown() - pSprite->GetUp());
  172. if ( y != 0 && x != 0 )
  173. {
  174. return x / y;
  175. }
  176. }
  177. return 1.0f;
  178. }
  179. float C_SpriteRenderer::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, PIXELVIS_DEFAULT_PROXY_SIZE, aspect );
  184. return StandardGlowBlend( params, &m_queryHandle, rendermode, renderfx, alpha, pscale );
  185. }
  186. // since sprites can network down a glow proxy size, handle that here
  187. float CSprite::GlowBlend( CEngineSprite *psprite, const Vector& entorigin, int rendermode, int renderfx, int alpha, float *pscale )
  188. {
  189. pixelvis_queryparams_t params;
  190. float aspect = SpriteAspect(psprite);
  191. params.Init( entorigin, m_flGlowProxySize, aspect );
  192. return StandardGlowBlend( params, &m_queryHandle, rendermode, renderfx, alpha, pscale );
  193. }
  194. //-----------------------------------------------------------------------------
  195. // Purpose: Determine sprite orientation axes
  196. // Input : type -
  197. // forward -
  198. // right -
  199. // up -
  200. //-----------------------------------------------------------------------------
  201. void C_SpriteRenderer::GetSpriteAxes( SPRITETYPE type,
  202. const Vector& origin,
  203. const QAngle& angles,
  204. Vector& forward,
  205. Vector& right,
  206. Vector& up )
  207. {
  208. int i;
  209. float dot, angle, sr, cr;
  210. Vector tvec;
  211. // Automatically roll parallel sprites if requested
  212. if ( angles[2] != 0 && type == SPR_VP_PARALLEL )
  213. {
  214. type = SPR_VP_PARALLEL_ORIENTED;
  215. }
  216. switch( type )
  217. {
  218. case SPR_FACING_UPRIGHT:
  219. {
  220. // generate the sprite's axes, with vup straight up in worldspace, and
  221. // r_spritedesc.vright perpendicular to modelorg.
  222. // This will not work if the view direction is very close to straight up or
  223. // down, because the cross product will be between two nearly parallel
  224. // vectors and starts to approach an undefined state, so we don't draw if
  225. // the two vectors are less than 1 degree apart
  226. tvec[0] = -origin[0];
  227. tvec[1] = -origin[1];
  228. tvec[2] = -origin[2];
  229. VectorNormalize (tvec);
  230. dot = tvec[2]; // same as DotProduct (tvec, r_spritedesc.vup) because
  231. // r_spritedesc.vup is 0, 0, 1
  232. if ((dot > 0.999848f) || (dot < -0.999848f)) // cos(1 degree) = 0.999848
  233. return;
  234. up[0] = 0;
  235. up[1] = 0;
  236. up[2] = 1;
  237. right[0] = tvec[1];
  238. // CrossProduct(r_spritedesc.vup, -modelorg,
  239. right[1] = -tvec[0];
  240. // r_spritedesc.vright)
  241. right[2] = 0;
  242. VectorNormalize (right);
  243. forward[0] = -right[1];
  244. forward[1] = right[0];
  245. forward[2] = 0;
  246. // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
  247. // r_spritedesc.vpn)
  248. }
  249. break;
  250. case SPR_VP_PARALLEL:
  251. {
  252. // generate the sprite's axes, completely parallel to the viewplane. There
  253. // are no problem situations, because the sprite is always in the same
  254. // position relative to the viewer
  255. for (i=0 ; i<3 ; i++)
  256. {
  257. up[i] = CurrentViewUp()[i];
  258. right[i] = CurrentViewRight()[i];
  259. forward[i] = CurrentViewForward()[i];
  260. }
  261. }
  262. break;
  263. case SPR_VP_PARALLEL_UPRIGHT:
  264. {
  265. // generate the sprite's axes, with g_vecVUp straight up in worldspace, and
  266. // r_spritedesc.vright parallel to the viewplane.
  267. // This will not work if the view direction is very close to straight up or
  268. // down, because the cross product will be between two nearly parallel
  269. // vectors and starts to approach an undefined state, so we don't draw if
  270. // the two vectors are less than 1 degree apart
  271. dot = CurrentViewForward()[2]; // same as DotProduct (vpn, r_spritedesc.g_vecVUp) because
  272. // r_spritedesc.vup is 0, 0, 1
  273. if ((dot > 0.999848f) || (dot < -0.999848f)) // cos(1 degree) = 0.999848
  274. return;
  275. up[0] = 0;
  276. up[1] = 0;
  277. up[2] = 1;
  278. right[0] = CurrentViewForward()[1];
  279. // CrossProduct (r_spritedesc.vup, vpn,
  280. right[1] = -CurrentViewForward()[0]; // r_spritedesc.vright)
  281. right[2] = 0;
  282. VectorNormalize (right);
  283. forward[0] = -right[1];
  284. forward[1] = right[0];
  285. forward[2] = 0;
  286. // CrossProduct (r_spritedesc.vright, r_spritedesc.vup,
  287. // r_spritedesc.vpn)
  288. }
  289. break;
  290. case SPR_ORIENTED:
  291. {
  292. // generate the sprite's axes, according to the sprite's world orientation
  293. AngleVectors( angles, &forward, &right, &up );
  294. }
  295. break;
  296. case SPR_VP_PARALLEL_ORIENTED:
  297. {
  298. // generate the sprite's axes, parallel to the viewplane, but rotated in
  299. // that plane around the center according to the sprite entity's roll
  300. // angle. So vpn stays the same, but vright and vup rotate
  301. angle = angles[ROLL] * (M_PI*2.0f/360.0f);
  302. SinCos( angle, &sr, &cr );
  303. for (i=0 ; i<3 ; i++)
  304. {
  305. forward[i] = CurrentViewForward()[i];
  306. right[i] = CurrentViewRight()[i] * cr + CurrentViewUp()[i] * sr;
  307. up[i] = CurrentViewRight()[i] * -sr + CurrentViewUp()[i] * cr;
  308. }
  309. }
  310. break;
  311. default:
  312. Warning( "GetSpriteAxes: Bad sprite type %d\n", type );
  313. break;
  314. }
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose:
  318. // Output : int
  319. //-----------------------------------------------------------------------------
  320. int C_SpriteRenderer::DrawSprite(
  321. IClientEntity *entity,
  322. const model_t *model,
  323. const Vector& origin,
  324. const QAngle& angles,
  325. float frame,
  326. IClientEntity *attachedto,
  327. int attachmentindex,
  328. int rendermode,
  329. int renderfx,
  330. int alpha,
  331. int r,
  332. int g,
  333. int b,
  334. float scale,
  335. float flHDRColorScale
  336. )
  337. {
  338. VPROF_BUDGET( "C_SpriteRenderer::DrawSprite", VPROF_BUDGETGROUP_PARTICLE_RENDERING );
  339. if ( !r_drawsprites.GetBool() || !model || modelinfo->GetModelType( model ) != mod_sprite )
  340. {
  341. return 0;
  342. }
  343. // Get extra data
  344. CEngineSprite *psprite = (CEngineSprite *)modelinfo->GetModelExtraData( model );
  345. if ( !psprite )
  346. {
  347. return 0;
  348. }
  349. Vector effect_origin;
  350. VectorCopy( origin, effect_origin );
  351. // Use attachment point
  352. if ( attachedto )
  353. {
  354. C_BaseEntity *ent = attachedto->GetBaseEntity();
  355. if ( ent )
  356. {
  357. // don't draw viewmodel effects in reflections
  358. if ( CurrentViewID() == VIEW_REFLECTION )
  359. {
  360. if ( g_pClientLeafSystem->IsRenderingWithViewModels( ent->RenderHandle() ) )
  361. return 0;
  362. }
  363. QAngle temp;
  364. ent->GetAttachment( attachmentindex, effect_origin, temp );
  365. }
  366. }
  367. if ( rendermode != kRenderNormal )
  368. {
  369. float blend = render->GetBlend();
  370. // kRenderGlow and kRenderWorldGlow have a special blending function
  371. if (( rendermode == kRenderGlow ) || ( rendermode == kRenderWorldGlow ))
  372. {
  373. blend *= GlowBlend( psprite, effect_origin, rendermode, renderfx, alpha, &scale );
  374. // Fade out the sprite depending on distance from the view origin.
  375. r *= blend;
  376. g *= blend;
  377. b *= blend;
  378. }
  379. render->SetBlend( blend );
  380. if ( blend <= 0.0f )
  381. {
  382. return 0;
  383. }
  384. }
  385. // Get orthonormal basis
  386. Vector forward, right, up;
  387. GetSpriteAxes( (SPRITETYPE)psprite->GetOrientation(), origin, angles, forward, right, up );
  388. // Draw
  389. DrawSpriteModel(
  390. entity,
  391. psprite,
  392. effect_origin,
  393. scale,
  394. frame,
  395. rendermode,
  396. r,
  397. g,
  398. b,
  399. alpha,
  400. forward, right, up, flHDRColorScale );
  401. return 1;
  402. }
  403. //-----------------------------------------------------------------------------
  404. // Purpose:
  405. //-----------------------------------------------------------------------------
  406. void CSprite::GetToolRecordingState( KeyValues *msg )
  407. {
  408. if ( !ToolsEnabled() )
  409. return;
  410. VPROF_BUDGET( "CSprite::GetToolRecordingState", VPROF_BUDGETGROUP_TOOLS );
  411. BaseClass::GetToolRecordingState( msg );
  412. // Use attachment point
  413. if ( m_hAttachedToEntity )
  414. {
  415. C_BaseEntity *ent = m_hAttachedToEntity->GetBaseEntity();
  416. if ( ent )
  417. {
  418. BaseEntityRecordingState_t *pState = (BaseEntityRecordingState_t*)msg->GetPtr( "baseentity" );
  419. // override position if we're driven by an attachment
  420. QAngle temp;
  421. pState->m_vecRenderOrigin = GetAbsOrigin();
  422. ent->GetAttachment( m_nAttachment, pState->m_vecRenderOrigin, temp );
  423. // override viewmodel if we're driven by an attachment
  424. bool bViewModel = ToBaseViewModel( ent ) != NULL;
  425. msg->SetInt( "viewmodel", bViewModel );
  426. }
  427. }
  428. float renderscale = GetRenderScale();
  429. if ( m_bWorldSpaceScale )
  430. {
  431. CEngineSprite *psprite = ( CEngineSprite * )modelinfo->GetModelExtraData( GetModel() );
  432. float flMinSize = MIN( psprite->GetWidth(), psprite->GetHeight() );
  433. renderscale /= flMinSize;
  434. }
  435. color24 c = GetRenderColor();
  436. // sprite params
  437. static SpriteRecordingState_t state;
  438. state.m_flRenderScale = renderscale;
  439. state.m_flFrame = m_flFrame;
  440. state.m_flProxyRadius = m_flGlowProxySize;
  441. state.m_nRenderMode = GetRenderMode();
  442. state.m_nRenderFX = GetRenderFX() ? true : false;
  443. state.m_Color.SetColor( c.r, c.g, c.b, GetRenderBrightness() );
  444. msg->SetPtr( "sprite", &state );
  445. }
  446. CUtlVector< CSprite * > g_ClientsideSprites;
  447. // ===================== For clientside spawning of sprites =====================
  448. void CSprite::RecreateAllClientside()
  449. {
  450. DestroyAllClientside();
  451. ParseAllClientsideEntities( engine->GetMapEntitiesString() );
  452. }
  453. void CSprite::DestroyAllClientside()
  454. {
  455. // This only gets called during LevelInitPostEntity and LevelShutdown so we're going to use
  456. // Release() instead of Remove() or UTIL_Remove
  457. while ( g_ClientsideSprites.Count() > 0 )
  458. {
  459. CSprite *p = g_ClientsideSprites[0];
  460. // This will call into CSprite::~CSprite in sprite.cpp, which will FindAndRemove this sprite from the array
  461. p->Release();
  462. }
  463. }
  464. bool CSprite::InitializeClientside()
  465. {
  466. if ( InitializeAsClientEntity( STRING( GetModelName() ), false ) == false )
  467. {
  468. return false;
  469. }
  470. m_bClientOnly = true;
  471. g_ClientsideSprites.AddToTail( this );
  472. Spawn();
  473. const model_t *mod = GetModel();
  474. if ( mod )
  475. {
  476. Vector mins, maxs;
  477. modelinfo->GetModelBounds( mod, mins, maxs );
  478. SetCollisionBounds( mins, maxs );
  479. }
  480. SetBlocksLOS( false ); // this should be a small object
  481. SetNextClientThink( CLIENT_THINK_NEVER );
  482. return true;
  483. }
  484. const char *CSprite::ParseClientsideEntity( const char *pEntData )
  485. {
  486. CEntityMapData entData( (char*)pEntData );
  487. char className[MAPKEY_MAXLENGTH];
  488. MDLCACHE_CRITICAL_SECTION();
  489. if ( !entData.ExtractValue( "classname", className ) )
  490. {
  491. Error( "classname missing from entity!\n" );
  492. }
  493. if ( !Q_strcmp( className, "env_sprite_clientside" ))
  494. {
  495. // always force clientside entities placed in maps
  496. CSprite *pEntity = new CSprite();
  497. if ( pEntity )
  498. {
  499. // Set up keyvalues.
  500. pEntity->ParseMapData(&entData);
  501. if ( !pEntity->InitializeClientside() )
  502. pEntity->Release();
  503. return entData.CurrentBufferPosition();
  504. }
  505. }
  506. // Just skip past all the keys.
  507. char keyName[MAPKEY_MAXLENGTH];
  508. char value[MAPKEY_MAXLENGTH];
  509. if ( entData.GetFirstKey(keyName, value) )
  510. {
  511. do
  512. {
  513. }
  514. while ( entData.GetNextKey(keyName, value) );
  515. }
  516. //
  517. // Return the current parser position in the data block
  518. //
  519. return entData.CurrentBufferPosition();
  520. }
  521. bool CSprite::KeyValue( const char *szKeyName, const char *szValue )
  522. {
  523. if ( FStrEq( szKeyName, "scale" ) )
  524. {
  525. m_flSpriteScale = atof(szValue);
  526. }
  527. else if ( FStrEq( szKeyName, "framerate" ) )
  528. {
  529. m_flSpriteFramerate = atof(szValue);
  530. }
  531. else if ( FStrEq( szKeyName, "GlowProxySize" ) )
  532. {
  533. m_flGlowProxySize = atof(szValue);
  534. }
  535. else if ( FStrEq( szKeyName, "frame" ) )
  536. {
  537. m_flFrame = atof(szValue);
  538. }
  539. else if ( FStrEq( szKeyName, "HDRColorScale" ) )
  540. {
  541. m_flHDRColorScale = atof(szValue);
  542. }
  543. else if ( FStrEq( szKeyName, "rendermode" ) )
  544. {
  545. SetRenderMode( (RenderMode_t) atoi( szValue ) );
  546. }
  547. else if ( FStrEq( szKeyName, "model" ) )
  548. {
  549. SetModelName( AllocPooledString( szValue ) );
  550. }
  551. else
  552. {
  553. return BaseClass::KeyValue( szKeyName, szValue );
  554. }
  555. return true;
  556. }
  557. //-----------------------------------------------------------------------------
  558. // Purpose: Only called on BSP load. Parses and spawns all the entities in the BSP.
  559. // Input : pMapData - Pointer to the entity data block to parse.
  560. //-----------------------------------------------------------------------------
  561. void CSprite::ParseAllClientsideEntities(const char *pMapData)
  562. {
  563. int nEntities = 0;
  564. char szTokenBuffer[MAPKEY_MAXLENGTH];
  565. //
  566. // Loop through all entities in the map data, creating each.
  567. //
  568. for ( ; true; pMapData = MapEntity_SkipToNextEntity( pMapData, szTokenBuffer ) )
  569. {
  570. //
  571. // Parse the opening brace.
  572. //
  573. char token[MAPKEY_MAXLENGTH];
  574. pMapData = MapEntity_ParseToken( pMapData, token );
  575. //
  576. // Check to see if we've finished or not.
  577. //
  578. if ( !pMapData )
  579. break;
  580. if ( token[0] != '{' )
  581. {
  582. Error( "CSprite::ParseAllEntities: found %s when expecting {", token);
  583. continue;
  584. }
  585. //
  586. // Parse the entity and add it to the spawn list.
  587. //
  588. pMapData = ParseClientsideEntity( pMapData );
  589. nEntities++;
  590. }
  591. }