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.

1269 lines
37 KiB

  1. //========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Client side view model implementation. Responsible for drawing
  4. // the view model.
  5. //
  6. // $NoKeywords: $
  7. //===========================================================================//
  8. #include "cbase.h"
  9. #include "c_baseviewmodel.h"
  10. #include "model_types.h"
  11. #include "hud.h"
  12. #include "view_shared.h"
  13. #include "iviewrender.h"
  14. #include "view.h"
  15. #include "mathlib/vmatrix.h"
  16. #include "cl_animevent.h"
  17. #include "eventlist.h"
  18. #include "tools/bonelist.h"
  19. #include <keyvalues.h>
  20. #include "hltvcamera.h"
  21. #include "r_efx.h"
  22. #include "dlight.h"
  23. #include "clientalphaproperty.h"
  24. #include "iinput.h"
  25. #include "cs_shareddefs.h"
  26. #include "c_cs_player.h"
  27. #include "weapon_csbase.h"
  28. #include "weapon_basecsgrenade.h"
  29. #include "iclientmode.h"
  30. #include "platforminputdevice.h"
  31. #include "inputsystem/iinputsystem.h"
  32. #include "materialsystem/imaterialvar.h"
  33. #if defined( REPLAY_ENABLED )
  34. #include "replaycamera.h"
  35. #endif
  36. // memdbgon must be the last include file in a .cpp file!!!
  37. #include "tier0/memdbgon.h"
  38. #ifdef CSTRIKE_DLL
  39. ConVar cl_righthand( "cl_righthand", "1", FCVAR_ARCHIVE | FCVAR_SS, "Use right-handed view models." );
  40. ConVar vm_draw_addon( "vm_draw_addon", "1" );
  41. SplitScreenConVarRef ss_righthand( "cl_righthand", true );
  42. #endif
  43. ConVar vm_debug( "vm_debug", "0", FCVAR_CHEAT );
  44. ConVar vm_draw_always( "vm_draw_always", "0", FCVAR_CHEAT, "1 - Always draw view models, 2 - Never draw view models. Should be done before map launches." );
  45. ConVar vm_pointer_pitch_up_scale( "vm_pointer_pitch_up_scale", "0.25", FCVAR_DEVELOPMENTONLY, "Limit how much the view model follows the pointer in looking up." );
  46. #ifdef _DEBUG
  47. ConVar stickers_enabled_firstperson( "stickers_enabled_firstperson", "1", FCVAR_DEVELOPMENTONLY, "Enable work-in-progress stickers on viewmodels." );
  48. ConVar stickers_debug_randomize( "stickers_debug_randomize", "0", FCVAR_DEVELOPMENTONLY, "All weapons fill all slots with random stickers." );
  49. #endif
  50. void PostToolMessage( HTOOLHANDLE hEntity, KeyValues *msg );
  51. extern float g_flMuzzleFlashScale;
  52. extern ConVar r_drawviewmodel;
  53. extern ConVar cl_righthand;
  54. ConVar mat_preview( "mat_preview", "", FCVAR_CLIENTDLL | FCVAR_CHEAT );
  55. // [mlowrance] used for flame effect when pin pulled
  56. #define MOLOTOV_PARTICLE_EFFECT_NAME "weapon_molotov_fp"
  57. void FormatViewModelAttachment( C_BasePlayer *pPlayer, Vector &vOrigin, bool bInverse )
  58. {
  59. int nSlot = 0;
  60. if ( pPlayer )
  61. {
  62. int nPlayerSlot = C_BasePlayer::GetSplitScreenSlotForPlayer( pPlayer );
  63. if ( nPlayerSlot == -1 )
  64. {
  65. nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  66. }
  67. else
  68. {
  69. nSlot = nPlayerSlot;
  70. }
  71. }
  72. Assert( nSlot != -1 );
  73. // Presumably, SetUpView has been called so we know our FOV and render origin.
  74. const CViewSetup *pViewSetup = view->GetPlayerViewSetup( nSlot );
  75. float worldx = tan( pViewSetup->fov * M_PI/360.0 );
  76. float viewx = tan( pViewSetup->fovViewmodel * M_PI/360.0 );
  77. // aspect ratio cancels out, so only need one factor
  78. // the difference between the screen coordinates of the 2 systems is the ratio
  79. // of the coefficients of the projection matrices (tan (fov/2) is that coefficient)
  80. float factorX = worldx / viewx;
  81. float factorY = factorX;
  82. // Get the coordinates in the viewer's space.
  83. Vector tmp = vOrigin - pViewSetup->origin;
  84. Vector vTransformed( MainViewRight(nSlot).Dot( tmp ), MainViewUp(nSlot).Dot( tmp ), MainViewForward(nSlot).Dot( tmp ) );
  85. // Now squash X and Y.
  86. if ( bInverse )
  87. {
  88. if ( factorX != 0 && factorY != 0 )
  89. {
  90. vTransformed.x /= factorX;
  91. vTransformed.y /= factorY;
  92. }
  93. else
  94. {
  95. vTransformed.x = 0.0f;
  96. vTransformed.y = 0.0f;
  97. }
  98. }
  99. else
  100. {
  101. vTransformed.x *= factorX;
  102. vTransformed.y *= factorY;
  103. }
  104. // Transform back to world space.
  105. Vector vOut = (MainViewRight(nSlot) * vTransformed.x) + (MainViewUp(nSlot) * vTransformed.y) + (MainViewForward(nSlot) * vTransformed.z);
  106. vOrigin = pViewSetup->origin + vOut;
  107. }
  108. void Precache( void )
  109. {
  110. PrecacheParticleSystem( MOLOTOV_PARTICLE_EFFECT_NAME );
  111. // BaseClass::Precache();
  112. }
  113. void C_BaseViewModel::UpdateStatTrakGlow( void )
  114. {
  115. //approach the ideal in 2 seconds
  116. m_flStatTrakGlowMultiplier = Approach( m_flStatTrakGlowMultiplierIdeal, m_flStatTrakGlowMultiplier, (gpGlobals->frametime * 0.5) );
  117. }
  118. void C_BaseViewModel::OnNewParticleEffect( const char *pszParticleName, CNewParticleEffect *pNewParticleEffect )
  119. {
  120. // [msmith] We want the split screen visibility of the particles to be the same as the visibility of the view model.
  121. uint32 visBits = m_VisibilityBits.Get(0, 0xff);
  122. int nSlot = -1;
  123. if ( visBits < 3 )
  124. {
  125. // If visBits is 3 then it is both slots 1 and 2 meaning all users can see this effect.
  126. // If visBits is less than 3, then it is either slot 1 or 2 (but zero based means slot 0 or 1).
  127. nSlot = visBits-1;
  128. }
  129. pNewParticleEffect->SetDrawOnlyForSplitScreenUser( nSlot );
  130. if ( FStrEq( pszParticleName, MOLOTOV_PARTICLE_EFFECT_NAME ) )
  131. {
  132. m_viewmodelParticleEffect = pNewParticleEffect;
  133. }
  134. }
  135. void C_BaseViewModel::OnParticleEffectDeleted( CNewParticleEffect *pParticleEffect )
  136. {
  137. BaseClass::OnParticleEffectDeleted( pParticleEffect );
  138. if ( m_viewmodelParticleEffect == pParticleEffect )
  139. {
  140. m_viewmodelParticleEffect = NULL;
  141. }
  142. }
  143. void C_BaseViewModel::UpdateParticles( int nSlot )
  144. {
  145. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  146. if ( !pPlayer )
  147. return;
  148. if ( pPlayer->IsPlayerDead() )
  149. return;
  150. // Otherwise pass the event to our associated weapon
  151. C_BaseCombatWeapon *pWeapon = GetOwningWeapon();
  152. if ( !pWeapon )
  153. return;
  154. CWeaponCSBase *pCSWeapon = ( CWeaponCSBase* )pPlayer->GetActiveWeapon();
  155. if ( !pCSWeapon )
  156. return;
  157. int iWeaponId = pCSWeapon->GetCSWeaponID();
  158. bool shouldDrawPlayer = ( pPlayer->GetPlayerRenderMode( nSlot ) == PLAYER_RENDER_THIRDPERSON );
  159. bool visible = r_drawviewmodel.GetBool() && pPlayer && !shouldDrawPlayer;
  160. if ( visible && iWeaponId == WEAPON_MOLOTOV )
  161. {
  162. CBaseCSGrenade *pGren = dynamic_cast<CBaseCSGrenade*>( pPlayer->GetActiveWeapon() );
  163. if ( pGren->IsPinPulled() )
  164. {
  165. //if ( !pGren->IsLoopingSoundPlaying() )
  166. //{
  167. // pGren->SetLoopingSoundPlaying( true );
  168. // EmitSound( "Molotov.IdleLoop" );
  169. // //DevMsg( 1, "++++++++++>Playing Molotov.IdleLoop 2\n" );
  170. //}
  171. // TEST: [mlowrance] This is to test for attachment.
  172. int iAttachment = -1;
  173. if ( pWeapon && pWeapon->GetBaseAnimating() )
  174. iAttachment = pWeapon->GetBaseAnimating()->LookupAttachment( "Wick" );
  175. if ( iAttachment >= 0 )
  176. {
  177. if ( !m_viewmodelParticleEffect.IsValid() )
  178. {
  179. DispatchParticleEffect( MOLOTOV_PARTICLE_EFFECT_NAME, PATTACH_POINT_FOLLOW, this, "Wick" );
  180. }
  181. }
  182. }
  183. }
  184. else
  185. {
  186. if ( m_viewmodelParticleEffect.IsValid() )
  187. {
  188. StopSound( "Molotov.IdleLoop" );
  189. //DevMsg( 1, "---------->Stopping Molotov.IdleLoop 3\n" );
  190. m_viewmodelParticleEffect->StopEmission( false, true );
  191. m_viewmodelParticleEffect->SetRemoveFlag();
  192. m_viewmodelParticleEffect = NULL;
  193. }
  194. }
  195. }
  196. bool C_BaseViewModel::Simulate( void )
  197. {
  198. int nSlot = GET_ACTIVE_SPLITSCREEN_SLOT();
  199. ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( GetOwner() );
  200. UpdateParticles( nSlot );
  201. UpdateStatTrakGlow();
  202. BaseClass::Simulate();
  203. return true;
  204. }
  205. void C_BaseViewModel::FormatViewModelAttachment( int nAttachment, matrix3x4_t &attachmentToWorld )
  206. {
  207. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  208. Vector vecOrigin;
  209. MatrixPosition( attachmentToWorld, vecOrigin );
  210. ::FormatViewModelAttachment( pPlayer, vecOrigin, false );
  211. PositionMatrix( vecOrigin, attachmentToWorld );
  212. }
  213. void C_BaseViewModel::UncorrectViewModelAttachment( Vector &vOrigin )
  214. {
  215. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  216. // Unformat the attachment.
  217. ::FormatViewModelAttachment( pPlayer, vOrigin, true );
  218. }
  219. //-----------------------------------------------------------------------------
  220. // Purpose
  221. //-----------------------------------------------------------------------------
  222. void C_BaseViewModel::FireEvent( const Vector& origin, const QAngle& angles, int eventNum, const char *options )
  223. {
  224. // We override sound requests so that we can play them locally on the owning player
  225. if ( ( eventNum == AE_CL_PLAYSOUND ) || ( eventNum == CL_EVENT_SOUND ) )
  226. {
  227. // Only do this if we're owned by someone
  228. if ( GetOwner() != NULL && GetOwner()->IsAlive() )
  229. {
  230. // playing the same sound near-instantly is assumed to be an error and duplicates are skipped. This does NOT apply to weapon firing sounds.
  231. if ( !IsSoundSameAsPreviousSound( options, 0.1f ) )
  232. {
  233. CLocalPlayerFilter filter;
  234. EmitSound( filter, GetOwner()->GetSoundSourceIndex(), options, &GetAbsOrigin() );
  235. SetPreviousSoundStr( options );
  236. }
  237. ResetTimeSincePreviousSound();
  238. return;
  239. }
  240. }
  241. C_BasePlayer *pOwner = ToBasePlayer( GetOwner() );
  242. if ( !pOwner )
  243. return;
  244. ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( pOwner );
  245. // Otherwise pass the event to our associated weapon
  246. C_BaseCombatWeapon *pWeapon = pOwner->GetActiveWeapon();
  247. if ( pWeapon )
  248. {
  249. bool bResult = pWeapon->OnFireEvent( this, origin, angles, eventNum, options );
  250. if ( !bResult )
  251. {
  252. if ( eventNum == AE_CLIENT_EFFECT_ATTACH && ::input->CAM_IsThirdPerson() )
  253. return;
  254. BaseClass::FireEvent( origin, angles, eventNum, options );
  255. }
  256. }
  257. }
  258. bool C_BaseViewModel::Interpolate( float currentTime )
  259. {
  260. CStudioHdr *pStudioHdr = GetModelPtr();
  261. // Make sure we reset our animation information if we've switch sequences
  262. UpdateAnimationParity();
  263. bool bret = BaseClass::Interpolate( currentTime );
  264. // Hack to extrapolate cycle counter for view model
  265. float elapsed_time = currentTime - m_flAnimTime;
  266. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  267. // Predicted viewmodels have fixed up interval
  268. if ( GetPredictable() || IsClientCreated() )
  269. {
  270. Assert( pPlayer );
  271. float curtime = pPlayer ? pPlayer->GetFinalPredictedTime() : gpGlobals->curtime;
  272. elapsed_time = curtime - m_flAnimTime;
  273. // Adjust for interpolated partial frame
  274. if ( !engine->IsPaused() )
  275. {
  276. elapsed_time += ( gpGlobals->interpolation_amount * TICK_INTERVAL );
  277. }
  278. }
  279. // Prediction errors?
  280. if ( elapsed_time < 0 )
  281. {
  282. elapsed_time = 0;
  283. }
  284. float dt = elapsed_time * (GetPlaybackRate() * GetSequenceCycleRate( pStudioHdr, GetSequence() )) + m_fCycleOffset;
  285. if ( dt < 0.0f )
  286. {
  287. dt = 0.0f;
  288. }
  289. if ( dt >= 1.0f )
  290. {
  291. if ( !IsSequenceLooping( GetSequence() ) )
  292. {
  293. dt = 0.999f;
  294. }
  295. else
  296. {
  297. dt = fmod( dt, 1.0f );
  298. }
  299. }
  300. SetCycle( dt );
  301. return bret;
  302. }
  303. bool C_BaseViewModel::ShouldFlipModel()
  304. {
  305. #ifdef CSTRIKE_DLL
  306. // If cl_righthand is set, then we want them all right-handed.
  307. CBaseCombatWeapon *pWeapon = m_hWeapon.Get();
  308. if ( pWeapon )
  309. {
  310. const FileWeaponInfo_t *pInfo = &pWeapon->GetWpnData();
  311. if ( pInfo->m_bAllowFlipping )
  312. {
  313. if ( !ss_righthand.IsValid() )
  314. {
  315. ss_righthand.Init( "cl_righthand", true );
  316. }
  317. return pInfo->m_bBuiltRightHanded != ss_righthand.GetBool( GET_ACTIVE_SPLITSCREEN_SLOT() );
  318. }
  319. }
  320. #endif
  321. return false;
  322. }
  323. void C_BaseViewModel::ApplyBoneMatrixTransform( matrix3x4_t& transform )
  324. {
  325. ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( GetOwner() );
  326. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  327. bool bUsingMotionController = pPlayer && PlatformInputDevice::IsInputDeviceAPointer( g_pInputSystem->GetCurrentInputDevice() );
  328. if ( ShouldFlipModel() || bUsingMotionController )
  329. {
  330. matrix3x4_t viewMatrix, viewMatrixInverse;
  331. // We could get MATERIAL_VIEW here, but this is called sometimes before the renderer
  332. // has set that matrix. Luckily, this is called AFTER the CViewSetup has been initialized.
  333. const CViewSetup *pSetup = view->GetPlayerViewSetup();
  334. AngleMatrix( pSetup->angles, pSetup->origin, viewMatrixInverse );
  335. MatrixInvert( viewMatrixInverse, viewMatrix );
  336. // Transform into view space.
  337. matrix3x4_t temp;
  338. ConcatTransforms( viewMatrix, transform, temp );
  339. if ( ShouldFlipModel() )
  340. {
  341. // Flip it along X.
  342. // (This is the slower way to do it, and it equates to negating the top row).
  343. //matrix3x4_t mScale;
  344. //SetIdentityMatrix( mScale );
  345. //mScale[0][0] = 1;
  346. //mScale[1][1] = -1;
  347. //mScale[2][2] = 1;
  348. //ConcatTransforms( mScale, temp, temp );
  349. temp[1][0] = -temp[1][0];
  350. temp[1][1] = -temp[1][1];
  351. temp[1][2] = -temp[1][2];
  352. temp[1][3] = -temp[1][3];
  353. }
  354. if ( bUsingMotionController )
  355. {
  356. matrix3x4_t localAngleMatrix;
  357. QAngle localAngles = pPlayer->GetEyeAngleOffset();
  358. // We want to use negative angles if they're closer to zero. If any of the angles are greater than 180, subtract off 360 to give a negative version of it.
  359. if ( localAngles[YAW] > 180.0f )
  360. {
  361. localAngles[YAW] -= 360.0f;
  362. }
  363. if ( localAngles[PITCH] > 180.0f )
  364. {
  365. localAngles[PITCH] -= 360.0f;
  366. }
  367. // If we're looking up ( negative pitch ), we want to scale back the pitch so that we don't cover too much of the screen or reveal parts of the arms we don't want the player to see.
  368. if ( localAngles[PITCH] < 0.0f )
  369. {
  370. localAngles[PITCH] *= vm_pointer_pitch_up_scale.GetFloat();
  371. }
  372. // Since the world and view model FOVs don't match up, scale down the offset angles so they point in the same direction as the cursor in the world.
  373. float viewmodel_fov_ratio = GetClientMode()->GetViewModelFOV() / pPlayer->GetFOV();
  374. localAngles *= viewmodel_fov_ratio;
  375. AngleMatrix(localAngles, localAngleMatrix);
  376. // We want to tweak the transform so that we include the angles for the pointer.
  377. // Do this by pre multiplying the angle tweaks.
  378. ConcatTransforms( localAngleMatrix, temp, temp );
  379. }
  380. // Transform back out of view space.
  381. ConcatTransforms( viewMatrixInverse, temp, transform );
  382. }
  383. }
  384. //-----------------------------------------------------------------------------
  385. // Purpose: check if weapon viewmodel should be drawn
  386. //-----------------------------------------------------------------------------
  387. bool C_BaseViewModel::ShouldDraw()
  388. {
  389. ACTIVE_SPLITSCREEN_PLAYER_GUARD( GET_ACTIVE_SPLITSCREEN_SLOT() );
  390. if ( g_bEngineIsHLTV )
  391. {
  392. return ( HLTVCamera()->GetMode() == OBS_MODE_IN_EYE &&
  393. HLTVCamera()->GetPrimaryTarget() == GetOwner() );
  394. }
  395. #if defined( REPLAY_ENABLED )
  396. else if ( engine->IsReplay() )
  397. {
  398. return ( ReplayCamera()->GetMode() == OBS_MODE_IN_EYE &&
  399. ReplayCamera()->GetPrimaryTarget() == GetOwner() );
  400. }
  401. #endif
  402. else
  403. {
  404. Assert( GetRenderMode() != kRenderNone );
  405. if ( vm_draw_always.GetBool() )
  406. return true;
  407. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
  408. C_BasePlayer *pObsTarget = NULL;
  409. if ( pLocalPlayer )
  410. {
  411. pObsTarget = ToBasePlayer( pLocalPlayer->GetObserverTarget() );
  412. }
  413. if ( pLocalPlayer )
  414. if ( ( pLocalPlayer->GetObserverMode() != OBS_MODE_IN_EYE ) && ( GetOwner() != pLocalPlayer ) )
  415. return false;
  416. return BaseClass::ShouldDraw();
  417. }
  418. }
  419. bool C_BaseViewModel::ShouldSuppressForSplitScreenPlayer( int nSlot )
  420. {
  421. if ( vm_draw_always.GetBool() )
  422. {
  423. if ( vm_draw_always.GetInt() == 1 )
  424. {
  425. return false;
  426. }
  427. return true;
  428. }
  429. if ( BaseClass::ShouldSuppressForSplitScreenPlayer( nSlot ) )
  430. {
  431. return true;
  432. }
  433. C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer( nSlot );
  434. C_BasePlayer *pOwner = ToBasePlayer( GetOwner() );
  435. // We supress viewing of the view model if we are not looking through the eyes of the player who owns that view model.
  436. // We still need to call animation updates on this view model.
  437. if ( pOwner == pLocalPlayer )
  438. {
  439. return false;
  440. }
  441. C_BasePlayer *pObserverTarget = ToBasePlayer( pLocalPlayer->GetObserverTarget() );
  442. if ( pOwner == pObserverTarget )
  443. {
  444. // [msmith] We only ever want to draw viewmodels if the player who owns this split screen is in the OBS_MODE_IN_EYE observer mode for view models.
  445. return ( OBS_MODE_IN_EYE != pLocalPlayer->GetObserverMode() );
  446. }
  447. return true;
  448. }
  449. //-----------------------------------------------------------------------------
  450. // Purpose: Render the weapon. Draw the Viewmodel if the weapon's being carried
  451. // by this player, otherwise draw the worldmodel.
  452. //-----------------------------------------------------------------------------
  453. int C_BaseViewModel::DrawModel( int flags, const RenderableInstance_t &instance )
  454. {
  455. if ( !m_bReadyToDraw )
  456. return 0;
  457. if ( !UpdateBlending( flags, instance ) )
  458. return 0;
  459. CMatRenderContextPtr pRenderContext( materials );
  460. if ( (flags & STUDIO_RENDER) && mat_preview.GetString()[0] )
  461. {
  462. int nMatRet = 0;
  463. if ( m_pMaterialPreviewShape == NULL )
  464. {
  465. MDLCACHE_CRITICAL_SECTION();
  466. const char *pszMatLibraryModel = "models/matlibrary/matlibrary_default.mdl";
  467. CBaseAnimating *pPreviewShape = new class CBaseAnimating;
  468. if ( pPreviewShape && pPreviewShape->InitializeAsClientEntity( pszMatLibraryModel, false ) )
  469. {
  470. m_pMaterialPreviewShape = pPreviewShape;
  471. pPreviewShape->SetParent( this );
  472. pPreviewShape->SetLocalOrigin( Vector(25,0,-4) );
  473. pPreviewShape->UpdatePartitionListEntry();
  474. pPreviewShape->CollisionProp()->MarkPartitionHandleDirty();
  475. pPreviewShape->UpdateVisibility();
  476. pPreviewShape->SetUseParentLightingOrigin( true );
  477. pPreviewShape->SetEFlags( EF_NODRAW );
  478. pPreviewShape->SetEFlags( EF_NOCSM );
  479. }
  480. else
  481. {
  482. DevWarning( "Couldn't load material library preview model: %s\n", pszMatLibraryModel );
  483. }
  484. }
  485. else
  486. {
  487. IMaterial *pMatPreview = materials->FindMaterial( mat_preview.GetString(), TEXTURE_GROUP_OTHER, true );
  488. //if ( !pMatPreview )
  489. //{
  490. // for ( MaterialHandle_t i = materials->FirstMaterial(); i != materials->InvalidMaterial(); i = materials->NextMaterial(i) )
  491. // {
  492. // char const *szMatName = materials->GetMaterial(i)->GetName();
  493. // if ( V_stristr( szMatName, mat_preview.GetString() ) )
  494. // {
  495. // pMatPreview = materials->GetMaterial(i);
  496. // break;
  497. // }
  498. // }
  499. //}
  500. if ( pMatPreview )
  501. {
  502. g_pStudioRender->ForcedMaterialOverride( pMatPreview );
  503. m_pMaterialPreviewShape->SetAbsAngles( QAngle(0,gpGlobals->curtime*8.0f,0) );
  504. nMatRet = m_pMaterialPreviewShape->DrawModel( flags | STUDIO_DONOTMODIFYSTENCILSTATE, instance );
  505. g_pStudioRender->ForcedMaterialOverride( NULL );
  506. }
  507. }
  508. return nMatRet;
  509. }
  510. if ( ShouldFlipModel() )
  511. pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
  512. int ret = 0;
  513. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  514. C_BaseCombatWeapon *pWeapon = GetOwningWeapon();
  515. if ( pWeapon )
  516. {
  517. // clear first because number of materials may differ (or be 0)
  518. ClearCustomMaterials();
  519. // set our custom materials to the ones on the world model (they are actually view model res because of the owner being local)
  520. for ( int i = 0; i < pWeapon->GetCustomMaterialCount(); i++ )
  521. {
  522. SetCustomMaterial( pWeapon->GetCustomMaterial( i ), i );
  523. }
  524. }
  525. #ifdef IRONSIGHT
  526. // try to render the scope lens mask stencil shape
  527. if ( flags && (pWeapon && GetScopeStencilMaskMode() == true) )
  528. {
  529. // first create and bonemerge a new scope lens mask stencil shape if we don't have one
  530. if ( !m_viewmodelScopeStencilMask )
  531. {
  532. if ( pWeapon )
  533. {
  534. CEconItemView *pItem = pWeapon->GetEconItemView();
  535. if ( pItem && pItem->GetScopeLensMaskModel() )
  536. {
  537. C_ViewmodelAttachmentModel *pScopeStencilMask = new class C_ViewmodelAttachmentModel;
  538. if ( pScopeStencilMask && pScopeStencilMask->InitializeAsClientEntity( pItem->GetScopeLensMaskModel(), true ) )
  539. {
  540. m_viewmodelScopeStencilMask = pScopeStencilMask;
  541. pScopeStencilMask->SetParent( this );
  542. pScopeStencilMask->SetLocalOrigin( vec3_origin );
  543. pScopeStencilMask->UpdatePartitionListEntry();
  544. pScopeStencilMask->UpdateVisibility();
  545. pScopeStencilMask->SetViewmodel( this );
  546. }
  547. }
  548. }
  549. }
  550. // now render the scope lens mask stencil shape if we have one
  551. if ( m_viewmodelScopeStencilMask && m_viewmodelScopeStencilMask.Get() )
  552. {
  553. render->SetBlend( 0.0f );
  554. pRenderContext->OverrideColorWriteEnable( true, false );
  555. pRenderContext->OverrideDepthEnable( true, false, true );
  556. IMaterial *pMatScopeDummyMaterial = materials->FindMaterial( "dev/scope_mask", TEXTURE_GROUP_OTHER, true );
  557. g_pStudioRender->ForcedMaterialOverride( pMatScopeDummyMaterial );
  558. m_viewmodelScopeStencilMask->DrawModel( flags | STUDIO_DONOTMODIFYSTENCILSTATE, instance );
  559. g_pStudioRender->ForcedMaterialOverride( NULL );
  560. }
  561. }
  562. else
  563. #endif
  564. {
  565. //otherwise, render the viewmodel normally
  566. // If the local player's overriding the viewmodel rendering, let him do it
  567. if ( pPlayer && pPlayer->IsOverridingViewmodel() )
  568. {
  569. ret = pPlayer->DrawOverriddenViewmodel( this, flags, instance );
  570. }
  571. else if ( pWeapon && pWeapon->IsOverridingViewmodel() )
  572. {
  573. ret = pWeapon->DrawOverriddenViewmodel( this, flags, instance );
  574. }
  575. else
  576. {
  577. ret = BaseClass::DrawModel( flags, instance );
  578. }
  579. }
  580. pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
  581. // Now that we've rendered, reset the animation restart flag
  582. if ( flags & STUDIO_RENDER )
  583. {
  584. if ( m_nOldAnimationParity != m_nAnimationParity )
  585. {
  586. m_nOldAnimationParity = m_nAnimationParity;
  587. }
  588. // Tell the weapon itself that we've rendered, in case it wants to do something
  589. if ( pWeapon )
  590. {
  591. pWeapon->ViewModelDrawn( flags, this );
  592. }
  593. if ( vm_debug.GetBool() )
  594. {
  595. MDLCACHE_CRITICAL_SECTION();
  596. int line = 16;
  597. CStudioHdr *hdr = GetModelPtr();
  598. engine->Con_NPrintf( line++, "%s: %s(%d), cycle: %.2f cyclerate: %.2f playbackrate: %.2f\n",
  599. (hdr)?hdr->pszName():"(null)",
  600. GetSequenceName( GetSequence() ),
  601. GetSequence(),
  602. GetCycle(),
  603. GetSequenceCycleRate( hdr, GetSequence() ),
  604. GetPlaybackRate()
  605. );
  606. if ( hdr )
  607. {
  608. for( int i=0; i < hdr->GetNumPoseParameters(); ++i )
  609. {
  610. const mstudioposeparamdesc_t &Pose = hdr->pPoseParameter( i );
  611. engine->Con_NPrintf( line++, "pose_param %s: %f",
  612. Pose.pszName(), GetPoseParameter( i ) );
  613. }
  614. }
  615. // Determine blending amount and tell engine
  616. float blend = (float)( instance.m_nAlpha / 255.0f );
  617. float color[3];
  618. GetColorModulation( color );
  619. engine->Con_NPrintf( line++, "blend=%f, color=%f,%f,%f", blend, color[0], color[1], color[2] );
  620. engine->Con_NPrintf( line++, "GetRenderMode()=%d", GetRenderMode() );
  621. engine->Con_NPrintf( line++, "m_nRenderFX=0x%8.8X", GetRenderFX() );
  622. color24 c = GetRenderColor();
  623. unsigned char a = GetRenderAlpha();
  624. engine->Con_NPrintf( line++, "rendercolor=%d,%d,%d,%d", c.r, c.g, c.b, a );
  625. engine->Con_NPrintf( line++, "origin=%f, %f, %f", GetRenderOrigin().x, GetRenderOrigin().y, GetRenderOrigin().z );
  626. engine->Con_NPrintf( line++, "angles=%f, %f, %f", GetRenderAngles()[0], GetRenderAngles()[1], GetRenderAngles()[2] );
  627. if ( IsEffectActive( EF_NODRAW ) )
  628. {
  629. engine->Con_NPrintf( line++, "EF_NODRAW" );
  630. }
  631. }
  632. }
  633. if ( flags && vm_draw_addon.GetBool()
  634. #ifdef IRONSIGHT
  635. && (GetScopeStencilMaskMode() == false)
  636. #endif
  637. )
  638. {
  639. FOR_EACH_VEC( m_vecViewmodelArmModels, i )
  640. {
  641. if ( m_vecViewmodelArmModels[i] )
  642. {
  643. if ( m_vecViewmodelArmModels[i]->GetMoveParent() != this )
  644. {
  645. m_vecViewmodelArmModels[i]->SetEFlags( EF_BONEMERGE );
  646. m_vecViewmodelArmModels[i]->SetParent( this );
  647. }
  648. m_vecViewmodelArmModels[i]->DrawModel( flags | STUDIO_DONOTMODIFYSTENCILSTATE, instance );
  649. }
  650. }
  651. for ( int i=0; i < m_hStickerModelAddons.Count(); ++i )
  652. {
  653. if ( m_hStickerModelAddons[i] )
  654. {
  655. m_hStickerModelAddons[i]->DrawModel( flags, instance );
  656. }
  657. }
  658. if ( m_viewmodelStatTrakAddon )
  659. {
  660. m_viewmodelStatTrakAddon->DrawModel( flags | STUDIO_DONOTMODIFYSTENCILSTATE, instance );
  661. }
  662. if ( m_viewmodelUidAddon )
  663. {
  664. m_viewmodelUidAddon->DrawModel( flags | STUDIO_DONOTMODIFYSTENCILSTATE, instance );
  665. }
  666. }
  667. #ifdef IRONSIGHT
  668. //Scope stencil mask mode is automatically turned off after rendering. It needs to be explicitly enabled before each draw.
  669. if ( flags )
  670. SetScopeStencilMaskMode( false );
  671. #endif
  672. return ret;
  673. }
  674. //-----------------------------------------------------------------------------
  675. // Purpose: Called by the player when the player's overriding the viewmodel drawing. Avoids infinite recursion.
  676. //-----------------------------------------------------------------------------
  677. int C_BaseViewModel::DrawOverriddenViewmodel( C_BaseViewModel *pViewmodel, int flags, const RenderableInstance_t &instance )
  678. {
  679. return BaseClass::DrawModel( flags, instance );
  680. }
  681. //-----------------------------------------------------------------------------
  682. // Purpose:
  683. // Output : int
  684. //-----------------------------------------------------------------------------
  685. uint8 C_BaseViewModel::OverrideAlphaModulation( uint8 nAlpha )
  686. {
  687. ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( GetOwner() );
  688. // See if the local player wants to override the viewmodel's rendering
  689. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  690. if ( pPlayer && pPlayer->IsOverridingViewmodel() )
  691. return pPlayer->AlphaProp()->ComputeRenderAlpha();
  692. C_BaseCombatWeapon *pWeapon = GetOwningWeapon();
  693. if ( pWeapon && pWeapon->IsOverridingViewmodel() )
  694. return pWeapon->AlphaProp()->ComputeRenderAlpha();
  695. return nAlpha;
  696. }
  697. //-----------------------------------------------------------------------------
  698. // Purpose:
  699. // Output : Returns true on success, false on failure.
  700. //-----------------------------------------------------------------------------
  701. RenderableTranslucencyType_t C_BaseViewModel::ComputeTranslucencyType( void )
  702. {
  703. ACTIVE_SPLITSCREEN_PLAYER_GUARD_ENT( GetOwner() );
  704. // See if the local player wants to override the viewmodel's rendering
  705. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  706. if ( pPlayer && pPlayer->IsOverridingViewmodel() )
  707. return pPlayer->ComputeTranslucencyType();
  708. C_BaseCombatWeapon *pWeapon = GetOwningWeapon();
  709. if ( pWeapon && pWeapon->IsOverridingViewmodel() )
  710. return pWeapon->ComputeTranslucencyType();
  711. return BaseClass::ComputeTranslucencyType();
  712. }
  713. //-----------------------------------------------------------------------------
  714. // Purpose: If the animation parity of the weapon has changed, we reset cycle to avoid popping
  715. //-----------------------------------------------------------------------------
  716. void C_BaseViewModel::UpdateAnimationParity( void )
  717. {
  718. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  719. // If we're predicting, then we don't use animation parity because we change the animations on the clientside
  720. // while predicting. When not predicting, only the server changes the animations, so a parity mismatch
  721. // tells us if we need to reset the animation.
  722. if ( ( m_nOldAnimationParity != m_nAnimationParity && !GetPredictable() ) )
  723. {
  724. float curtime = (pPlayer && IsIntermediateDataAllocated()) ? pPlayer->GetFinalPredictedTime() : gpGlobals->curtime;
  725. // FIXME: this is bad
  726. // Simulate a networked m_flAnimTime and m_flCycle
  727. // FIXME: Do we need the magic 0.1?
  728. SetCycle( 0.0f ); // GetSequenceCycleRate( GetSequence() ) * 0.1;
  729. m_flAnimTime = curtime;
  730. m_fCycleOffset = 0.0f;
  731. }
  732. }
  733. //-----------------------------------------------------------------------------
  734. // Purpose: Update global map state based on data received
  735. // Input : bnewentity -
  736. //-----------------------------------------------------------------------------
  737. void C_BaseViewModel::OnDataChanged( DataUpdateType_t updateType )
  738. {
  739. if ( updateType == DATA_UPDATE_CREATED )
  740. {
  741. AlphaProp()->EnableAlphaModulationOverride( true );
  742. }
  743. SetPredictionEligible( true );
  744. BaseClass::OnDataChanged(updateType);
  745. }
  746. void C_BaseViewModel::PostDataUpdate( DataUpdateType_t updateType )
  747. {
  748. BaseClass::PostDataUpdate(updateType);
  749. OnLatchInterpolatedVariables( LATCH_ANIMATION_VAR );
  750. }
  751. //-----------------------------------------------------------------------------
  752. // Purpose: Return the player who will predict this entity
  753. //-----------------------------------------------------------------------------
  754. CBasePlayer *C_BaseViewModel::GetPredictionOwner()
  755. {
  756. return ToBasePlayer( GetOwner() );
  757. }
  758. //-----------------------------------------------------------------------------
  759. // Purpose:
  760. //-----------------------------------------------------------------------------
  761. void C_BaseViewModel::GetBoneControllers(float controllers[MAXSTUDIOBONECTRLS])
  762. {
  763. BaseClass::GetBoneControllers( controllers );
  764. // Tell the weapon itself that we've rendered, in case it wants to do something
  765. C_BasePlayer *pPlayer = ToBasePlayer( GetOwner() );
  766. if ( !pPlayer )
  767. return;
  768. C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
  769. if ( pWeapon )
  770. {
  771. pWeapon->GetViewmodelBoneControllers( this, controllers );
  772. }
  773. }
  774. void C_BaseViewModel::UpdateAllViewmodelAddons( void )
  775. {
  776. C_CSPlayer *pPlayer = ToCSPlayer( GetOwner() );
  777. // Remove any view model add ons if we're spectating.
  778. if ( !pPlayer )
  779. {
  780. RemoveViewmodelArmModels();
  781. RemoveViewmodelLabel();
  782. RemoveViewmodelStatTrak();
  783. RemoveViewmodelStickers();
  784. return;
  785. }
  786. C_BaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
  787. if ( !pWeapon )
  788. {
  789. RemoveViewmodelArmModels();
  790. RemoveViewmodelLabel();
  791. RemoveViewmodelStatTrak();
  792. RemoveViewmodelStickers();
  793. return;
  794. }
  795. CWeaponCSBase* pCSWeapon = dynamic_cast<CWeaponCSBase*>( pWeapon );
  796. if ( !pCSWeapon )
  797. {
  798. RemoveViewmodelArmModels();
  799. RemoveViewmodelLabel();
  800. RemoveViewmodelStatTrak();
  801. RemoveViewmodelStickers();
  802. return;
  803. }
  804. int weaponID = pCSWeapon->GetCSWeaponID();
  805. // Note: only arms race (gun game) knives change their bodygroup to indicate their team. This will soon go away.
  806. if ( weaponID == WEAPON_KNIFE_GG )
  807. {
  808. int bodyPartID = ( pPlayer->GetTeamNumber() == TEAM_TERRORIST ) ? 0 : 1;
  809. SetBodygroup( 0 , bodyPartID );
  810. }
  811. if ( pPlayer->m_pViewmodelArmConfig == NULL )
  812. {
  813. RemoveViewmodelArmModels();
  814. CStudioHdr *pHdr = pPlayer->GetModelPtr();
  815. if ( pHdr )
  816. {
  817. pPlayer->m_pViewmodelArmConfig = GetPlayerViewmodelArmConfigForPlayerModel( pHdr->pszName() );
  818. }
  819. }
  820. // add gloves and sleeves
  821. if ( pPlayer->m_pViewmodelArmConfig != NULL && m_vecViewmodelArmModels.Count() == 0 )
  822. {
  823. {
  824. AddViewmodelArmModel( pPlayer->m_pViewmodelArmConfig->szAssociatedGloveModel, atoi(pPlayer->m_pViewmodelArmConfig->szSkintoneIndex) );
  825. AddViewmodelArmModel( pPlayer->m_pViewmodelArmConfig->szAssociatedSleeveModel );
  826. }
  827. }
  828. // econ-related addons follow, so bail out if we can't get at the econitemview
  829. CEconItemView *pItem = pWeapon->GetEconItemView();
  830. if ( !pItem )
  831. {
  832. RemoveViewmodelLabel();
  833. RemoveViewmodelStatTrak();
  834. RemoveViewmodelStickers();
  835. return;
  836. }
  837. // verify weapon label and add if necessary
  838. AddViewmodelLabel( pItem );
  839. // verify stattrak module and add if necessary
  840. CUtlSortVector<uint32> vTypes;
  841. pItem->GetKillEaterTypes( vTypes );
  842. if ( (vTypes.Count() > 0) && ( pItem->GetKillEaterValueByType( vTypes[ vTypes.Count() - 1] ) >= 0 ) )
  843. {
  844. CSteamID HolderSteamID;
  845. pPlayer->GetSteamID( &HolderSteamID );
  846. AddViewmodelStatTrak( pItem, vTypes[ vTypes.Count() - 1], weaponID, HolderSteamID.GetAccountID() );
  847. }
  848. else
  849. {
  850. RemoveViewmodelStatTrak();
  851. }
  852. // add viewmodel stickers
  853. AddViewmodelStickers( pItem, weaponID );
  854. #ifdef IRONSIGHT
  855. if ( m_viewmodelScopeStencilMask )
  856. {
  857. C_ViewmodelAttachmentModel *pScopeMask = m_viewmodelScopeStencilMask.Get();
  858. if ( pScopeMask )
  859. pScopeMask->Remove();
  860. }
  861. #endif
  862. }
  863. C_ViewmodelAttachmentModel* C_BaseViewModel::FindArmModelForLoadoutPosition( loadout_positions_t nPosition ) const
  864. {
  865. /* Removed for partner depot */
  866. return NULL;
  867. }
  868. //--------------------------------------------------------------------------------------------------------
  869. C_ViewmodelAttachmentModel* C_BaseViewModel::AddViewmodelArmModel( const char *pszArmsModel, int nSkintoneIndex )
  870. {
  871. if ( pszArmsModel == NULL || pszArmsModel[ 0 ] == '\0' || modelinfo->GetModelIndex( pszArmsModel ) == -1 )
  872. {
  873. //pszArmsModel = //g_pGameTypes->GetCTViewModelArmsForMap( engine->GetLevelNameShort() );
  874. C_CSPlayer *pPlayer = ToCSPlayer( GetOwner() );
  875. pszArmsModel = pPlayer->m_szArmsModel.Get();
  876. }
  877. // Only create the view model attachment if we have a valid arm model
  878. if ( pszArmsModel == NULL || pszArmsModel[0] == '\0' || modelinfo->GetModelIndex( pszArmsModel ) == -1 )
  879. return NULL;
  880. C_ViewmodelAttachmentModel *pEnt = new class C_ViewmodelAttachmentModel;
  881. if ( pEnt && pEnt->InitializeAsClientEntity( pszArmsModel, true ) )
  882. {
  883. m_vecViewmodelArmModels[ m_vecViewmodelArmModels.AddToTail() ] = pEnt;
  884. if ( nSkintoneIndex != -1 )
  885. pEnt->SetSkin( nSkintoneIndex );
  886. pEnt->SetParent( this );
  887. pEnt->SetLocalOrigin( vec3_origin );
  888. pEnt->UpdatePartitionListEntry();
  889. pEnt->CollisionProp()->MarkPartitionHandleDirty();
  890. pEnt->UpdateVisibility();
  891. pEnt->SetViewmodel( this );
  892. pEnt->SetUseParentLightingOrigin( true );
  893. RemoveEffects( EF_NODRAW );
  894. return pEnt;
  895. }
  896. return NULL;
  897. }
  898. void C_BaseViewModel::AddViewmodelLabel( CEconItemView *pItem )
  899. {
  900. if ( !pItem || !pItem->GetCustomName() )
  901. {
  902. RemoveViewmodelLabel();
  903. return;
  904. } else if ( m_viewmodelUidAddon && m_viewmodelUidAddon.Get() && m_viewmodelUidAddon->GetMoveParent() )
  905. {
  906. return;
  907. }
  908. RemoveViewmodelLabel();
  909. C_ViewmodelAttachmentModel *pUidEnt = new class C_ViewmodelAttachmentModel;
  910. if ( pUidEnt && pUidEnt->InitializeAsClientEntity( pItem->GetUidModel(), true ) )
  911. {
  912. m_viewmodelUidAddon = pUidEnt;
  913. pUidEnt->SetParent( this );
  914. pUidEnt->SetLocalOrigin( vec3_origin );
  915. pUidEnt->UpdatePartitionListEntry();
  916. pUidEnt->CollisionProp()->MarkPartitionHandleDirty();
  917. pUidEnt->UpdateVisibility();
  918. pUidEnt->SetViewmodel( this );
  919. pUidEnt->SetUseParentLightingOrigin( true );
  920. if ( !cl_righthand.GetBool() )
  921. {
  922. pUidEnt->SetBodygroup( 0, 1 ); // use a special mirror-image that appears correct for lefties
  923. }
  924. RemoveEffects( EF_NODRAW );
  925. }
  926. }
  927. void C_BaseViewModel::AddViewmodelStatTrak( CEconItemView *pItem, int nStatTrakType, int nWeaponID, AccountID_t holderAcctId )
  928. {
  929. if ( m_viewmodelStatTrakAddon && m_viewmodelStatTrakAddon.Get() && m_viewmodelStatTrakAddon->GetMoveParent() )
  930. return;
  931. RemoveViewmodelStatTrak();
  932. if (!pItem)
  933. return;
  934. C_ViewmodelAttachmentModel *pStatTrakEnt = new class C_ViewmodelAttachmentModel;
  935. if ( pStatTrakEnt && pStatTrakEnt->InitializeAsClientEntity( pItem->GetStatTrakModelByType( nStatTrakType ), true ) )
  936. {
  937. m_viewmodelStatTrakAddon = pStatTrakEnt;
  938. pStatTrakEnt->SetParent( this );
  939. pStatTrakEnt->SetLocalOrigin( vec3_origin );
  940. pStatTrakEnt->UpdatePartitionListEntry();
  941. pStatTrakEnt->CollisionProp()->MarkPartitionHandleDirty();
  942. pStatTrakEnt->UpdateVisibility();
  943. pStatTrakEnt->SetViewmodel( this );
  944. pStatTrakEnt->SetUseParentLightingOrigin( true );
  945. if ( !cl_righthand.GetBool() )
  946. {
  947. pStatTrakEnt->SetBodygroup( 0, 1 ); // use a special mirror-image stattrak module that appears correct for lefties
  948. }
  949. // this stat trak weapon doesn't belong to the current holder, display error message on the digital display. This is impossible for knives
  950. if ( nWeaponID != WEAPON_KNIFE && nWeaponID != WEAPON_KNIFE_GG )
  951. {
  952. if ( pItem->GetAccountID() != holderAcctId )
  953. {
  954. pStatTrakEnt->SetBodygroup( 1, cl_righthand.GetBool() ? 1 : 2 ); // show the error screen bodygroup
  955. }
  956. }
  957. RemoveEffects( EF_NODRAW );
  958. }
  959. }
  960. bool C_BaseViewModel::ViewmodelStickersAreValid( int nWeaponID )
  961. {
  962. if ( m_hStickerModelAddons.Count() == 0 )
  963. {
  964. return false;
  965. }
  966. // returns true if all viewmodel sticker handles are non-null and appropriate for the given weapon id
  967. for ( int i=0; i < m_hStickerModelAddons.Count(); ++i )
  968. {
  969. if ( !m_hStickerModelAddons[i] || !m_hStickerModelAddons[i].Get() || !m_hStickerModelAddons[i]->GetMoveParent() )
  970. {
  971. return false;
  972. }
  973. }
  974. return true;
  975. }
  976. void C_BaseViewModel::AddViewmodelStickers( CEconItemView *pItem, int nWeaponID )
  977. {
  978. /* Removed for partner depot */
  979. }
  980. void C_BaseViewModel::RemoveViewmodelArmModels( void )
  981. {
  982. FOR_EACH_VEC_BACK( m_vecViewmodelArmModels, i )
  983. {
  984. C_ViewmodelAttachmentModel *pEnt = m_vecViewmodelArmModels[i].Get();
  985. if ( pEnt )
  986. {
  987. pEnt->Remove();
  988. }
  989. }
  990. m_vecViewmodelArmModels.RemoveAll();
  991. }
  992. void C_BaseViewModel::RemoveViewmodelLabel( void )
  993. {
  994. C_ViewmodelAttachmentModel *pUidEnt = m_viewmodelUidAddon.Get();
  995. if ( pUidEnt )
  996. {
  997. pUidEnt->Remove();
  998. }
  999. }
  1000. void C_BaseViewModel::RemoveViewmodelStatTrak( void )
  1001. {
  1002. C_ViewmodelAttachmentModel *pStatTrakEnt = m_viewmodelStatTrakAddon.Get();
  1003. if ( pStatTrakEnt )
  1004. {
  1005. pStatTrakEnt->Remove();
  1006. }
  1007. }
  1008. void C_BaseViewModel::RemoveViewmodelStickers( void )
  1009. {
  1010. for ( int i=0; i < m_hStickerModelAddons.Count(); ++i )
  1011. {
  1012. C_ViewmodelAttachmentModel *pStickerAddon = m_hStickerModelAddons[i];
  1013. if ( pStickerAddon )
  1014. {
  1015. pStickerAddon->Remove();
  1016. }
  1017. }
  1018. m_hStickerModelAddons.RemoveAll();
  1019. }
  1020. #if defined (_GAMECONSOLE)
  1021. //C_ViewmodelAttachmentModel
  1022. //C_ViewmodelAttachmentModel
  1023. //C_ViewmodelAttachmentModel
  1024. //C_ViewmodelAttachmentModel
  1025. //--------------------------------------------------------------------------------------------------------
  1026. bool C_ViewmodelAttachmentModel::InitializeAsClientEntity( const char *pszModelName, bool bRenderWithViewModels )
  1027. {
  1028. if ( !BaseClass::InitializeAsClientEntity( pszModelName, bRenderWithViewModels ) )
  1029. return false;
  1030. AddEffects( EF_BONEMERGE );
  1031. AddEffects( EF_BONEMERGE_FASTCULL );
  1032. AddEffects( EF_NODRAW );
  1033. return true;
  1034. }
  1035. void C_ViewmodelAttachmentModel::SetViewmodel( C_BaseViewModel *pVM )
  1036. {
  1037. m_hViewmodel = pVM;
  1038. }
  1039. int C_ViewmodelAttachmentModel::InternalDrawModel( int flags, const RenderableInstance_t &instance )
  1040. {
  1041. CMatRenderContextPtr pRenderContext( materials );
  1042. C_BaseViewModel *pViewmodel = m_hViewmodel;
  1043. if ( pViewmodel && pViewmodel->ShouldFlipModel() )
  1044. pRenderContext->CullMode( MATERIAL_CULLMODE_CW );
  1045. bool bValidMaterialOverride = (m_MaterialOverrides != NULL) && (m_MaterialOverrides->IsErrorMaterial() == false);
  1046. if (bValidMaterialOverride)
  1047. modelrender->ForcedMaterialOverride( m_MaterialOverrides );
  1048. int ret = BaseClass::InternalDrawModel( flags, instance );
  1049. if (bValidMaterialOverride)
  1050. modelrender->ForcedMaterialOverride( NULL );
  1051. pRenderContext->CullMode( MATERIAL_CULLMODE_CCW );
  1052. return ret;
  1053. }
  1054. #endif