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.

1001 lines
33 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Workfile: $
  6. // $Date: $
  7. // $NoKeywords: $
  8. //=============================================================================//
  9. #include "cbase.h"
  10. #include "c_basetempentity.h"
  11. #include "iefx.h"
  12. #include "fx.h"
  13. #include "decals.h"
  14. #include "materialsystem/imaterialsystem.h"
  15. #include "filesystem.h"
  16. #include "materialsystem/imaterial.h"
  17. #include "materialsystem/itexture.h"
  18. #include "materialsystem/imaterialvar.h"
  19. #include "functionproxy.h"
  20. #include "imaterialproxydict.h"
  21. #include "precache_register.h"
  22. #include "econ/econ_item_schema.h"
  23. #include "tier0/vprof.h"
  24. #include "playerdecals_signature.h"
  25. #include "tier1/callqueue.h"
  26. #include "engine/decal_flags.h"
  27. #include "cstrike15/Scaleform/HUD/sfhud_rosettaselector.h"
  28. #include "c_cs_player.h"
  29. // memdbgon must be the last include file in a .cpp file!!!
  30. #include "tier0/memdbgon.h"
  31. PRECACHE_REGISTER_BEGIN( GLOBAL, PrecachePlayerDecal )
  32. PRECACHE( MATERIAL, "decals/playerlogo01" )
  33. PRECACHE_REGISTER_END()
  34. #define PLAYERDECAL_VERBOSE_DEBUG 0
  35. void QcCreatePreviewDecal( uint32 nStickerKitDefinition, uint32 nTintID, const trace_t& trace, const Vector* pRight );
  36. //-----------------------------------------------------------------------------
  37. // Purpose: Player Decal TE
  38. //-----------------------------------------------------------------------------
  39. class C_TEPlayerDecal : public C_BaseTempEntity
  40. {
  41. public:
  42. DECLARE_CLASS( C_TEPlayerDecal, C_BaseTempEntity );
  43. DECLARE_CLIENTCLASS();
  44. C_TEPlayerDecal( void );
  45. virtual ~C_TEPlayerDecal( void );
  46. virtual void PostDataUpdate( DataUpdateType_t updateType );
  47. virtual void Precache( void );
  48. public:
  49. int m_nPlayer;
  50. Vector m_vecOrigin;
  51. Vector m_vecStart;
  52. Vector m_vecRight;
  53. int m_nEntity;
  54. int m_nHitbox;
  55. };
  56. int g_nPlayerLogoProxyForPreviewKey = ( 8 << 24 ) | 0x1FFFF; // used for preview (model panel and in-game)
  57. // there's a rendering batching approach that uses only low 16-bits of the proxy data for splitting decals into batches, this is sufficient for now, but
  58. // eventually we might want to find all places that use only 16-bits and upgrade them to full proxy data value comparison, e.g. patterns like this:
  59. // ( unPlayerDecalStickerKitID && ( pDecal->flags & FDECAL_PLAYERSPRAY ) && ( uint16( reinterpret_cast< uintp >( pDecal->userdata ) ) != unPlayerDecalStickerKitID ) )
  60. inline int MakeKey( int nUniqueId )
  61. {
  62. return ( 1 << 24 ) | nUniqueId;
  63. }
  64. // This whole thing exists so we can shunt data from the main thread to the material thread.
  65. // We will queue calls to create decal data and DeleteDecalData so that the material thread doesn't
  66. // have to grab a mutex to examine s_mapUniqueId2DecalData.
  67. struct DecalData_t
  68. {
  69. ITexture* m_pTex;
  70. int m_nRarity;
  71. int m_nTintID;
  72. float m_flCreationTime;
  73. };
  74. static CUtlMap< int, DecalData_t, int, CDefLess< int > > s_mapUniqueId2DecalData;
  75. void CreateDecalData( int nKey, ITexture* pTex, int nRarity, int nTintID, float flCreateTime )
  76. {
  77. Assert( pTex );
  78. if ( !pTex )
  79. return;
  80. int itExisting = s_mapUniqueId2DecalData.Find( nKey );
  81. if ( itExisting != s_mapUniqueId2DecalData.InvalidIndex() )
  82. {
  83. DecalData_t &ddata = s_mapUniqueId2DecalData[ itExisting ];
  84. // Release the old texture
  85. Assert( ddata.m_pTex );
  86. ddata.m_pTex->DecrementReferenceCount();
  87. ddata.m_pTex = NULL;
  88. // Overwrite the data for the new item.
  89. ddata.m_pTex = pTex;
  90. ddata.m_nRarity = nRarity;
  91. ddata.m_nTintID = nTintID;
  92. ddata.m_flCreationTime = flCreateTime;
  93. return;
  94. }
  95. // It wasn't already in the map, so add it.
  96. DecalData_t dd = { pTex, nRarity, nTintID, flCreateTime };
  97. s_mapUniqueId2DecalData.Insert( nKey, dd );
  98. }
  99. void DeleteDecalData( int nKey )
  100. {
  101. int it = s_mapUniqueId2DecalData.Find( nKey );
  102. Assert ( ( nKey == g_nPlayerLogoProxyForPreviewKey )
  103. || ( it != s_mapUniqueId2DecalData.InvalidIndex() ) );
  104. if ( it == s_mapUniqueId2DecalData.InvalidIndex() )
  105. return;
  106. Assert( s_mapUniqueId2DecalData[ it ].m_pTex );
  107. s_mapUniqueId2DecalData[ it ].m_pTex->DecrementReferenceCount();
  108. s_mapUniqueId2DecalData.RemoveAt( it );
  109. }
  110. bool ReadDecalData( int nKey, DecalData_t* pOutDD )
  111. {
  112. if ( !pOutDD )
  113. return false;
  114. int it = s_mapUniqueId2DecalData.Find( nKey );
  115. if ( it == s_mapUniqueId2DecalData.InvalidIndex() )
  116. return false;
  117. ( *pOutDD ) = s_mapUniqueId2DecalData[ it ];
  118. return true;
  119. }
  120. inline bool BShouldHaveDrips( const Vector& normal )
  121. {
  122. return fabs( normal.z ) < 0.8;
  123. }
  124. class C_FEPlayerDecal;
  125. typedef CUtlMap< int, C_FEPlayerDecal *, int, CDefLess< int > > PlayerDecalsByUniqueId_t;
  126. static PlayerDecalsByUniqueId_t s_mapPlayerDecalsUniqueIDsAll; // All client-side player decals entities
  127. static PlayerDecalsByUniqueId_t s_mapPlayerDecalsUniqueIDsToApply; // Player decals entities that should still be applied to the world
  128. static CUtlMap< int, bool, int, CDefLess< int > > s_mapPlayerDecalsUniqueIDsRecreating; // Player decals entities that were temporarily destroyed and getting recreated ID->bReapplyPending
  129. void OnPlayerDecalsLevelShutdown()
  130. { // we purge and re-apply decals here
  131. // so add all client-side decals that we know about into the IDs to apply map
  132. FOR_EACH_MAP( s_mapPlayerDecalsUniqueIDsAll, i )
  133. {
  134. #if PLAYERDECAL_VERBOSE_DEBUG
  135. DevMsg( "DECAL: schedule for reapply ( %d )\n", s_mapPlayerDecalsUniqueIDsAll.Key( i ) );
  136. #endif
  137. s_mapPlayerDecalsUniqueIDsToApply.InsertOrReplace( s_mapPlayerDecalsUniqueIDsAll.Key( i ), s_mapPlayerDecalsUniqueIDsAll.Element( i ) );
  138. }
  139. s_mapPlayerDecalsUniqueIDsRecreating.RemoveAll();
  140. }
  141. class C_FEPlayerDecal : public C_BaseEntity
  142. {
  143. public:
  144. DECLARE_CLASS( C_FEPlayerDecal, C_BaseEntity );
  145. DECLARE_CLIENTCLASS();
  146. C_FEPlayerDecal( void ) {
  147. m_nUniqueID = 0;
  148. m_unAccountID = 0;
  149. m_unTraceID = 0;
  150. m_rtGcTime = 0;
  151. m_vecEndPos.Init();
  152. m_vecStart.Init();
  153. m_vecRight.Init();
  154. m_vecNormal.Init();
  155. m_nPlayer = 0;
  156. m_nEntity = 0;
  157. m_nHitbox = 0;
  158. m_nTintID = 0;
  159. m_flCreationTime = 0;
  160. m_nVersion = 0;
  161. V_memset( m_ubSignature, 0, sizeof( m_ubSignature ) );
  162. m_bDecalReadyToApplyToWorld = false;
  163. }
  164. virtual ~C_FEPlayerDecal( void )
  165. {
  166. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  167. ICallQueue* pCQ = pRenderContext->GetCallQueue();
  168. if ( pCQ )
  169. pCQ->QueueCall( DeleteDecalData, MakeKey( m_nUniqueID ) );
  170. else
  171. DeleteDecalData( MakeKey( m_nUniqueID ) );
  172. if ( m_nUniqueID )
  173. {
  174. #if PLAYERDECAL_VERBOSE_DEBUG
  175. DevMsg( "DECAL: entity removed ( %d ): %s %s %s\n",
  176. m_nUniqueID,
  177. ( ( s_mapPlayerDecalsUniqueIDsToApply.Find( m_nUniqueID ) == s_mapPlayerDecalsUniqueIDsToApply.InvalidIndex() ) ? "" : "{was scheduled for application}" ),
  178. ( ( s_mapPlayerDecalsUniqueIDsAll.Find( m_nUniqueID ) == s_mapPlayerDecalsUniqueIDsAll.InvalidIndex() ) ? "[NOT REGISTERED IN MAP]" : "" ),
  179. ( ( s_mapPlayerDecalsUniqueIDsRecreating.Find( m_nUniqueID ) == s_mapPlayerDecalsUniqueIDsRecreating.InvalidIndex() ) ? "" : ( ( s_mapPlayerDecalsUniqueIDsRecreating.Element( s_mapPlayerDecalsUniqueIDsRecreating.Find( m_nUniqueID ) ) ? "{recreate and reapply pending}" : "{recreating, but already applied}" ) ) ) );
  180. #endif
  181. Assert( ( s_mapPlayerDecalsUniqueIDsToApply.Find( m_nUniqueID ) == s_mapPlayerDecalsUniqueIDsToApply.InvalidIndex() ) ||
  182. ( s_mapPlayerDecalsUniqueIDsToApply.Element( s_mapPlayerDecalsUniqueIDsToApply.Find( m_nUniqueID ) ) == this ) );
  183. s_mapPlayerDecalsUniqueIDsToApply.Remove( m_nUniqueID );
  184. Assert( ( s_mapPlayerDecalsUniqueIDsAll.Find( m_nUniqueID ) != s_mapPlayerDecalsUniqueIDsAll.InvalidIndex() ) &&
  185. ( s_mapPlayerDecalsUniqueIDsAll.Element( s_mapPlayerDecalsUniqueIDsAll.Find( m_nUniqueID ) ) == this ) );
  186. s_mapPlayerDecalsUniqueIDsAll.Remove( m_nUniqueID );
  187. }
  188. }
  189. virtual void PostDataUpdate( DataUpdateType_t updateType );
  190. virtual void SetDestroyedOnRecreateEntities( void ) OVERRIDE;
  191. bool BMakeDecalReadyToApplyToWorld();
  192. void ApplyDecalDataToWorld();
  193. void MakeDecalReady( int nKey );
  194. public:
  195. int m_nUniqueID;
  196. uint32 m_unAccountID;
  197. uint32 m_unTraceID;
  198. uint32 m_rtGcTime;
  199. Vector m_vecEndPos;
  200. Vector m_vecStart;
  201. Vector m_vecRight;
  202. Vector m_vecNormal;
  203. int m_nPlayer;
  204. int m_nEntity;
  205. int m_nHitbox;
  206. int m_nTintID;
  207. float m_flCreationTime;
  208. uint8 m_nVersion;
  209. uint8 m_ubSignature[ PLAYERDECALS_SIGNATURE_BYTELEN ];
  210. private:
  211. bool m_bDecalReadyToApplyToWorld;
  212. };
  213. //-----------------------------------------------------------------------------
  214. // Purpose:
  215. //-----------------------------------------------------------------------------
  216. C_TEPlayerDecal::C_TEPlayerDecal( void )
  217. {
  218. m_nPlayer = 0;
  219. m_vecOrigin.Init();
  220. m_vecStart.Init();
  221. m_vecRight.Init();
  222. m_nEntity = 0;
  223. m_nHitbox = 0;
  224. }
  225. //-----------------------------------------------------------------------------
  226. // Purpose:
  227. //-----------------------------------------------------------------------------
  228. C_TEPlayerDecal::~C_TEPlayerDecal( void )
  229. {
  230. }
  231. //-----------------------------------------------------------------------------
  232. // Purpose:
  233. //-----------------------------------------------------------------------------
  234. void C_TEPlayerDecal::Precache( void )
  235. {
  236. }
  237. //-----------------------------------------------------------------------------
  238. // Purpose:
  239. //-----------------------------------------------------------------------------
  240. void TE_PlayerDecal( IRecipientFilter& filter, float delay,
  241. const Vector* pos, const Vector* start, const Vector* right, int nPlayerAndStickerKitID, int entity, int hitbox, int nAdditionalDecalFlags )
  242. {
  243. // Special case for world entity with hitbox:
  244. trace_t tr;
  245. if ( ( entity == 0 ) && ( hitbox != 0 ) )
  246. {
  247. Ray_t ray;
  248. ray.Init( *start, *pos );
  249. staticpropmgr->AddDecalToStaticProp( *start, *pos, hitbox - 1, nPlayerAndStickerKitID, false, tr, ( void * ) nPlayerAndStickerKitID, right, EDF_PLAYERSPRAY | nAdditionalDecalFlags );
  250. }
  251. else
  252. {
  253. // Only decal the world + brush models
  254. // Here we deal with decals on entities.
  255. C_BaseEntity* ent;
  256. if ( ( ent = cl_entitylist->GetEnt( entity ) ) == NULL )
  257. return;
  258. ent->AddDecal( *start, *pos, *pos, hitbox,
  259. nPlayerAndStickerKitID, false, tr, ADDDECAL_TO_ALL_LODS, right, EDF_PLAYERSPRAY | nAdditionalDecalFlags );
  260. }
  261. }
  262. void C_FEPlayerDecal::SetDestroyedOnRecreateEntities()
  263. {
  264. BaseClass::SetDestroyedOnRecreateEntities();
  265. if ( m_nUniqueID )
  266. { // Remeber that we are going to be recreating this decal entity
  267. s_mapPlayerDecalsUniqueIDsRecreating.InsertOrReplace( m_nUniqueID,
  268. s_mapPlayerDecalsUniqueIDsToApply.Find( m_nUniqueID ) != s_mapPlayerDecalsUniqueIDsToApply.InvalidIndex() );
  269. }
  270. }
  271. void C_FEPlayerDecal::PostDataUpdate( DataUpdateType_t updateType )
  272. {
  273. if ( m_nUniqueID )
  274. {
  275. if ( s_mapPlayerDecalsUniqueIDsAll.Find( m_nUniqueID ) == s_mapPlayerDecalsUniqueIDsAll.InvalidIndex() )
  276. {
  277. int idxRecreate = s_mapPlayerDecalsUniqueIDsRecreating.Find( m_nUniqueID );
  278. bool bApplyImmediate = true;
  279. bool bRecreating = ( idxRecreate != s_mapPlayerDecalsUniqueIDsRecreating.InvalidIndex() );
  280. if ( bRecreating )
  281. {
  282. bApplyImmediate = s_mapPlayerDecalsUniqueIDsRecreating.Element( idxRecreate );
  283. s_mapPlayerDecalsUniqueIDsRecreating.RemoveAt( idxRecreate );
  284. }
  285. #if PLAYERDECAL_VERBOSE_DEBUG
  286. DevMsg( "DECAL: PostDataUpdate %s decal ( %d ) %s\n", bRecreating ? "recreating" : "new", m_nUniqueID, bApplyImmediate ? "apply" : "already applied, skipping application" );
  287. #endif
  288. s_mapPlayerDecalsUniqueIDsAll.InsertOrReplace( m_nUniqueID, this );
  289. if ( bApplyImmediate )
  290. s_mapPlayerDecalsUniqueIDsToApply.InsertOrReplace( m_nUniqueID, this );
  291. }
  292. }
  293. // Make sure that we push render data to QMS thread every time we get data update about the decal
  294. m_bDecalReadyToApplyToWorld = BMakeDecalReadyToApplyToWorld();
  295. }
  296. DEVELOPMENT_ONLY_CONVAR( cl_playerspray_debug_pulse_force, 0 );
  297. // Checks if the local player has an equipped spray and is aiming in a sprayable area with the rosetta menu up and if cooldown is ready
  298. // Note: rosetta menu code is using this check to determine if we're passing all the validity checks to spray.
  299. bool Helper_CanShowPreviewDecal( CEconItemView **ppOutEconItemView = NULL, trace_t* pOutSprayTrace = NULL, Vector *pOutVecPlayerRight = NULL, uint32* pOutUnStickerKitID = NULL )
  300. {
  301. if ( !Helper_CanUseSprays() )
  302. return false;
  303. C_CSPlayer *pLocalPlayer = C_CSPlayer::GetLocalCSPlayer();
  304. if ( !pLocalPlayer )
  305. return false;
  306. if ( !cl_playerspray_debug_pulse_force.GetInt() )
  307. {
  308. // Check if UI is visible
  309. SFHudRosettaSelector* pRosetta = ( SFHudRosettaSelector* ) ( GetHud( 0 ).FindElement( "SFHudRosettaSelector" ) );
  310. if ( !pRosetta || !pRosetta->Visible() || !pRosetta->ShouldDraw() )
  311. return false;
  312. // Check player spray cooldown
  313. if ( pLocalPlayer->GetNextDecalTime() > gpGlobals->curtime )
  314. return false;
  315. }
  316. Vector playerRight;
  317. trace_t sprayTrace;
  318. if ( pLocalPlayer->IsAbleToApplySpray( &sprayTrace, NULL, &playerRight ) )
  319. return false;
  320. if ( pOutSprayTrace )
  321. *pOutSprayTrace = sprayTrace;
  322. if ( pOutVecPlayerRight )
  323. *pOutVecPlayerRight = playerRight;
  324. CCSPlayerInventory* pPlayerInv = CSInventoryManager()->GetLocalCSInventory();
  325. if ( !pPlayerInv )
  326. return false;
  327. CEconItemView* pEconItem = pPlayerInv->GetItemInLoadout( 0, LOADOUT_POSITION_SPRAY0 );
  328. if ( !pEconItem || !pEconItem->IsValid() )
  329. return false;
  330. if ( ppOutEconItemView )
  331. *ppOutEconItemView = pEconItem;
  332. uint32 unStickerKitID = pEconItem->GetStickerAttributeBySlotIndexInt( 0, k_EStickerAttribute_ID, 0 );
  333. if ( !unStickerKitID )
  334. return false;
  335. if ( pOutUnStickerKitID )
  336. *pOutUnStickerKitID = unStickerKitID;
  337. return true;
  338. }
  339. void UpdatePreviewDecal()
  340. {
  341. uint32 unStickerKitID;
  342. trace_t sprayTrace;
  343. Vector playerRight;
  344. CEconItemView *pEconItem;
  345. if ( !Helper_CanShowPreviewDecal( &pEconItem, &sprayTrace, &playerRight, &unStickerKitID ) )
  346. return;
  347. static CSchemaAttributeDefHandle hAttrSprayTintID( "spray tint id" );
  348. uint32 unTintID = 0;
  349. if ( !hAttrSprayTintID || !pEconItem->FindAttribute( hAttrSprayTintID, &unTintID ) )
  350. unTintID = 0;
  351. QcCreatePreviewDecal( unStickerKitID, unTintID, sprayTrace, &playerRight );
  352. }
  353. void OnPlayerDecalsUpdate()
  354. {
  355. FOR_EACH_MAP( s_mapPlayerDecalsUniqueIDsToApply, i )
  356. {
  357. #if PLAYERDECAL_VERBOSE_DEBUG
  358. DevMsg( "DECAL: ApplyDecalDataToWorld( %d )\n", s_mapPlayerDecalsUniqueIDsToApply.Key( i ) );
  359. #endif
  360. s_mapPlayerDecalsUniqueIDsToApply.Element( i )->ApplyDecalDataToWorld();
  361. }
  362. s_mapPlayerDecalsUniqueIDsToApply.RemoveAll();
  363. #if PLAYERDECAL_VERBOSE_DEBUG
  364. if ( s_mapPlayerDecalsUniqueIDsRecreating.Count() )
  365. {
  366. DevMsg( "DECAL: Was recreating %d decals, cleared recreate cache\n", s_mapPlayerDecalsUniqueIDsRecreating.Count() );
  367. }
  368. #endif
  369. s_mapPlayerDecalsUniqueIDsRecreating.RemoveAll();
  370. }
  371. bool C_FEPlayerDecal::BMakeDecalReadyToApplyToWorld()
  372. {
  373. VPROF( "C_FEPlayerDecal::BMakeDecalReadyToApplyToWorld" );
  374. // Validate the signature before applying on the client
  375. PlayerDecalDigitalSignature data;
  376. data.set_accountid( m_unAccountID );
  377. data.set_trace_id( m_unTraceID );
  378. data.set_rtime( m_rtGcTime );
  379. for ( int k = 0; k < 3; ++ k ) data.add_endpos( m_vecEndPos[k] );
  380. for ( int k = 0; k < 3; ++ k ) data.add_startpos( m_vecStart[k] );
  381. for ( int k = 0; k < 3; ++ k ) data.add_right( m_vecRight[k] );
  382. for ( int k = 0; k < 3; ++ k ) data.add_normal( m_vecNormal[k] );
  383. data.set_tx_defidx( m_nPlayer );
  384. data.set_entindex( m_nEntity );
  385. data.set_hitbox( m_nHitbox );
  386. data.set_tint_id( m_nTintID );
  387. data.set_creationtime( m_flCreationTime );
  388. if ( m_nVersion == PLAYERDECALS_SIGNATURE_VERSION )
  389. data.set_signature( &m_ubSignature[0], PLAYERDECALS_SIGNATURE_BYTELEN );
  390. #ifdef _DEBUG
  391. {
  392. float flendpos[ 3 ] = { data.endpos( 0 ), data.endpos( 1 ), data.endpos( 2 ) };
  393. float flstartpos[ 3 ] = { data.startpos( 0 ), data.startpos( 1 ), data.startpos( 2 ) };
  394. float flright[ 3 ] = { data.right( 0 ), data.right( 1 ), data.right( 2 ) };
  395. float flnormal[ 3 ] = { data.normal( 0 ), data.normal( 1 ), data.normal( 2 ) };
  396. DevMsg( "Client signature #%u e(%08X,%08X,%08X) s(%08X,%08X,%08X) r(%08X,%08X,%08X) n(%08X,%08X,%08X)\n", data.trace_id(),
  397. *reinterpret_cast< uint32 * >( &flendpos[ 0 ] ), *reinterpret_cast< uint32 * >( &flendpos[ 1 ] ), *reinterpret_cast< uint32 * >( &flendpos[ 2 ] ),
  398. *reinterpret_cast< uint32 * >( &flstartpos[ 0 ] ), *reinterpret_cast< uint32 * >( &flstartpos[ 1 ] ), *reinterpret_cast< uint32 * >( &flstartpos[ 2 ] ),
  399. *reinterpret_cast< uint32 * >( &flright[ 0 ] ), *reinterpret_cast< uint32 * >( &flright[ 1 ] ), *reinterpret_cast< uint32 * >( &flright[ 2 ] ),
  400. *reinterpret_cast< uint32 * >( &flnormal[ 0 ] ), *reinterpret_cast< uint32 * >( &flnormal[ 1 ] ), *reinterpret_cast< uint32 * >( &flnormal[ 2 ] )
  401. );
  402. }
  403. #endif
  404. if ( !BValidateClientPlayerDecalSignature( data ) )
  405. return false;
  406. // Make the decal ready.
  407. const int nKey = MakeKey( m_nUniqueID );
  408. MakeDecalReady( nKey );
  409. return true;
  410. }
  411. void C_FEPlayerDecal::ApplyDecalDataToWorld()
  412. {
  413. if ( !m_bDecalReadyToApplyToWorld )
  414. return;
  415. CLocalPlayerFilter filter;
  416. const int nKey = MakeKey( m_nUniqueID );
  417. TE_PlayerDecal( filter, 0.0f, &m_vecEndPos, &m_vecStart, &m_vecRight, nKey, m_nEntity, m_nHitbox, 0 );
  418. }
  419. void QcCreateDecalData( int nKey, int nStickerKitDefinition, int nTintID, bool bDrips, float flCreationTime )
  420. {
  421. const CStickerKit *pStickerKit = GetItemSchema()->GetStickerKitDefinition( nStickerKitDefinition );
  422. if ( pStickerKit && !pStickerKit->sMaterialPath.IsEmpty() )
  423. {
  424. // TODO: We should convert this to be an async texture load once texture streaming is in.
  425. CFmtStr fmtTextureName( "decals/sprays/%s", bDrips ? pStickerKit->sMaterialPath.Get() : pStickerKit->sMaterialPathNoDrips.Get() );
  426. ITexture* texture = materials->FindTexture( fmtTextureName, TEXTURE_GROUP_DECAL, false );
  427. if ( !texture || texture->IsError() )
  428. {
  429. Assert( !"Failed to find a texture for this decal. We're never going to see it." );
  430. Warning( "Failed to find spray '%s', this is never going to render.\n", fmtTextureName.Access() );
  431. return;
  432. }
  433. texture->IncrementReferenceCount();
  434. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  435. ICallQueue* pCQ = pRenderContext->GetCallQueue();
  436. if ( pCQ )
  437. pCQ->QueueCall( CreateDecalData, nKey, texture, pStickerKit->nRarity, nTintID, flCreationTime );
  438. else
  439. CreateDecalData( nKey, texture, pStickerKit->nRarity, nTintID, flCreationTime );
  440. }
  441. }
  442. void QcCreatePreviewDecal( uint32 nStickerKitDefinition, uint32 nTintID, const trace_t& trace, const Vector* pRight )
  443. {
  444. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  445. ICallQueue* pCQ = pRenderContext->GetCallQueue();
  446. if ( pCQ )
  447. pCQ->QueueCall( DeleteDecalData, g_nPlayerLogoProxyForPreviewKey );
  448. else
  449. DeleteDecalData( g_nPlayerLogoProxyForPreviewKey );
  450. bool bHasDrips = BShouldHaveDrips( trace.plane.normal );
  451. // Matches code in CCSPlayer::SprayPaint. Update that if you touch this.
  452. Vector startPos = trace.endpos + trace.plane.normal;
  453. CLocalPlayerFilter filter;
  454. QcCreateDecalData( g_nPlayerLogoProxyForPreviewKey, nStickerKitDefinition, nTintID, bHasDrips, gpGlobals->curtime - PLAYERDECALS_DURATION_APPLY );
  455. TE_PlayerDecal( filter, 0.0f, &trace.endpos, &startPos, pRight, g_nPlayerLogoProxyForPreviewKey, trace.GetEntityIndex(), trace.hitbox, EDF_IMMEDIATECLEANUP );
  456. }
  457. IMaterial * QcCreateDecalDataForModelPreviewPanel( int nStickerKitDefinition, int nTintID )
  458. {
  459. IMaterial *pMatStickerOverride = NULL;
  460. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  461. ICallQueue* pCQ = pRenderContext->GetCallQueue();
  462. if ( pCQ )
  463. pCQ->QueueCall( DeleteDecalData, g_nPlayerLogoProxyForPreviewKey );
  464. else
  465. DeleteDecalData( g_nPlayerLogoProxyForPreviewKey );
  466. if ( nStickerKitDefinition > 0 && GetItemSchema() && GetItemSchema()->GetStickerKitDefinition( nStickerKitDefinition ) )
  467. {
  468. char const *szDesiredPreviewMaterialPath = "decals/playerlogo01_modelpreview.vmt";
  469. pMatStickerOverride = materials->FindMaterial( szDesiredPreviewMaterialPath, TEXTURE_GROUP_OTHER );
  470. if ( pMatStickerOverride->IsErrorMaterial() )
  471. {
  472. KeyValues *pSpecificStickerMaterialKeyValues = new KeyValues( "vmt" );
  473. KeyValues::AutoDelete autodelete_pSpecificStickerMaterialKeyValues( pSpecificStickerMaterialKeyValues );
  474. if ( pSpecificStickerMaterialKeyValues->LoadFromFile( g_pFullFileSystem, szDesiredPreviewMaterialPath, "GAME" ) )
  475. {
  476. pMatStickerOverride = materials->CreateMaterial( szDesiredPreviewMaterialPath, pSpecificStickerMaterialKeyValues );
  477. }
  478. autodelete_pSpecificStickerMaterialKeyValues.Detach();
  479. }
  480. }
  481. if ( !pMatStickerOverride || pMatStickerOverride->IsErrorMaterial() )
  482. return NULL;
  483. QcCreateDecalData( g_nPlayerLogoProxyForPreviewKey, nStickerKitDefinition, nTintID, true, gpGlobals->curtime - PLAYERDECALS_DURATION_APPLY );
  484. return pMatStickerOverride;
  485. }
  486. //-----------------------------------------------------------------------------
  487. // Purpose:
  488. // Input : bool -
  489. //-----------------------------------------------------------------------------
  490. void C_FEPlayerDecal::MakeDecalReady( int nKey )
  491. {
  492. QcCreateDecalData( nKey, m_nPlayer, m_nTintID, BShouldHaveDrips( m_vecNormal ), m_flCreationTime );
  493. }
  494. //-----------------------------------------------------------------------------
  495. // Purpose:
  496. // Input : bool -
  497. //-----------------------------------------------------------------------------
  498. void C_TEPlayerDecal::PostDataUpdate( DataUpdateType_t updateType )
  499. {
  500. VPROF( "C_TEPlayerDecal::PostDataUpdate" );
  501. // Decals disabled?
  502. if ( !r_decals.GetBool() )
  503. return;
  504. CLocalPlayerFilter filter;
  505. TE_PlayerDecal( filter, 0.0f, &m_vecOrigin, &m_vecStart, &m_vecRight, m_nPlayer, m_nEntity, m_nHitbox, 0 );
  506. }
  507. IMPLEMENT_CLIENTCLASS_EVENT_DT(C_TEPlayerDecal, DT_TEPlayerDecal, CTEPlayerDecal)
  508. RecvPropVector( RECVINFO(m_vecOrigin)),
  509. RecvPropVector( RECVINFO(m_vecStart)),
  510. RecvPropVector( RECVINFO(m_vecRight)),
  511. RecvPropInt( RECVINFO(m_nEntity)),
  512. RecvPropInt( RECVINFO(m_nPlayer)),
  513. RecvPropInt( RECVINFO(m_nHitbox)),
  514. END_RECV_TABLE()
  515. IMPLEMENT_CLIENTCLASS_DT(C_FEPlayerDecal, DT_FEPlayerDecal, CFEPlayerDecal)
  516. RecvPropInt( RECVINFO( m_nUniqueID ) ),
  517. RecvPropInt( RECVINFO( m_unAccountID ) ),
  518. RecvPropInt( RECVINFO( m_unTraceID ) ),
  519. RecvPropInt( RECVINFO( m_rtGcTime ) ),
  520. RecvPropVector( RECVINFO(m_vecEndPos)),
  521. RecvPropVector( RECVINFO(m_vecStart)),
  522. RecvPropVector( RECVINFO(m_vecRight)),
  523. RecvPropVector( RECVINFO(m_vecNormal)),
  524. RecvPropInt( RECVINFO(m_nEntity)),
  525. RecvPropInt( RECVINFO(m_nPlayer)),
  526. RecvPropInt( RECVINFO(m_nHitbox)),
  527. RecvPropInt( RECVINFO(m_nTintID)),
  528. RecvPropFloat( RECVINFO( m_flCreationTime ) ),
  529. RecvPropInt( RECVINFO(m_nVersion)),
  530. RecvPropArray3( RECVINFO_ARRAY( m_ubSignature ), RecvPropInt( RECVINFO( m_ubSignature[0] ) ) ),
  531. END_RECV_TABLE()
  532. //-----------------------------------------------------------------------------
  533. // Purpose: material proxy
  534. //-----------------------------------------------------------------------------
  535. class CPlayerLogoProxy : public IMaterialProxy
  536. {
  537. public:
  538. CPlayerLogoProxy();
  539. virtual bool Init( IMaterial* pMaterial, KeyValues *pKeyValues );
  540. virtual void OnBind( void *pC_BaseEntity );
  541. virtual void Release()
  542. {
  543. if ( m_pDefaultTexture )
  544. {
  545. m_pDefaultTexture->DecrementReferenceCount();
  546. }
  547. delete this;
  548. }
  549. virtual IMaterial *GetMaterial();
  550. virtual bool CanBeCalledAsync() const { return true; }
  551. protected:
  552. virtual void OnLogoBindInternal( const DecalData_t& decalData, bool bPreviewMaterial );
  553. bool m_bInspectInModelPreviewWindow;
  554. bool m_bVertexLitMaterial;
  555. private:
  556. IMaterialVar *m_pAlphaVar;
  557. IMaterialVar *m_pColorVar;
  558. IMaterialVar *m_pDetailBlendFactorVar;
  559. IMaterialVar *m_pBaseTextureVar;
  560. ITexture *m_pDefaultTexture;
  561. };
  562. CPlayerLogoProxy::CPlayerLogoProxy()
  563. {
  564. m_bInspectInModelPreviewWindow = false;
  565. m_bVertexLitMaterial = false;
  566. m_pAlphaVar = NULL;
  567. m_pColorVar = NULL;
  568. m_pDetailBlendFactorVar = NULL;
  569. m_pBaseTextureVar = NULL;
  570. m_pDefaultTexture = NULL;
  571. }
  572. bool CPlayerLogoProxy::Init( IMaterial *pMaterial, KeyValues *pKeyValues )
  573. {
  574. bool found = false;
  575. m_pAlphaVar = pMaterial->FindVar( "$alpha", &found );
  576. if ( !found )
  577. return false;
  578. if ( !m_pAlphaVar )
  579. return false;
  580. m_bVertexLitMaterial = pMaterial->IsVertexLit();
  581. // m_pColorVar = pMaterial->FindVar( pMaterial->IsVertexLit() ? "$color2" : "$color", &found );
  582. m_pColorVar = pMaterial->FindVar( "$color", &found );
  583. if ( !found )
  584. return false;
  585. if ( !m_pColorVar )
  586. return false;
  587. m_pDetailBlendFactorVar = pMaterial->FindVar( "$detailblendfactor", &found );
  588. if ( !found )
  589. return false;
  590. if ( !m_pDetailBlendFactorVar )
  591. return false;
  592. m_pBaseTextureVar = pMaterial->FindVar( "$basetexture", &found );
  593. if ( !found )
  594. return false;
  595. if ( !m_pBaseTextureVar )
  596. return false;
  597. m_pDefaultTexture = m_pBaseTextureVar->GetTextureValue();
  598. if ( !m_pDefaultTexture )
  599. return false;
  600. m_pDefaultTexture->IncrementReferenceCount();
  601. return true;
  602. }
  603. void CPlayerLogoProxy::OnBind( void *pC_BaseEntity )
  604. {
  605. if ( !pC_BaseEntity )
  606. return; // dummy bind from mesh init
  607. if ( !m_pBaseTextureVar )
  608. return;
  609. DecalData_t dd;
  610. if ( ReadDecalData( ( int )( intp ) pC_BaseEntity, &dd ) )
  611. {
  612. bool bPreviewMaterial = ( g_nPlayerLogoProxyForPreviewKey == ( int )( intp ) pC_BaseEntity );
  613. OnLogoBindInternal( dd, bPreviewMaterial );
  614. return;
  615. }
  616. m_pBaseTextureVar->SetTextureValue( m_pDefaultTexture );
  617. m_pAlphaVar->SetFloatValue( 0.0f );
  618. m_pColorVar->SetVecValue( 1.0f, 1.0f, 1.0f, 1.0f );
  619. m_pDetailBlendFactorVar->SetFloatValue( 0.0f );
  620. }
  621. DEVELOPMENT_ONLY_CONVAR( cl_playerspray_debug_pulse_timescale, 0.6 );
  622. DEVELOPMENT_ONLY_CONVAR( cl_playerspray_debug_pulse_alpha_low, 0.125 );
  623. DEVELOPMENT_ONLY_CONVAR( cl_playerspray_debug_pulse_alpha_fraction, 1.0 );
  624. #if ECON_SPRAY_TINT_IDS_FLOAT_COLORSPACE
  625. void Helper_TintColorSpaceToRGBf( uint8 unHSVID, float arrFlcs[3], float flRenderRGB[3] )
  626. {
  627. //
  628. // Tint processing
  629. // see: csgo_english.txt and econ_item_schema.cpp Helper_ExtractIntegerFromValueStringEntry for tint color names
  630. //
  631. struct CHSVSprayTintDefinition_t
  632. {
  633. // Based on the hue value the correct lerped bucket will be used
  634. int m_hue[3]; // HUE low-mid-hi
  635. int m_slo[3]; // SAT low low-mid-hi
  636. int m_shi[3]; // SAT high low-mid-hi
  637. int m_vlo[3]; // VAL low low-mid-hi
  638. int m_vhi[3]; // VAL high low-mid-hi
  639. }
  640. const arrSprayTintDefinitions[] =
  641. {
  642. { // zero-index: all white
  643. { 180, 180, 180 }, // hue
  644. { 0, 0, 0 }, // sat low
  645. { 0, 0, 0 }, // sat high
  646. { 100, 100, 100 }, // val low
  647. { 100, 100, 100 }, // val high
  648. },
  649. { // index #1 - Aqua
  650. { 158, 174, 190 }, // hue
  651. { 50, 60, 40 }, // sat low
  652. { 90, 90, 90 }, // sat high
  653. { 60, 60, 60 }, // val low
  654. { 90, 90, 90 }, // val high
  655. },
  656. { // index #2 - Red
  657. { 348, 358, 368 }, // hue
  658. { 78, 80, 70 }, // sat low
  659. { 90, 90, 90 }, // sat high
  660. { 48, 50, 50 }, // val low
  661. { 82, 80, 80 }, // val high
  662. },
  663. { // index #3 - Orange
  664. { 18, 28, 38 }, // hue
  665. { 74, 72, 70 }, // sat low
  666. { 94, 92, 90 }, // sat high
  667. { 58, 78, 74 }, // val low
  668. { 84, 92, 98 }, // val high
  669. },
  670. { // index #4 - Yellow
  671. { 48, 54, 61 }, // hue
  672. { 40, 40, 40 }, // sat low
  673. { 92, 92, 92 }, // sat high
  674. { 82, 82, 82 }, // val low
  675. { 98, 98, 98 }, // val high
  676. },
  677. { // index #5 - Green
  678. { 108, 129, 150 }, // hue
  679. { 40, 50, 54 }, // sat low
  680. { 90, 90, 90 }, // sat high
  681. { 60, 60, 58 }, // val low
  682. { 90, 90, 90 }, // val high
  683. },
  684. { // index #6 - Blue
  685. { 207, 223, 238 }, // hue
  686. { 70, 40, 50 }, // sat low
  687. { 90, 90, 90 }, // sat high
  688. { 60, 50, 70 }, // val low
  689. { 100, 80, 100 }, // val high
  690. },
  691. { // index #7 - Purple
  692. { 258, 272, 289 }, // hue
  693. { 50, 40, 40 }, // sat low
  694. { 90, 80, 80 }, // sat high
  695. { 60, 60, 60 }, // val low
  696. { 100, 90, 80 }, // val high
  697. },
  698. { // index #8 - Pink
  699. { 308, 340, 372 }, // hue
  700. { 20, 20, 20 }, // sat low
  701. { 67, 50, 64 }, // sat high
  702. { 84, 80, 86 }, // val low
  703. { 98, 98, 98 }, // val high
  704. },
  705. { // index #9 - Lime
  706. { 68, 78, 94 }, // hue
  707. { 50, 50, 50 }, // sat low
  708. { 92, 90, 90 }, // sat high
  709. { 62, 70, 80 }, // val low
  710. { 98, 98, 98 }, // val high
  711. },
  712. { // index #10 - White
  713. { 0, 180, 360 }, // hue
  714. { 2, 2, 2 }, // sat low
  715. { 7, 7, 7 }, // sat high
  716. { 90, 90, 90 }, // val low
  717. { 97, 97, 97 }, // val high
  718. },
  719. };
  720. COMPILE_TIME_ASSERT( 1 + NUM_SPRAY_TINT_IDS_SUPPORTED == Q_ARRAYSIZE( arrSprayTintDefinitions ) );
  721. if ( ( unHSVID > 0 ) && ( unHSVID <= NUM_SPRAY_TINT_IDS_SUPPORTED ) )
  722. {
  723. CHSVSprayTintDefinition_t const &tintdef = arrSprayTintDefinitions[unHSVID];
  724. //
  725. // Hue percentage value
  726. //
  727. float flHuePct = arrFlcs[0]*2.0f;
  728. int nHueIdx = 0; // 0 when in first half, 1 when in second half
  729. if ( flHuePct > 1.0f )
  730. {
  731. nHueIdx = 1;
  732. flHuePct -= 1.0f;
  733. }
  734. //
  735. // Actual HSV interpolated value
  736. //
  737. float flHue = Lerp<float>( flHuePct, tintdef.m_hue[nHueIdx], tintdef.m_hue[nHueIdx+1] );
  738. float flSat = Lerp<float>( arrFlcs[1],
  739. Lerp<float>( flHuePct, tintdef.m_slo[nHueIdx], tintdef.m_slo[nHueIdx+1] ),
  740. Lerp<float>( flHuePct, tintdef.m_shi[nHueIdx], tintdef.m_shi[nHueIdx+1] ) ) / 100.0f;
  741. float flVal = Lerp<float>( arrFlcs[2],
  742. Lerp<float>( flHuePct, tintdef.m_vlo[nHueIdx], tintdef.m_vlo[nHueIdx+1] ),
  743. Lerp<float>( flHuePct, tintdef.m_vhi[nHueIdx], tintdef.m_vhi[nHueIdx+1] ) ) / 100.0f;
  744. if ( flHue >= 360.0f )
  745. flHue -= 360.0f; // rotate into [0:360) range if needed overlapping area for lerps
  746. //
  747. // Now we just need to convert HSV->RGB
  748. //
  749. float hh = flHue/60.0f;
  750. int numhh = int( hh );
  751. float ff = hh - numhh;
  752. float xp = flVal * ( 1.0f - flSat );
  753. float xq = flVal * ( 1.0f - ( flSat * ff ) );
  754. float xt = flVal * ( 1.0f - ( flSat * ( 1.0f - ff ) ) );
  755. switch ( numhh )
  756. {
  757. case 0:
  758. flRenderRGB[ 0 ] = flVal; flRenderRGB[ 1 ] = xt; flRenderRGB[ 2 ] = xp;
  759. break;
  760. case 1:
  761. flRenderRGB[ 0 ] = xq; flRenderRGB[ 1 ] = flVal; flRenderRGB[ 2 ] = xp;
  762. break;
  763. case 2:
  764. flRenderRGB[ 0 ] = xp; flRenderRGB[ 1 ] = flVal; flRenderRGB[ 2 ] = xt;
  765. break;
  766. case 3:
  767. flRenderRGB[ 0 ] = xp; flRenderRGB[ 1 ] = xq; flRenderRGB[ 2 ] = flVal;
  768. break;
  769. case 4:
  770. flRenderRGB[ 0 ] = xt; flRenderRGB[ 1 ] = xp; flRenderRGB[ 2 ] = flVal;
  771. break;
  772. case 5:
  773. default:
  774. flRenderRGB[ 0 ] = flVal; flRenderRGB[ 1 ] = xp; flRenderRGB[ 2 ] = xq;
  775. break;
  776. }
  777. }
  778. }
  779. #endif
  780. void CPlayerLogoProxy::OnLogoBindInternal( const DecalData_t& decalData, bool bPreviewMaterial )
  781. {
  782. Assert( decalData.m_pTex );
  783. m_pBaseTextureVar->SetTextureValue( decalData.m_pTex );
  784. // Drive alpha to expiration (but not outside of creation time)
  785. float flRenderAlpha = 0;
  786. float flDetailBlendFactor = 0;
  787. if ( gpGlobals->curtime >= decalData.m_flCreationTime )
  788. {
  789. float flDecalTime = gpGlobals->curtime;
  790. #if 0 // quick decals fading code
  791. flDecalTime = decalData.m_flCreationTime + PLAYERDECALS_DURATION_SOLID - 5;
  792. flDecalTime = flDecalTime + ( gpGlobals->curtime - decalData.m_flCreationTime ) * 4;
  793. #endif
  794. if ( !m_bInspectInModelPreviewWindow && bPreviewMaterial )
  795. {
  796. flRenderAlpha = flDecalTime * cl_playerspray_debug_pulse_timescale.GetFloat(); // make the full cycle longer than 1 second
  797. flRenderAlpha -= floorf( flRenderAlpha ); // [0..1)[0..1)
  798. flRenderAlpha = fabsf( flRenderAlpha - 0.5f ); // 0.5..0..0.5..0..0.5
  799. flRenderAlpha = cl_playerspray_debug_pulse_alpha_low.GetFloat() + flRenderAlpha/cl_playerspray_debug_pulse_alpha_fraction.GetFloat(); // 12.5% ... 63% opacity cycle over 0.85 sec
  800. }
  801. else if ( gpGlobals->curtime >= decalData.m_flCreationTime + PLAYERDECALS_DURATION_APPLY )
  802. {
  803. float flFadeStart = decalData.m_flCreationTime + PLAYERDECALS_DURATION_SOLID;
  804. float flFadeEnd = flFadeStart + PLAYERDECALS_DURATION_FADE2;
  805. float flFadeAlphaStart = flFadeEnd - PLAYERDECALS_DURATION_FADE1;
  806. flDetailBlendFactor = RemapValClamped( flDecalTime, flFadeStart, flFadeEnd, 0.0f, 1.0f );
  807. flRenderAlpha = RemapValClamped( flDecalTime, flFadeAlphaStart, flFadeEnd, 1.0f, 0.0f );
  808. }
  809. else
  810. {
  811. float flFadeStart = decalData.m_flCreationTime;
  812. float flFadeEnd = flFadeStart + PLAYERDECALS_DURATION_APPLY;
  813. float flFadeAlphaStart = flFadeStart + PLAYERDECALS_DURATION_APPLY/2;
  814. flDetailBlendFactor = RemapValClamped( flDecalTime, flFadeStart, flFadeEnd, 1.0f, 0.0f );
  815. flRenderAlpha = RemapValClamped( flDecalTime, flFadeStart, flFadeAlphaStart, 0.0f, 1.0f );
  816. }
  817. }
  818. //
  819. // Convert tint HSV to RGB
  820. //
  821. float flRenderRGB[ 3 ] = { 1.0f, 1.0f, 1.0f };
  822. uint8 unRenderHSVID = CombinedTintIDGetHSVID( decalData.m_nTintID );
  823. if ( unRenderHSVID )
  824. {
  825. #if ECON_SPRAY_TINT_IDS_FLOAT_COLORSPACE
  826. float arrFlcs[ 3 ] = { CombinedTintIDGetHSVc( decalData.m_nTintID, 0 ), CombinedTintIDGetHSVc( decalData.m_nTintID, 1 ), CombinedTintIDGetHSVc( decalData.m_nTintID, 2 ) };
  827. Helper_TintColorSpaceToRGBf( unRenderHSVID, arrFlcs, flRenderRGB );
  828. #else
  829. if ( const CEconGraffitiTintDefinition *pDef = GEconItemSchema().GetGraffitiTintDefinitionByID( unRenderHSVID ) )
  830. {
  831. uint32 uiRGB = pDef->GetHexColorRGB();
  832. flRenderRGB[ 0 ] = float( ( uiRGB >> 16 ) & 0xFF ) / float( 0xFF );
  833. flRenderRGB[ 1 ] = float( ( uiRGB >> 8 ) & 0xFF ) / float( 0xFF );
  834. flRenderRGB[ 2 ] = float( ( uiRGB ) & 0xFF ) / float( 0xFF );
  835. }
  836. #endif
  837. }
  838. // Do srgb color transform when applied to brush models via lightmapped generic
  839. if ( !m_bVertexLitMaterial && !m_bInspectInModelPreviewWindow )
  840. {
  841. for ( int j = 0; j < 3; ++j )
  842. {
  843. flRenderRGB[ j ] = SrgbGammaToLinear( flRenderRGB[ j ] );
  844. }
  845. }
  846. m_pColorVar->SetVecValue( flRenderRGB[ 0 ], flRenderRGB[ 1 ], flRenderRGB[ 2 ], 1.0f );
  847. m_pAlphaVar->SetFloatValue( flRenderAlpha );
  848. m_pDetailBlendFactorVar->SetFloatValue( flDetailBlendFactor );
  849. }
  850. IMaterial *CPlayerLogoProxy::GetMaterial()
  851. {
  852. return m_pBaseTextureVar->GetOwningMaterial();
  853. }
  854. EXPOSE_MATERIAL_PROXY( CPlayerLogoProxy, PlayerLogo );
  855. class CPlayerLogoProxyForModelWeaponPreviewPanel : public CPlayerLogoProxy
  856. {
  857. public:
  858. CPlayerLogoProxyForModelWeaponPreviewPanel()
  859. {
  860. m_bInspectInModelPreviewWindow = true;
  861. }
  862. virtual void OnBind( void *pC_BaseEntity ) OVERRIDE
  863. {
  864. CPlayerLogoProxy::OnBind( ( void * ) ( intp ) g_nPlayerLogoProxyForPreviewKey );
  865. }
  866. };
  867. EXPOSE_MATERIAL_PROXY( CPlayerLogoProxyForModelWeaponPreviewPanel, PlayerLogoModelPreview );