Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2359 lines
71 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $Revision: $
  6. // $NoKeywords: $
  7. //
  8. // This file contains code to allow us to associate client data with bsp leaves.
  9. //
  10. //===========================================================================//
  11. #include "staticpropmgr.h"
  12. #include "convar.h"
  13. #include "vcollide_parse.h"
  14. #include "engine/ICollideable.h"
  15. #include "iclientunknown.h"
  16. #include "iclientrenderable.h"
  17. #include "gamebspfile.h"
  18. #include "engine/ivmodelrender.h"
  19. #include "engine/IClientLeafSystem.h"
  20. #include "ispatialpartitioninternal.h"
  21. #include "utlbuffer.h"
  22. #include "utlvector.h"
  23. #include "filesystem.h"
  24. #include "gl_model_private.h"
  25. #include "gl_matsysiface.h"
  26. #include "materialsystem/imaterialsystemhardwareconfig.h"
  27. #include "materialsystem/ivballoctracker.h"
  28. #include "materialsystem/imesh.h"
  29. #include "lightcache.h"
  30. #include "tier0/vprof.h"
  31. #include "render.h"
  32. #include "cmodel_engine.h"
  33. #include "datacache/imdlcache.h"
  34. #include "ModelInfo.h"
  35. #include "cdll_engine_int.h"
  36. #include "tier0/dbg.h"
  37. #include "debugoverlay.h"
  38. #include "draw.h"
  39. #include "client.h"
  40. #include "server.h"
  41. #include "l_studio.h"
  42. #include "tier0/icommandline.h"
  43. #include "sys_dll.h"
  44. #include "generichash.h"
  45. #include "tier2/renderutils.h"
  46. #include "ipooledvballocator.h"
  47. // memdbgon must be the last include file in a .cpp file!!!
  48. #include "tier0/memdbgon.h"
  49. //-----------------------------------------------------------------------------
  50. // Convars!
  51. //-----------------------------------------------------------------------------
  52. static ConVar r_DrawSpecificStaticProp( "r_DrawSpecificStaticProp", "-1" );
  53. static ConVar r_drawstaticprops( "r_drawstaticprops", "1", FCVAR_CHEAT, "0=Off, 1=Normal, 2=Wireframe" );
  54. static ConVar r_colorstaticprops( "r_colorstaticprops", "0", FCVAR_CHEAT );
  55. ConVar r_staticpropinfo( "r_staticpropinfo", "0" );
  56. ConVar r_drawmodeldecals( "r_drawmodeldecals", "1" );
  57. extern ConVar mat_fullbright;
  58. static bool g_MakingDevShots = false;
  59. //-----------------------------------------------------------------------------
  60. // Index into the fade list
  61. //-----------------------------------------------------------------------------
  62. enum
  63. {
  64. INVALID_FADE_INDEX = (unsigned short)~0
  65. };
  66. //-----------------------------------------------------------------------------
  67. // All static props have these bits set (to differentiate them from edict indices)
  68. //-----------------------------------------------------------------------------
  69. enum
  70. {
  71. // This bit will be set in GetRefEHandle for all static props
  72. STATICPROP_EHANDLE_MASK = 0x40000000
  73. };
  74. //-----------------------------------------------------------------------------
  75. // A default physics property for non-vphysics static props
  76. //-----------------------------------------------------------------------------
  77. static const objectparams_t g_PhysDefaultObjectParams =
  78. {
  79. NULL,
  80. 1.0, //mass
  81. 1.0, // inertia
  82. 0.1f, // damping
  83. 0.1f, // rotdamping
  84. 0.05f, // rotIntertiaLimit
  85. "DEFAULT",
  86. NULL,// game data
  87. 0.f, // volume (leave 0 if you don't have one or call physcollision->CollideVolume() to compute it)
  88. 1.0f, // drag coefficient
  89. true,// enable collisions?
  90. };
  91. // return true if the renderer should use the slow path that supports the various debug modes
  92. inline bool IsUsingStaticPropDebugModes()
  93. {
  94. if ( r_drawstaticprops.GetInt() != 1 ||
  95. r_DrawSpecificStaticProp.GetInt() >= 0 ||
  96. r_colorstaticprops.GetBool() ||
  97. r_staticpropinfo.GetInt() ||
  98. mat_fullbright.GetInt() ||
  99. r_drawmodellightorigin.GetBool() ||
  100. r_drawmodelstatsoverlay.GetBool() )
  101. return true;
  102. return false;
  103. }
  104. //-----------------------------------------------------------------------------
  105. // A static prop
  106. //-----------------------------------------------------------------------------
  107. class CStaticProp : public IClientUnknown, public IClientRenderable, public ICollideable
  108. {
  109. public:
  110. CStaticProp();
  111. ~CStaticProp();
  112. // IHandleEntity overrides
  113. public:
  114. virtual void SetRefEHandle( const CBaseHandle &handle );
  115. virtual const CBaseHandle& GetRefEHandle() const;
  116. // IClientUnknown overrides.
  117. public:
  118. virtual IClientUnknown* GetIClientUnknown() { return this; }
  119. virtual ICollideable* GetCollideable() { return this; }
  120. virtual IClientNetworkable* GetClientNetworkable() { return NULL; }
  121. virtual IClientRenderable* GetClientRenderable() { return this; }
  122. virtual IClientEntity* GetIClientEntity() { return NULL; }
  123. virtual C_BaseEntity* GetBaseEntity() { return NULL; }
  124. virtual IClientThinkable* GetClientThinkable() { return NULL; }
  125. public:
  126. // These methods return a box defined in the space of the entity
  127. virtual const Vector& OBBMinsPreScaled() const { return OBBMins(); }
  128. virtual const Vector& OBBMaxsPreScaled() const { return OBBMaxs(); }
  129. virtual const Vector& OBBMins() const;
  130. virtual const Vector& OBBMaxs() const;
  131. // custom collision test
  132. virtual bool TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr );
  133. // Perform hitbox test, returns true *if hitboxes were tested at all*!!
  134. virtual bool TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr );
  135. // Returns the BRUSH model index if this is a brush model. Otherwise, returns -1.
  136. virtual int GetCollisionModelIndex();
  137. // Return the model, if it's a studio model.
  138. virtual const model_t* GetCollisionModel();
  139. // Get angles and origin.
  140. virtual const Vector& GetCollisionOrigin() const;
  141. virtual const QAngle& GetCollisionAngles() const;
  142. virtual const matrix3x4_t& CollisionToWorldTransform() const;
  143. // Return a SOLID_ define.
  144. virtual SolidType_t GetSolid() const;
  145. virtual int GetSolidFlags() const;
  146. // Gets at the entity handle associated with the collideable
  147. virtual IHandleEntity *GetEntityHandle() { return this; }
  148. virtual int GetCollisionGroup() const { return COLLISION_GROUP_NONE; }
  149. virtual void WorldSpaceTriggerBounds( Vector* pVecWorldMins, Vector *pVecWorldMaxs ) const;
  150. virtual void WorldSpaceSurroundingBounds( Vector* pVecWorldMins, Vector *pVecWorldMaxs );
  151. virtual bool ShouldTouchTrigger( int triggerSolidFlags ) const { return false; }
  152. virtual const matrix3x4_t *GetRootParentToWorldTransform() const { return NULL; }
  153. // IClientRenderable overrides.
  154. public:
  155. virtual int GetBody() { return 0; }
  156. virtual int GetSkin() { return 0; }
  157. virtual const Vector& GetRenderOrigin( );
  158. virtual const QAngle& GetRenderAngles( );
  159. virtual bool ShouldDraw();
  160. virtual bool IsTransparent( void );
  161. virtual bool IsTwoPass( void );
  162. virtual void OnThreadedDrawSetup() {}
  163. virtual const model_t* GetModel( ) const;
  164. virtual int DrawModel( int flags );
  165. virtual void ComputeFxBlend( );
  166. virtual int GetFxBlend( );
  167. virtual void GetColorModulation( float* color );
  168. virtual bool LODTest() { return true; } // NOTE: UNUSED
  169. virtual bool SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime );
  170. virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights );
  171. virtual bool UsesFlexDelayedWeights() { return false; }
  172. virtual void DoAnimationEvents( void );
  173. virtual IPVSNotify* GetPVSNotifyInterface();
  174. virtual void GetRenderBounds( Vector& mins, Vector& maxs );
  175. virtual void GetRenderBoundsWorldspace( Vector& mins, Vector& maxs );
  176. virtual bool ShouldCacheRenderInfo();
  177. virtual bool ShouldReceiveProjectedTextures( int flags );
  178. virtual bool GetShadowCastDistance( float *pDist, ShadowType_t shadowType ) const { return false; }
  179. virtual bool GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const { return false; }
  180. virtual bool UsesPowerOfTwoFrameBufferTexture();
  181. virtual bool UsesFullFrameBufferTexture();
  182. virtual ClientShadowHandle_t GetShadowHandle() const { return CLIENTSHADOW_INVALID_HANDLE; }
  183. virtual ClientRenderHandle_t& RenderHandle();
  184. virtual void RecordToolMessage() {}
  185. // These normally call through to GetRenderAngles/GetRenderBounds, but some entities custom implement them.
  186. virtual void GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
  187. {
  188. GetRenderBounds( mins, maxs );
  189. }
  190. // Other methods related to shadow rendering
  191. virtual bool IsShadowDirty( ) { return false; }
  192. virtual void MarkShadowDirty( bool bDirty ) {}
  193. // Iteration over shadow hierarchy
  194. virtual IClientRenderable *GetShadowParent() { return NULL; }
  195. virtual IClientRenderable *FirstShadowChild() { return NULL; }
  196. virtual IClientRenderable *NextShadowPeer() { return NULL; }
  197. // Returns the shadow cast type
  198. virtual ShadowType_t ShadowCastType() { return SHADOWS_NONE; }
  199. // Create/get/destroy model instance
  200. virtual void CreateModelInstance() { Assert(0); }
  201. virtual ModelInstanceHandle_t GetModelInstance();
  202. // Attachments
  203. virtual int LookupAttachment( const char *pAttachmentName ) { return -1; }
  204. virtual bool GetAttachment( int number, Vector &origin, QAngle &angles );
  205. virtual bool GetAttachment( int number, matrix3x4_t &matrix );
  206. virtual bool IgnoresZBuffer( void ) const { return false; }
  207. // Rendering clip plane, should be 4 floats, return value of NULL indicates a disabled render clip plane
  208. virtual float *GetRenderClipPlane( void ) { return NULL; }
  209. // Returns the transform from RenderOrigin/RenderAngles to world
  210. virtual const matrix3x4_t &RenderableToWorldTransform()
  211. {
  212. return m_ModelToWorld;
  213. }
  214. public:
  215. bool Init( int index, StaticPropLump_t &lump, model_t *pModel );
  216. // KD Tree
  217. void InsertPropIntoKDTree();
  218. void RemovePropFromKDTree();
  219. void PrecacheLighting();
  220. void RecomputeStaticLighting();
  221. int LeafCount() const;
  222. int FirstLeaf() const;
  223. LightCacheHandle_t GetLightCacheHandle() const;
  224. void SetModelInstance( ModelInstanceHandle_t handle );
  225. void SetRenderHandle( ClientRenderHandle_t handle );
  226. void CleanUpRenderHandle( );
  227. ClientRenderHandle_t GetRenderHandle() const;
  228. void SetAlpha( unsigned char alpha );
  229. // Create VPhysics representation
  230. void CreateVPhysics( IPhysicsEnvironment *physenv, IVPhysicsKeyHandler *pDefaults, void *pGameData );
  231. float Radius() const { return m_flRadius; }
  232. int Flags() const { return m_Flags; }
  233. void SetFadeIndex( unsigned short nIndex ) { m_FadeIndex = nIndex; }
  234. unsigned short FadeIndex() const { return m_FadeIndex; }
  235. float ForcedFadeScale() const { return m_flForcedFadeScale; }
  236. int DrawModelSlow( int flags );
  237. private:
  238. // Diagnostic information for static props
  239. void DisplayStaticPropInfo( int nInfoType );
  240. inline void InitModelRenderInfo( ModelRenderInfo_t &sInfo, int flags )
  241. {
  242. sInfo.origin = m_Origin;
  243. sInfo.angles = m_Angles;
  244. sInfo.pRenderable = this;
  245. sInfo.pModel = m_pModel;
  246. sInfo.pModelToWorld = &m_ModelToWorld;
  247. sInfo.pLightingOffset = NULL;
  248. sInfo.pLightingOrigin = &m_LightingOrigin;
  249. sInfo.flags = flags;
  250. sInfo.entity_index = -1;
  251. sInfo.skin = m_Skin;
  252. sInfo.body = 0;
  253. sInfo.hitboxset = 0;
  254. sInfo.instance = m_ModelInstance;
  255. }
  256. private:
  257. friend class CStaticPropMgr;
  258. Vector m_Origin;
  259. QAngle m_Angles;
  260. model_t* m_pModel;
  261. SpatialPartitionHandle_t m_Partition;
  262. ModelInstanceHandle_t m_ModelInstance;
  263. unsigned char m_Alpha;
  264. unsigned char m_nSolidType;
  265. unsigned char m_Skin;
  266. unsigned char m_Flags;
  267. unsigned short m_FirstLeaf;
  268. unsigned short m_LeafCount;
  269. CBaseHandle m_EntHandle; // FIXME: Do I need client + server handles?
  270. ClientRenderHandle_t m_RenderHandle;
  271. unsigned short m_FadeIndex; // Index into the m_StaticPropFade dictionary
  272. float m_flForcedFadeScale;
  273. // bbox is the same for both GetBounds and GetRenderBounds since static props never move.
  274. // GetRenderBounds is interpolated data, and GetBounds is last networked.
  275. Vector m_RenderBBoxMin;
  276. Vector m_RenderBBoxMax;
  277. matrix3x4_t m_ModelToWorld;
  278. float m_flRadius;
  279. Vector m_WorldRenderBBoxMin;
  280. Vector m_WorldRenderBBoxMax;
  281. // FIXME: This sucks. Need to store the lighting origin off
  282. // because the time at which the static props are unserialized
  283. // doesn't necessarily match the time at which we can initialize the light cache
  284. Vector m_LightingOrigin;
  285. };
  286. //-----------------------------------------------------------------------------
  287. // The engine's static prop manager
  288. //-----------------------------------------------------------------------------
  289. class CStaticPropMgr : public IStaticPropMgrEngine, public IStaticPropMgrClient, public IStaticPropMgrServer
  290. {
  291. public:
  292. // constructor, destructor
  293. CStaticPropMgr();
  294. virtual ~CStaticPropMgr();
  295. // methods of IStaticPropMgrEngine
  296. virtual bool Init();
  297. virtual void Shutdown();
  298. virtual void LevelInit();
  299. virtual void LevelInitClient();
  300. virtual void LevelShutdown();
  301. virtual void LevelShutdownClient();
  302. virtual bool IsPropInPVS( IHandleEntity *pHandleEntity, const byte *pVis ) const;
  303. virtual ICollideable *GetStaticProp( IHandleEntity *pHandleEntity );
  304. virtual void RecomputeStaticLighting( );
  305. virtual LightCacheHandle_t GetLightCacheHandleForStaticProp( IHandleEntity *pHandleEntity );
  306. virtual bool IsStaticProp( IHandleEntity *pHandleEntity ) const;
  307. virtual bool IsStaticProp( CBaseHandle handle ) const;
  308. virtual int GetStaticPropIndex( IHandleEntity *pHandleEntity ) const;
  309. virtual ICollideable *GetStaticPropByIndex( int propIndex );
  310. // methods of IStaticPropMgrClient
  311. virtual void ComputePropOpacity( const Vector &viewOrigin, float factor );
  312. virtual void TraceRayAgainstStaticProp( const Ray_t& ray, int staticPropIndex, trace_t& tr );
  313. virtual void AddDecalToStaticProp( Vector const& rayStart, Vector const& rayEnd,
  314. int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr );
  315. virtual void AddColorDecalToStaticProp( Vector const& rayStart, Vector const& rayEnd,
  316. int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr, bool bUseColor, Color cColor );
  317. virtual void AddShadowToStaticProp( unsigned short shadowHandle, IClientRenderable* pRenderable );
  318. virtual void RemoveAllShadowsFromStaticProp( IClientRenderable* pRenderable );
  319. virtual void GetStaticPropMaterialColorAndLighting( trace_t* pTrace,
  320. int staticPropIndex, Vector& lighting, Vector& matColor );
  321. virtual void CreateVPhysicsRepresentations( IPhysicsEnvironment *physenv, IVPhysicsKeyHandler *pDefaults, void *pGameData );
  322. // methods of IStaticPropMgrServer
  323. //Changes made specifically to support the Portal mod (smack Dave Kircher if something breaks)
  324. //===================================================================
  325. virtual void GetAllStaticProps( CUtlVector<ICollideable *> *pOutput );
  326. virtual void GetAllStaticPropsInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector<ICollideable *> *pOutput );
  327. virtual void GetAllStaticPropsInOBB( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, CUtlVector<ICollideable *> *pOutput );
  328. //===================================================================
  329. virtual bool PropHasBakedLightingDisabled( IHandleEntity *pHandleEntity) const;
  330. // Internal methods
  331. const Vector &ViewOrigin() const { return m_vecLastViewOrigin; }
  332. // Computes the opacity for a single static prop
  333. void ComputePropOpacity( CStaticProp &prop );
  334. void DrawStaticProps( IClientRenderable **pProps, int count, bool bShadowDepth, bool drawVCollideWireframe );
  335. void DrawStaticProps_Slow( IClientRenderable **pProps, int count, bool bShadowDepth, bool drawVCollideWireframe );
  336. void DrawStaticProps_Fast( IClientRenderable **pProps, int count, bool bShadowDepth );
  337. void DrawStaticProps_FastPipeline( IClientRenderable **pProps, int count, bool bShadowDepth );
  338. private:
  339. void OutputLevelStats( void );
  340. void PrecacheLighting();
  341. // Methods associated with unserializing static props
  342. void UnserializeModelDict( CUtlBuffer& buf );
  343. void UnserializeLeafList( CUtlBuffer& buf );
  344. void UnserializeModels( CUtlBuffer& buf );
  345. void UnserializeStaticProps();
  346. int HandleEntityToIndex( IHandleEntity *pHandleEntity ) const;
  347. // Computes fade from screen-space fading
  348. unsigned char ComputeScreenFade( CStaticProp &prop, float flMinSize, float flMaxSize, float flFalloffFactor );
  349. void ChangeRenderGroup( CStaticProp &prop );
  350. private:
  351. // Unique static prop models
  352. struct StaticPropDict_t
  353. {
  354. model_t* m_pModel;
  355. MDLHandle_t m_hMDL;
  356. };
  357. // Static props that fade use this data to fade
  358. struct StaticPropFade_t
  359. {
  360. int m_Model;
  361. union
  362. {
  363. float m_MinDistSq;
  364. float m_MaxScreenWidth;
  365. };
  366. union
  367. {
  368. float m_MaxDistSq;
  369. float m_MinScreenWidth;
  370. };
  371. float m_FalloffFactor;
  372. };
  373. // The list of all static props
  374. CUtlVector <StaticPropDict_t> m_StaticPropDict;
  375. CUtlVector <CStaticProp> m_StaticProps;
  376. CUtlVector <StaticPropLeafLump_t> m_StaticPropLeaves;
  377. // Static props that fade...
  378. CUtlVector<StaticPropFade_t> m_StaticPropFade;
  379. bool m_bLevelInitialized;
  380. bool m_bClientInitialized;
  381. Vector m_vecLastViewOrigin;
  382. float m_flLastViewFactor;
  383. };
  384. //-----------------------------------------------------------------------------
  385. // Expose Interface to the game + client DLLs.
  386. //-----------------------------------------------------------------------------
  387. static CStaticPropMgr s_StaticPropMgr;
  388. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CStaticPropMgr, IStaticPropMgrClient, INTERFACEVERSION_STATICPROPMGR_CLIENT, s_StaticPropMgr);
  389. EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CStaticPropMgr, IStaticPropMgrServer, INTERFACEVERSION_STATICPROPMGR_SERVER, s_StaticPropMgr);
  390. //-----------------------------------------------------------------------------
  391. //
  392. // Static prop
  393. //
  394. //-----------------------------------------------------------------------------
  395. CStaticProp::CStaticProp() : m_pModel(0), m_Alpha(255)
  396. {
  397. m_ModelInstance = MODEL_INSTANCE_INVALID;
  398. m_Partition = PARTITION_INVALID_HANDLE;
  399. m_EntHandle = INVALID_EHANDLE_INDEX;
  400. m_RenderHandle = INVALID_CLIENT_RENDER_HANDLE;
  401. }
  402. CStaticProp::~CStaticProp()
  403. {
  404. RemovePropFromKDTree( );
  405. if (m_ModelInstance != MODEL_INSTANCE_INVALID)
  406. {
  407. modelrender->DestroyInstance( m_ModelInstance );
  408. }
  409. }
  410. //-----------------------------------------------------------------------------
  411. // Initialization
  412. //-----------------------------------------------------------------------------
  413. bool CStaticProp::Init( int index, StaticPropLump_t &lump, model_t *pModel )
  414. {
  415. m_EntHandle.Init(index, STATICPROP_EHANDLE_MASK >> NUM_ENT_ENTRY_BITS);
  416. m_Partition = PARTITION_INVALID_HANDLE;
  417. m_flForcedFadeScale = lump.m_flForcedFadeScale;
  418. VectorCopy( lump.m_Origin, m_Origin );
  419. VectorCopy( lump.m_Angles, m_Angles );
  420. m_pModel = pModel;
  421. m_FirstLeaf = lump.m_FirstLeaf;
  422. m_LeafCount = lump.m_LeafCount;
  423. m_nSolidType = lump.m_Solid;
  424. m_FadeIndex = INVALID_FADE_INDEX;
  425. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  426. studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( m_pModel );
  427. if ( pStudioHdr )
  428. {
  429. if ( !( pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP ) )
  430. {
  431. static int nBitchCount = 0;
  432. if( nBitchCount < 100 )
  433. {
  434. Warning( "model %s used as a static prop, but not compiled as a static prop\n", pStudioHdr->pszName() );
  435. nBitchCount++;
  436. }
  437. }
  438. if ( pStudioHdr->flags & STUDIOHDR_FLAGS_NO_FORCED_FADE )
  439. {
  440. m_flForcedFadeScale = 0.0f;
  441. }
  442. }
  443. switch ( m_nSolidType )
  444. {
  445. // These are valid
  446. case SOLID_VPHYSICS:
  447. case SOLID_BBOX:
  448. case SOLID_NONE:
  449. break;
  450. default:
  451. {
  452. char szModel[MAX_PATH];
  453. Q_strncpy( szModel, m_pModel ? modelloader->GetName( m_pModel ) : "unknown model", sizeof( szModel ) );
  454. Warning( "CStaticProp::Init: Map error, static_prop with bogus SOLID_ flag (%d)! (%s)\n", m_nSolidType, szModel );
  455. m_nSolidType = SOLID_NONE;
  456. }
  457. break;
  458. }
  459. m_Alpha = 255;
  460. m_Skin = (unsigned char)lump.m_Skin;
  461. m_Flags = ( lump.m_Flags & (STATIC_PROP_SCREEN_SPACE_FADE | STATIC_PROP_FLAG_FADES | STATIC_PROP_NO_PER_VERTEX_LIGHTING) );
  462. int nCurrentDXLevel = g_pMaterialSystemHardwareConfig->GetDXSupportLevel();
  463. bool bNoDraw = ( lump.m_nMinDXLevel && lump.m_nMinDXLevel > nCurrentDXLevel );
  464. bNoDraw = bNoDraw || ( lump.m_nMaxDXLevel && lump.m_nMaxDXLevel < nCurrentDXLevel );
  465. if ( bNoDraw )
  466. {
  467. m_Flags |= STATIC_PROP_NO_DRAW;
  468. }
  469. // Cache the model to world matrix since it never changes.
  470. AngleMatrix( lump.m_Angles, lump.m_Origin, m_ModelToWorld );
  471. // Cache the collision bounding box since it'll never change.
  472. modelinfo->GetModelRenderBounds( m_pModel, m_RenderBBoxMin, m_RenderBBoxMax );
  473. m_flRadius = m_RenderBBoxMin.DistTo( m_RenderBBoxMax ) * 0.5f;
  474. TransformAABB( m_ModelToWorld, m_RenderBBoxMin, m_RenderBBoxMax, m_WorldRenderBBoxMin, m_WorldRenderBBoxMax );
  475. // FIXME: Sucky, but unless we want to re-read the static prop lump when the client is
  476. // initialized (possible, but also gross), we need to cache off the illum center now
  477. if (lump.m_Flags & STATIC_PROP_USE_LIGHTING_ORIGIN)
  478. {
  479. m_LightingOrigin = lump.m_LightingOrigin;
  480. }
  481. else
  482. {
  483. modelinfo->GetIlluminationPoint( m_pModel, this, m_Origin, m_Angles, &m_LightingOrigin );
  484. }
  485. g_MakingDevShots = CommandLine()->FindParm( "-makedevshots" ) ? true : false;
  486. // If we do Mod_SetMaterialVarFlag() while running with the dedicated server, we crash.
  487. // RJ said he'd save my butt and look into this. (Hip hip horray! We love RJ!)
  488. if ( !sv.IsDedicated() && m_pModel )
  489. {
  490. Mod_SetMaterialVarFlag( pModel, MATERIAL_VAR_IGNORE_ALPHA_MODULATION, true );
  491. }
  492. return true;
  493. }
  494. //-----------------------------------------------------------------------------
  495. // EHandle
  496. //-----------------------------------------------------------------------------
  497. void CStaticProp::SetRefEHandle( const CBaseHandle &handle )
  498. {
  499. // Only the static prop mgr should be setting this...
  500. Assert( 0 );
  501. }
  502. const CBaseHandle& CStaticProp::GetRefEHandle() const
  503. {
  504. return m_EntHandle;
  505. }
  506. //-----------------------------------------------------------------------------
  507. // These methods return a box defined in the space of the entity
  508. //-----------------------------------------------------------------------------
  509. const Vector& CStaticProp::OBBMins( ) const
  510. {
  511. if ( GetSolid() == SOLID_VPHYSICS )
  512. {
  513. return m_pModel->mins;
  514. }
  515. Vector& tv = AllocTempVector();
  516. // FIXME: why doesn't this just return m_RenderBBoxMin?
  517. VectorSubtract( m_WorldRenderBBoxMin, GetCollisionOrigin(), tv );
  518. return tv;
  519. }
  520. const Vector& CStaticProp::OBBMaxs( ) const
  521. {
  522. if ( GetSolid() == SOLID_VPHYSICS )
  523. {
  524. return m_pModel->maxs;
  525. }
  526. Vector& tv = AllocTempVector();
  527. // FIXME: why doesn't this just return m_RenderBBoxMax?
  528. VectorSubtract( m_WorldRenderBBoxMax, GetCollisionOrigin(), tv );
  529. return tv;
  530. }
  531. void CStaticProp::WorldSpaceTriggerBounds( Vector* pVecWorldMins, Vector *pVecWorldMaxs ) const
  532. {
  533. // This should never be called..
  534. Assert(0);
  535. }
  536. //-----------------------------------------------------------------------------
  537. // Surrounding box
  538. //-----------------------------------------------------------------------------
  539. void CStaticProp::WorldSpaceSurroundingBounds( Vector* pVecWorldMins, Vector *pVecWorldMaxs )
  540. {
  541. *pVecWorldMins = m_WorldRenderBBoxMin;
  542. *pVecWorldMaxs = m_WorldRenderBBoxMax;
  543. }
  544. //-----------------------------------------------------------------------------
  545. // Data accessors
  546. //-----------------------------------------------------------------------------
  547. const Vector& CStaticProp::GetRenderOrigin( void )
  548. {
  549. return m_Origin;
  550. }
  551. const QAngle& CStaticProp::GetRenderAngles( void )
  552. {
  553. return m_Angles;
  554. }
  555. bool CStaticProp::GetAttachment( int number, Vector &origin, QAngle &angles )
  556. {
  557. origin = m_Origin;
  558. angles = m_Angles;
  559. return true;
  560. }
  561. bool CStaticProp::GetAttachment( int number, matrix3x4_t &matrix )
  562. {
  563. MatrixCopy( RenderableToWorldTransform(), matrix );
  564. return true;
  565. }
  566. bool CStaticProp::IsTransparent( void )
  567. {
  568. return (m_Alpha < 255) || modelinfo->IsTranslucent(m_pModel);
  569. }
  570. bool CStaticProp::IsTwoPass( void )
  571. {
  572. return modelinfo->IsTranslucentTwoPass(m_pModel);
  573. }
  574. bool CStaticProp::ShouldDraw()
  575. {
  576. return ( m_Flags & STATIC_PROP_NO_DRAW ) == 0;
  577. }
  578. //-----------------------------------------------------------------------------
  579. // Render setup
  580. //-----------------------------------------------------------------------------
  581. bool CStaticProp::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
  582. {
  583. if (!m_pModel)
  584. return false;
  585. MatrixCopy( m_ModelToWorld, pBoneToWorldOut[0] );
  586. return true;
  587. }
  588. void CStaticProp::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
  589. {
  590. }
  591. void CStaticProp::DoAnimationEvents( void )
  592. {
  593. }
  594. //-----------------------------------------------------------------------------
  595. // Render baby!
  596. //-----------------------------------------------------------------------------
  597. const model_t* CStaticProp::GetModel( ) const
  598. {
  599. return m_pModel;
  600. }
  601. //-----------------------------------------------------------------------------
  602. // Accessors
  603. //-----------------------------------------------------------------------------
  604. inline int CStaticProp::LeafCount() const
  605. {
  606. return m_LeafCount;
  607. }
  608. inline int CStaticProp::FirstLeaf() const
  609. {
  610. return m_FirstLeaf;
  611. }
  612. inline ModelInstanceHandle_t CStaticProp::GetModelInstance()
  613. {
  614. return m_ModelInstance;
  615. }
  616. inline void CStaticProp::SetModelInstance( ModelInstanceHandle_t handle )
  617. {
  618. m_ModelInstance = handle;
  619. }
  620. inline void CStaticProp::SetRenderHandle( ClientRenderHandle_t handle )
  621. {
  622. m_RenderHandle = handle;
  623. }
  624. inline ClientRenderHandle_t CStaticProp::GetRenderHandle() const
  625. {
  626. return m_RenderHandle;
  627. }
  628. void CStaticProp::CleanUpRenderHandle( )
  629. {
  630. if ( m_RenderHandle != INVALID_CLIENT_RENDER_HANDLE )
  631. {
  632. #ifndef SWDS
  633. clientleafsystem->RemoveRenderable( m_RenderHandle );
  634. #endif
  635. m_RenderHandle = INVALID_CLIENT_RENDER_HANDLE;
  636. }
  637. }
  638. //-----------------------------------------------------------------------------
  639. // Determine alpha and blend amount for transparent objects based on render state info
  640. //-----------------------------------------------------------------------------
  641. inline void CStaticProp::SetAlpha( unsigned char alpha )
  642. {
  643. m_Alpha = alpha;
  644. }
  645. void CStaticProp::ComputeFxBlend( )
  646. {
  647. s_StaticPropMgr.ComputePropOpacity( *this );
  648. }
  649. int CStaticProp::GetFxBlend( )
  650. {
  651. return m_Alpha;
  652. }
  653. void CStaticProp::GetColorModulation( float* color )
  654. {
  655. color[0] = color[1] = color[2] = 1.0f;
  656. }
  657. //-----------------------------------------------------------------------------
  658. // custom collision test
  659. //-----------------------------------------------------------------------------
  660. bool CStaticProp::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
  661. {
  662. Assert(0);
  663. return false;
  664. }
  665. //-----------------------------------------------------------------------------
  666. // Perform hitbox test, returns true *if hitboxes were tested at all*!!
  667. //-----------------------------------------------------------------------------
  668. bool CStaticProp::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr )
  669. {
  670. return false;
  671. }
  672. //-----------------------------------------------------------------------------
  673. // Returns the BRUSH model index if this is a brush model. Otherwise, returns -1.
  674. //-----------------------------------------------------------------------------
  675. int CStaticProp::GetCollisionModelIndex()
  676. {
  677. return -1;
  678. }
  679. //-----------------------------------------------------------------------------
  680. // Return the model, if it's a studio model.
  681. //-----------------------------------------------------------------------------
  682. const model_t* CStaticProp::GetCollisionModel()
  683. {
  684. return m_pModel;
  685. }
  686. //-----------------------------------------------------------------------------
  687. // Get angles and origin.
  688. //-----------------------------------------------------------------------------
  689. const Vector& CStaticProp::GetCollisionOrigin() const
  690. {
  691. return m_Origin;
  692. }
  693. const QAngle& CStaticProp::GetCollisionAngles() const
  694. {
  695. if ( GetSolid() == SOLID_VPHYSICS )
  696. {
  697. return m_Angles;
  698. }
  699. return vec3_angle;
  700. }
  701. const matrix3x4_t& CStaticProp::CollisionToWorldTransform() const
  702. {
  703. return m_ModelToWorld;
  704. }
  705. //-----------------------------------------------------------------------------
  706. // Return a SOLID_ define.
  707. //-----------------------------------------------------------------------------
  708. SolidType_t CStaticProp::GetSolid() const
  709. {
  710. return (SolidType_t)m_nSolidType;
  711. }
  712. int CStaticProp::GetSolidFlags() const
  713. {
  714. return 0;
  715. }
  716. bool CStaticProp::UsesPowerOfTwoFrameBufferTexture( void )
  717. {
  718. if ( !m_pModel )
  719. return false;
  720. return ( m_pModel->flags & MODELFLAG_STUDIOHDR_USES_FB_TEXTURE ) ? true : false;
  721. }
  722. bool CStaticProp::UsesFullFrameBufferTexture( void )
  723. {
  724. return false;
  725. }
  726. ClientRenderHandle_t& CStaticProp::RenderHandle()
  727. {
  728. return m_RenderHandle;
  729. }
  730. IPVSNotify* CStaticProp::GetPVSNotifyInterface()
  731. {
  732. return NULL;
  733. }
  734. void CStaticProp::GetRenderBounds( Vector& mins, Vector& maxs )
  735. {
  736. mins = m_RenderBBoxMin;
  737. maxs = m_RenderBBoxMax;
  738. }
  739. void CStaticProp::GetRenderBoundsWorldspace( Vector& mins, Vector& maxs )
  740. {
  741. mins = m_WorldRenderBBoxMin;
  742. maxs = m_WorldRenderBBoxMax;
  743. }
  744. bool CStaticProp::ShouldReceiveProjectedTextures( int flags )
  745. {
  746. if( flags & SHADOW_FLAGS_FLASHLIGHT )
  747. {
  748. return true;
  749. }
  750. else
  751. {
  752. return false;
  753. }
  754. }
  755. bool CStaticProp::ShouldCacheRenderInfo()
  756. {
  757. return true;
  758. }
  759. void CStaticProp::PrecacheLighting()
  760. {
  761. #ifndef SWDS
  762. if ( m_ModelInstance == MODEL_INSTANCE_INVALID )
  763. {
  764. LightCacheHandle_t lightCacheHandle = CreateStaticLightingCache( m_LightingOrigin, m_WorldRenderBBoxMin, m_WorldRenderBBoxMax );
  765. m_ModelInstance = modelrender->CreateInstance( this, &lightCacheHandle );
  766. }
  767. #endif
  768. }
  769. void CStaticProp::RecomputeStaticLighting( void )
  770. {
  771. #ifndef SWDS
  772. modelrender->RecomputeStaticLighting( m_ModelInstance );
  773. #endif
  774. }
  775. //-----------------------------------------------------------------------------
  776. // Diagnostic information for static props
  777. //-----------------------------------------------------------------------------
  778. void CStaticProp::DisplayStaticPropInfo( int nInfoType )
  779. {
  780. #ifndef SWDS
  781. char buf[512];
  782. switch( nInfoType )
  783. {
  784. case 1:
  785. Q_snprintf( buf, sizeof( buf ), "%s", modelloader->GetName( m_pModel ) );
  786. break;
  787. case 2:
  788. Q_snprintf(buf, sizeof( buf ), "%d", (m_EntHandle.ToInt() & (~STATICPROP_EHANDLE_MASK)) );
  789. break;
  790. case 3:
  791. {
  792. float flDist = GetRenderOrigin().DistTo( s_StaticPropMgr.ViewOrigin() );
  793. Q_snprintf(buf, sizeof( buf ), "%.1f", flDist );
  794. }
  795. break;
  796. case 4:
  797. {
  798. CMatRenderContextPtr pRenderContext( materials );
  799. float flPixelWidth = pRenderContext->ComputePixelWidthOfSphere( GetRenderOrigin(), Radius() );
  800. Q_snprintf(buf, sizeof( buf ), "%.1f", flPixelWidth );
  801. }
  802. break;
  803. }
  804. Vector vecTextBox = ( m_WorldRenderBBoxMax + m_WorldRenderBBoxMin ) * 0.5f;
  805. vecTextBox.z = m_WorldRenderBBoxMax.z + 10;
  806. CDebugOverlay::AddTextOverlay( vecTextBox, 0.0f, buf );
  807. #endif
  808. }
  809. //-----------------------------------------------------------------------------
  810. // Draws the model
  811. //-----------------------------------------------------------------------------
  812. int CStaticProp::DrawModelSlow( int flags )
  813. {
  814. #ifndef SWDS
  815. VPROF_BUDGET( "CStaticProp::DrawModel", VPROF_BUDGETGROUP_STATICPROP_RENDERING );
  816. if ( !r_drawstaticprops.GetBool() )
  817. return 0;
  818. if ( r_drawstaticprops.GetInt() == 2 )
  819. {
  820. flags |= STUDIO_WIREFRAME;
  821. }
  822. #ifdef _DEBUG
  823. if ( r_DrawSpecificStaticProp.GetInt() >= 0 )
  824. {
  825. if ( (m_EntHandle.ToInt() & (~STATICPROP_EHANDLE_MASK) ) != r_DrawSpecificStaticProp.GetInt() )
  826. return 0;
  827. }
  828. #endif
  829. if ( (m_Alpha == 0) || !m_pModel )
  830. return 0;
  831. #ifdef _DEBUG
  832. studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( m_pModel );
  833. Assert( pStudioHdr );
  834. if ( !( pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP ) )
  835. {
  836. return 0;
  837. }
  838. #endif
  839. if ( r_colorstaticprops.GetBool() )
  840. {
  841. // deterministic random sequence
  842. unsigned short hash[3];
  843. hash[0] = HashItem( m_ModelInstance );
  844. hash[1] = HashItem( hash[0] );
  845. hash[2] = HashItem( hash[1] );
  846. r_colormod[0] = (float)hash[0] * 1.0f/65535.0f;
  847. r_colormod[1] = (float)hash[1] * 1.0f/65535.0f;
  848. r_colormod[2] = (float)hash[2] * 1.0f/65535.0f;
  849. VectorNormalize( r_colormod );
  850. }
  851. flags |= STUDIO_STATIC_LIGHTING;
  852. int nInfoType = r_staticpropinfo.GetInt();
  853. if ( nInfoType )
  854. {
  855. DisplayStaticPropInfo( nInfoType );
  856. }
  857. // CDebugOverlay::AddBoxOverlay( vec3_origin, m_WorldRenderBBoxMin, m_WorldRenderBBoxMax, vec3_angle, 255, 0, 0, 32, 0.01 );
  858. // CDebugOverlay::AddBoxOverlay( GetRenderOrigin(), m_RenderBBoxMin, m_RenderBBoxMax, GetRenderAngles(), 0, 255, 0, 32, 0.01 );
  859. ModelRenderInfo_t sInfo;
  860. InitModelRenderInfo( sInfo, flags );
  861. g_pStudioRender->SetColorModulation( r_colormod );
  862. g_pStudioRender->SetAlphaModulation( r_blend );
  863. // Restore the matrices if we're skinning
  864. CMatRenderContextPtr pRenderContext( materials );
  865. pRenderContext->MatrixMode( MATERIAL_MODEL );
  866. pRenderContext->PushMatrix();
  867. pRenderContext->LoadIdentity();
  868. int drawn = modelrender->DrawModelEx( sInfo );
  869. pRenderContext->MatrixMode( MATERIAL_MODEL );
  870. pRenderContext->PopMatrix();
  871. if ( m_pModel && (flags & STUDIO_WIREFRAME_VCOLLIDE) )
  872. {
  873. if ( m_nSolidType == SOLID_VPHYSICS )
  874. {
  875. // This works because VCollideForModel only uses modelindex for mod_brush
  876. // and props are always mod_Studio.
  877. vcollide_t * pCollide = CM_VCollideForModel( -1, m_pModel );
  878. if ( pCollide && pCollide->solidCount == 1 )
  879. {
  880. static color32 debugColor = {0,255,255,0};
  881. DebugDrawPhysCollide( pCollide->solids[0], NULL, m_ModelToWorld, debugColor, false );
  882. }
  883. }
  884. else if ( m_nSolidType == SOLID_BBOX )
  885. {
  886. static Color debugColor( 0, 255, 255, 255 );
  887. RenderWireframeBox( m_Origin, vec3_angle, m_pModel->mins, m_pModel->maxs, debugColor, true );
  888. }
  889. }
  890. return drawn;
  891. #else
  892. return 0;
  893. #endif
  894. }
  895. int CStaticProp::DrawModel( int flags )
  896. {
  897. #ifndef SWDS
  898. VPROF_BUDGET( "CStaticProp::DrawModel", VPROF_BUDGETGROUP_STATICPROP_RENDERING );
  899. if ( (m_Alpha == 0) || !m_pModel )
  900. return 0;
  901. if ( IsUsingStaticPropDebugModes() || (flags & STUDIO_WIREFRAME_VCOLLIDE) )
  902. return DrawModelSlow(flags);
  903. flags |= STUDIO_STATIC_LIGHTING;
  904. ModelRenderInfo_t sInfo;
  905. InitModelRenderInfo( sInfo, flags );
  906. g_pStudioRender->SetColorModulation( r_colormod );
  907. g_pStudioRender->SetAlphaModulation( r_blend );
  908. // Restore the matrices if we're skinning
  909. CMatRenderContextPtr pRenderContext( materials );
  910. pRenderContext->MatrixMode( MATERIAL_MODEL );
  911. pRenderContext->PushMatrix();
  912. pRenderContext->LoadIdentity();
  913. int drawn = modelrender->DrawModelExStaticProp( sInfo );
  914. pRenderContext->MatrixMode( MATERIAL_MODEL );
  915. pRenderContext->PopMatrix();
  916. return drawn;
  917. #else
  918. return 0;
  919. #endif
  920. }
  921. //-----------------------------------------------------------------------------
  922. // KD Tree
  923. //-----------------------------------------------------------------------------
  924. void CStaticProp::InsertPropIntoKDTree()
  925. {
  926. Assert( m_Partition == PARTITION_INVALID_HANDLE );
  927. if ( m_nSolidType == SOLID_NONE )
  928. return;
  929. // Compute the bbox of the prop
  930. Vector mins, maxs;
  931. matrix3x4_t propToWorld;
  932. AngleMatrix( m_Angles, m_Origin, propToWorld );
  933. TransformAABB( propToWorld, m_pModel->mins, m_pModel->maxs, mins, maxs );
  934. // If it's using vphysics, get a good AABB
  935. if ( m_nSolidType == SOLID_VPHYSICS )
  936. {
  937. vcollide_t *pCollide = CM_VCollideForModel( -1, m_pModel );
  938. if ( pCollide && pCollide->solidCount )
  939. {
  940. physcollision->CollideGetAABB( &mins, &maxs, pCollide->solids[0], m_Origin, m_Angles );
  941. }
  942. else
  943. {
  944. char szModel[MAX_PATH];
  945. Q_strncpy( szModel, m_pModel ? modelloader->GetName( m_pModel ) : "unknown model", sizeof( szModel ) );
  946. Warning( "SOLID_VPHYSICS static prop with no vphysics model! (%s)\n", szModel );
  947. m_nSolidType = SOLID_NONE;
  948. return;
  949. }
  950. }
  951. // add the entity to the KD tree so we will collide against it
  952. m_Partition = SpatialPartition()->CreateHandle( this,
  953. PARTITION_CLIENT_SOLID_EDICTS | PARTITION_CLIENT_STATIC_PROPS |
  954. PARTITION_ENGINE_SOLID_EDICTS | PARTITION_ENGINE_STATIC_PROPS,
  955. mins, maxs );
  956. Assert( m_Partition != PARTITION_INVALID_HANDLE );
  957. }
  958. void CStaticProp::RemovePropFromKDTree()
  959. {
  960. // Release the spatial partition handle
  961. if ( m_Partition != PARTITION_INVALID_HANDLE )
  962. {
  963. SpatialPartition()->DestroyHandle( m_Partition );
  964. m_Partition = PARTITION_INVALID_HANDLE;
  965. }
  966. }
  967. //-----------------------------------------------------------------------------
  968. // Create VPhysics representation
  969. //-----------------------------------------------------------------------------
  970. void CStaticProp::CreateVPhysics( IPhysicsEnvironment *pPhysEnv, IVPhysicsKeyHandler *pDefaults, void *pGameData )
  971. {
  972. if ( m_nSolidType == SOLID_NONE )
  973. return;
  974. vcollide_t *pVCollide = NULL;
  975. solid_t solid;
  976. CPhysCollide* pPhysCollide = NULL;
  977. if ( m_pModel && m_nSolidType == SOLID_VPHYSICS )
  978. {
  979. // This works because VCollideForModel only uses modelindex for mod_brush
  980. // and props are always mod_Studio.
  981. pVCollide = CM_VCollideForModel( -1, m_pModel );
  982. }
  983. if (pVCollide)
  984. {
  985. pPhysCollide = pVCollide->solids[0];
  986. IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pVCollide->pKeyValues );
  987. while ( !pParse->Finished() )
  988. {
  989. const char *pBlock = pParse->GetCurrentBlockName();
  990. if ( !strcmpi( pBlock, "solid" ) )
  991. {
  992. pParse->ParseSolid( &solid, pDefaults );
  993. break;
  994. }
  995. else
  996. {
  997. pParse->SkipBlock();
  998. }
  999. }
  1000. physcollision->VPhysicsKeyParserDestroy( pParse );
  1001. }
  1002. else
  1003. {
  1004. if ( m_nSolidType != SOLID_BBOX )
  1005. {
  1006. char szModel[MAX_PATH];
  1007. Q_strncpy( szModel, m_pModel ? modelloader->GetName( m_pModel ) : "unknown model", sizeof( szModel ) );
  1008. Warning( "Map Error: Static prop with bogus solid type %d! (%s)\n", m_nSolidType, szModel );
  1009. m_nSolidType = SOLID_NONE;
  1010. return;
  1011. }
  1012. #ifdef _XBOX
  1013. else
  1014. solid.surfaceprop[0] = '\0';
  1015. #endif
  1016. // If there's no collide, we need a bbox...
  1017. pPhysCollide = physcollision->BBoxToCollide( m_pModel->mins, m_pModel->maxs );
  1018. solid.params = g_PhysDefaultObjectParams;
  1019. }
  1020. Assert(pPhysCollide);
  1021. solid.params.enableCollisions = true;
  1022. solid.params.pGameData = pGameData;
  1023. solid.params.pName = "prop_static";
  1024. int surfaceData = physprop->GetSurfaceIndex( solid.surfaceprop );
  1025. pPhysEnv->CreatePolyObjectStatic( pPhysCollide,
  1026. surfaceData, m_Origin, m_Angles, &solid.params );
  1027. //PhysCheckAdd( pPhys, "Static" );
  1028. }
  1029. //-----------------------------------------------------------------------------
  1030. // Expose IStaticPropMgr to the engine
  1031. //-----------------------------------------------------------------------------
  1032. IStaticPropMgrEngine* StaticPropMgr()
  1033. {
  1034. return &s_StaticPropMgr;
  1035. }
  1036. //-----------------------------------------------------------------------------
  1037. // constructor, destructor
  1038. //-----------------------------------------------------------------------------
  1039. CStaticPropMgr::CStaticPropMgr()
  1040. {
  1041. m_bLevelInitialized = false;
  1042. m_bClientInitialized = false;
  1043. }
  1044. CStaticPropMgr::~CStaticPropMgr()
  1045. {
  1046. Assert( !m_bLevelInitialized );
  1047. }
  1048. //-----------------------------------------------------------------------------
  1049. // Purpose:
  1050. // Output : Returns true on success, false on failure.
  1051. //-----------------------------------------------------------------------------
  1052. bool CStaticPropMgr::Init()
  1053. {
  1054. return true;
  1055. }
  1056. //-----------------------------------------------------------------------------
  1057. // Purpose:
  1058. //-----------------------------------------------------------------------------
  1059. void CStaticPropMgr::Shutdown()
  1060. {
  1061. if ( !m_bLevelInitialized )
  1062. return;
  1063. LevelShutdown();
  1064. }
  1065. //-----------------------------------------------------------------------------
  1066. // Unserialize static prop model dictionary
  1067. //-----------------------------------------------------------------------------
  1068. void CStaticPropMgr::UnserializeModelDict( CUtlBuffer& buf )
  1069. {
  1070. int count = buf.GetInt();
  1071. m_StaticPropDict.AddMultipleToTail( count );
  1072. for ( int i=0; i < count; i++ )
  1073. {
  1074. StaticPropDictLump_t lump;
  1075. buf.Get( &lump, sizeof(StaticPropDictLump_t) );
  1076. StaticPropDict_t &dict = m_StaticPropDict[i];
  1077. dict.m_pModel = (model_t *)modelloader->GetModelForName(
  1078. lump.m_Name, IModelLoader::FMODELLOADER_STATICPROP );
  1079. dict.m_hMDL = modelinfo->GetCacheHandle( dict.m_pModel );
  1080. g_pMDLCache->LockStudioHdr( dict.m_hMDL );
  1081. }
  1082. }
  1083. void CStaticPropMgr::UnserializeLeafList( CUtlBuffer& buf )
  1084. {
  1085. int nCount = buf.GetInt();
  1086. m_StaticPropLeaves.Purge();
  1087. if ( nCount > 0 )
  1088. {
  1089. m_StaticPropLeaves.AddMultipleToTail( nCount );
  1090. buf.Get( m_StaticPropLeaves.Base(), nCount * sizeof(StaticPropLeafLump_t) );
  1091. }
  1092. }
  1093. template <typename SerializedLumpType>
  1094. void UnserializeLump( StaticPropLump_t* _output, CUtlBuffer& buf )
  1095. {
  1096. Assert(_output != NULL);
  1097. SerializedLumpType srcLump;
  1098. buf.Get( &srcLump, sizeof(SerializedLumpType) );
  1099. (*_output) = srcLump;
  1100. }
  1101. // Specialization for current version.
  1102. template <>
  1103. void UnserializeLump<StaticPropLump_t>(StaticPropLump_t* _output, CUtlBuffer& buf)
  1104. {
  1105. Assert(_output != NULL);
  1106. buf.Get(_output, sizeof(StaticPropLump_t));
  1107. }
  1108. void CStaticPropMgr::UnserializeModels( CUtlBuffer& buf )
  1109. {
  1110. // Version check
  1111. int nLumpVersion = Mod_GameLumpVersion( GAMELUMP_STATIC_PROPS );
  1112. if ( nLumpVersion < 4 )
  1113. {
  1114. Warning("Really old map format! Static props can't be loaded...\n");
  1115. return;
  1116. }
  1117. int count = buf.GetInt();
  1118. // Gotta preallocate the static props here so no rellocations take place
  1119. // the leaf list stores pointers to these tricky little guys.
  1120. m_StaticProps.AddMultipleToTail(count);
  1121. for ( int i = 0; i < count; ++i )
  1122. {
  1123. StaticPropLump_t lump;
  1124. switch ( nLumpVersion )
  1125. {
  1126. case 4: UnserializeLump<StaticPropLumpV4_t>(&lump, buf); break;
  1127. case 5: UnserializeLump<StaticPropLumpV5_t>(&lump, buf); break;
  1128. case 6: UnserializeLump<StaticPropLumpV6_t>(&lump, buf); break;
  1129. case 7: // Falls down to version 10. We promoted TF to version 10 to deal with SFM.
  1130. case 10: UnserializeLump<StaticPropLump_t>(&lump, buf); break;
  1131. break;
  1132. default:
  1133. Assert("Unexpected version while deserializing lumps.");
  1134. }
  1135. m_StaticProps[i].Init( i, lump, m_StaticPropDict[lump.m_PropType].m_pModel );
  1136. // For distance-based fading, keep a list of the things that need
  1137. // to be faded out. Not sure if this is the optimal way of doing it
  1138. // but it's easy for now; we'll have to test later how large this list gets.
  1139. // If it's <100 or so, we should be fine
  1140. if (lump.m_Flags & STATIC_PROP_FLAG_FADES)
  1141. {
  1142. int idx = m_StaticPropFade.AddToTail();
  1143. m_StaticProps[i].SetFadeIndex( (unsigned short)idx );
  1144. StaticPropFade_t& fade = m_StaticPropFade[idx];
  1145. fade.m_Model = i;
  1146. fade.m_MinDistSq = lump.m_FadeMinDist;
  1147. fade.m_MaxDistSq = lump.m_FadeMaxDist;
  1148. if ( (lump.m_Flags & STATIC_PROP_SCREEN_SPACE_FADE) == 0 )
  1149. {
  1150. fade.m_MinDistSq *= fade.m_MinDistSq;
  1151. fade.m_MaxDistSq *= fade.m_MaxDistSq;
  1152. }
  1153. if (fade.m_MaxDistSq != fade.m_MinDistSq)
  1154. {
  1155. if (lump.m_Flags & STATIC_PROP_SCREEN_SPACE_FADE)
  1156. {
  1157. fade.m_FalloffFactor = 255.0f / (fade.m_MaxScreenWidth - fade.m_MinScreenWidth);
  1158. }
  1159. else
  1160. {
  1161. fade.m_FalloffFactor = 255.0f / (fade.m_MaxDistSq - fade.m_MinDistSq);
  1162. }
  1163. }
  1164. else
  1165. {
  1166. fade.m_FalloffFactor = 255.0f;
  1167. }
  1168. }
  1169. // Add the prop to the K-D tree for collision
  1170. m_StaticProps[i].InsertPropIntoKDTree( );
  1171. }
  1172. }
  1173. void CStaticPropMgr::OutputLevelStats( void )
  1174. {
  1175. // STATS
  1176. int i;
  1177. int totalVerts = 0;
  1178. for( i = 0; i < m_StaticProps.Count(); i++ )
  1179. {
  1180. CStaticProp *pStaticProp = &m_StaticProps[i];
  1181. model_t *pModel = (model_t*)pStaticProp->GetModel();
  1182. if( !pModel )
  1183. {
  1184. continue;
  1185. }
  1186. Assert( pModel->type == mod_studio );
  1187. studiohdr_t *pStudioHdr = ( studiohdr_t * )modelloader->GetExtraData( pModel );
  1188. int bodyPart;
  1189. for( bodyPart = 0; bodyPart < pStudioHdr->numbodyparts; bodyPart++ )
  1190. {
  1191. mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyPart );
  1192. int model;
  1193. for( model = 0; model < pBodyPart->nummodels; model++ )
  1194. {
  1195. mstudiomodel_t *pStudioModel = pBodyPart->pModel( model );
  1196. totalVerts += pStudioModel->numvertices;
  1197. }
  1198. }
  1199. }
  1200. Warning( "%d static prop instances in map\n", ( int )m_StaticProps.Count() );
  1201. Warning( "%d static prop models in map\n", ( int )m_StaticPropDict.Count() );
  1202. Warning( "%d static prop verts in map\n", ( int )totalVerts );
  1203. }
  1204. //-----------------------------------------------------------------------------
  1205. // Unserialize static props
  1206. //-----------------------------------------------------------------------------
  1207. void CStaticPropMgr::UnserializeStaticProps()
  1208. {
  1209. // Unserialize static props, insert them into the appropriate leaves
  1210. int size = Mod_GameLumpSize( GAMELUMP_STATIC_PROPS );
  1211. if (!size)
  1212. return;
  1213. COM_TimestampedLog( "UnserializeStaticProps - start");
  1214. MEM_ALLOC_CREDIT();
  1215. CUtlBuffer buf( 0, size );
  1216. if ( Mod_LoadGameLump( GAMELUMP_STATIC_PROPS, buf.PeekPut(), size ))
  1217. {
  1218. buf.SeekPut( CUtlBuffer::SEEK_HEAD, size );
  1219. COM_TimestampedLog( "UnserializeModelDict" );
  1220. UnserializeModelDict( buf );
  1221. COM_TimestampedLog( "UnserializeLeafList" );
  1222. UnserializeLeafList( buf );
  1223. COM_TimestampedLog( "UnserializeModels" );
  1224. UnserializeModels( buf );
  1225. }
  1226. COM_TimestampedLog( "UnserializeStaticProps - end");
  1227. }
  1228. //-----------------------------------------------------------------------------
  1229. // Level init, shutdown
  1230. //-----------------------------------------------------------------------------
  1231. void CStaticPropMgr::LevelInit()
  1232. {
  1233. if ( m_bLevelInitialized )
  1234. return;
  1235. Assert( !m_bClientInitialized );
  1236. m_bLevelInitialized = true;
  1237. // Read in static props that have been compiled into the bsp file
  1238. UnserializeStaticProps();
  1239. // OutputLevelStats();
  1240. }
  1241. void CStaticPropMgr::LevelShutdown()
  1242. {
  1243. if ( !m_bLevelInitialized )
  1244. return;
  1245. // Deal with client-side stuff, if appropriate
  1246. if ( m_bClientInitialized )
  1247. {
  1248. LevelShutdownClient();
  1249. }
  1250. m_bLevelInitialized = false;
  1251. FOR_EACH_VEC( m_StaticPropDict, i )
  1252. {
  1253. g_pMDLCache->UnlockStudioHdr( m_StaticPropDict[i].m_hMDL );
  1254. }
  1255. m_StaticProps.Purge();
  1256. m_StaticPropDict.Purge();
  1257. m_StaticPropFade.Purge();
  1258. }
  1259. void CStaticPropMgr::LevelInitClient()
  1260. {
  1261. #ifndef SWDS
  1262. if ( sv.IsDedicated() )
  1263. return;
  1264. extern ConVar r_proplightingfromdisk;
  1265. bool bNeedsMapAccess = r_proplightingfromdisk.GetBool();
  1266. if ( bNeedsMapAccess )
  1267. {
  1268. g_pFileSystem->BeginMapAccess();
  1269. }
  1270. Assert( m_bLevelInitialized );
  1271. Assert( !m_bClientInitialized );
  1272. // Since the client will be ready at a later time than the server
  1273. // to set up its data, we need a separate call to handle that
  1274. int nCount = m_StaticProps.Count();
  1275. for ( int i = 0; i < nCount; ++i )
  1276. {
  1277. CStaticProp &prop = m_StaticProps[i];
  1278. clientleafsystem->CreateRenderableHandle( &m_StaticProps[i], true );
  1279. if ( !prop.ShouldDraw() )
  1280. continue;
  1281. ClientRenderHandle_t handle = m_StaticProps[i].RenderHandle();
  1282. if ( prop.LeafCount() > 0 )
  1283. {
  1284. // Add the prop to all the leaves it lies in
  1285. clientleafsystem->AddRenderableToLeaves( handle, prop.LeafCount(), (unsigned short*)&m_StaticPropLeaves[prop.FirstLeaf()] );
  1286. }
  1287. else
  1288. {
  1289. Vector origin = prop.GetCollisionOrigin();
  1290. Vector mins = prop.OBBMins();
  1291. Vector maxs = prop.OBBMaxs();
  1292. DevMsg( 1, "Static prop in 0 leaves! %s, @ %.1f, %.1f, %.1f\n", modelloader->GetName( prop.GetModel() ), origin.x, origin.y, origin.z );
  1293. }
  1294. }
  1295. PrecacheLighting();
  1296. m_bClientInitialized = true;
  1297. if ( bNeedsMapAccess )
  1298. {
  1299. g_pFileSystem->EndMapAccess();
  1300. }
  1301. #endif
  1302. }
  1303. void CStaticPropMgr::LevelShutdownClient()
  1304. {
  1305. if ( !m_bClientInitialized )
  1306. return;
  1307. Assert( m_bLevelInitialized );
  1308. for (int i = m_StaticProps.Count(); --i >= 0; )
  1309. {
  1310. m_StaticProps[i].CleanUpRenderHandle( );
  1311. modelrender->SetStaticLighting( m_StaticProps[i].GetModelInstance(), NULL );
  1312. }
  1313. #ifndef SWDS
  1314. // Make sure static prop lightcache is reset
  1315. ClearStaticLightingCache();
  1316. #endif
  1317. m_bClientInitialized = false;
  1318. }
  1319. //-----------------------------------------------------------------------------
  1320. // Create physics representations of props
  1321. //-----------------------------------------------------------------------------
  1322. void CStaticPropMgr::CreateVPhysicsRepresentations( IPhysicsEnvironment *pPhysEnv, IVPhysicsKeyHandler *pDefaults, void *pGameData )
  1323. {
  1324. // Walk through the static props + make collideable thingies for them.
  1325. int nCount = m_StaticProps.Count();
  1326. for ( int i = nCount; --i >= 0; )
  1327. {
  1328. m_StaticProps[i].CreateVPhysics( pPhysEnv, pDefaults, pGameData );
  1329. }
  1330. }
  1331. //-----------------------------------------------------------------------------
  1332. // Handles to props
  1333. //-----------------------------------------------------------------------------
  1334. inline int CStaticPropMgr::HandleEntityToIndex( IHandleEntity *pHandleEntity ) const
  1335. {
  1336. Assert( IsStaticProp( pHandleEntity ) );
  1337. return pHandleEntity->GetRefEHandle().GetEntryIndex();
  1338. }
  1339. ICollideable *CStaticPropMgr::GetStaticProp( IHandleEntity *pHandleEntity )
  1340. {
  1341. if ( !IsStaticProp( pHandleEntity ) )
  1342. {
  1343. return NULL;
  1344. }
  1345. int nIndex = pHandleEntity ? pHandleEntity->GetRefEHandle().GetEntryIndex() : -1;
  1346. if ( nIndex < 0 || nIndex > m_StaticProps.Count() )
  1347. {
  1348. return NULL;
  1349. }
  1350. return &m_StaticProps[nIndex];
  1351. }
  1352. ICollideable *CStaticPropMgr::GetStaticPropByIndex( int propIndex )
  1353. {
  1354. if ( propIndex < m_StaticProps.Count() )
  1355. {
  1356. return &m_StaticProps[propIndex];
  1357. }
  1358. Assert(0);
  1359. return NULL;
  1360. }
  1361. //-----------------------------------------------------------------------------
  1362. // Get large amounts of handles to static props
  1363. //-----------------------------------------------------------------------------
  1364. void CStaticPropMgr::GetAllStaticProps( CUtlVector<ICollideable *> *pOutput )
  1365. {
  1366. if ( pOutput == NULL ) return;
  1367. int iPropVectorSize = m_StaticProps.Count();
  1368. int counter;
  1369. for ( counter = 0; counter != iPropVectorSize; ++counter )
  1370. {
  1371. pOutput->AddToTail( &m_StaticProps[counter] );
  1372. }
  1373. }
  1374. void CStaticPropMgr::GetAllStaticPropsInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector<ICollideable *> *pOutput )
  1375. {
  1376. if ( pOutput == NULL ) return;
  1377. int iPropVectorSize = m_StaticProps.Count();
  1378. int counter;
  1379. for ( counter = 0; counter != iPropVectorSize; ++counter )
  1380. {
  1381. CStaticProp *pProp = &m_StaticProps[counter];
  1382. Vector vPropMins, vPropMaxs;
  1383. pProp->WorldSpaceSurroundingBounds( &vPropMins, &vPropMaxs );
  1384. if( vPropMaxs.x < vMins.x ) continue;
  1385. if( vPropMaxs.y < vMins.y ) continue;
  1386. if( vPropMaxs.z < vMins.z ) continue;
  1387. if( vPropMins.x > vMaxs.x ) continue;
  1388. if( vPropMins.y > vMaxs.y ) continue;
  1389. if( vPropMins.z > vMaxs.z ) continue;
  1390. pOutput->AddToTail( pProp );
  1391. }
  1392. }
  1393. void CStaticPropMgr::GetAllStaticPropsInOBB( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, CUtlVector<ICollideable *> *pOutput )
  1394. {
  1395. if ( pOutput == NULL ) return;
  1396. int counter;
  1397. Vector vAABBMins, vAABBMaxs;
  1398. vAABBMins = ptOrigin;
  1399. vAABBMaxs = ptOrigin;
  1400. Vector ptAABBExtents[8];
  1401. Vector ptOBBExtents[8];
  1402. for( counter = 0; counter != 8; ++counter )
  1403. {
  1404. ptOBBExtents[counter] = ptOrigin;
  1405. if( counter & (1<<0) ) ptOBBExtents[counter] += vExtent1;
  1406. if( counter & (1<<1) ) ptOBBExtents[counter] += vExtent2;
  1407. if( counter & (1<<2) ) ptOBBExtents[counter] += vExtent3;
  1408. //expand AABB extents
  1409. if( ptOBBExtents[counter].x < vAABBMins.x ) vAABBMins.x = ptOBBExtents[counter].x;
  1410. if( ptOBBExtents[counter].x > vAABBMaxs.x ) vAABBMaxs.x = ptOBBExtents[counter].x;
  1411. if( ptOBBExtents[counter].y < vAABBMins.y ) vAABBMins.y = ptOBBExtents[counter].y;
  1412. if( ptOBBExtents[counter].y > vAABBMaxs.y ) vAABBMaxs.y = ptOBBExtents[counter].y;
  1413. if( ptOBBExtents[counter].z < vAABBMins.z ) vAABBMins.z = ptOBBExtents[counter].z;
  1414. if( ptOBBExtents[counter].z > vAABBMaxs.z ) vAABBMaxs.z = ptOBBExtents[counter].z;
  1415. }
  1416. //generate planes for the obb so we can use halfspace elimination
  1417. Vector vOBBPlaneNormals[6];
  1418. float fOBBPlaneDists[6];
  1419. vOBBPlaneNormals[0] = vExtent1;
  1420. vOBBPlaneNormals[0].NormalizeInPlace();
  1421. fOBBPlaneDists[0] = vOBBPlaneNormals[0].Dot( ptOrigin + vExtent1 );
  1422. vOBBPlaneNormals[1] = -vOBBPlaneNormals[0];
  1423. fOBBPlaneDists[1] = vOBBPlaneNormals[1].Dot( ptOrigin );
  1424. vOBBPlaneNormals[2] = vExtent2;
  1425. vOBBPlaneNormals[2].NormalizeInPlace();
  1426. fOBBPlaneDists[2] = vOBBPlaneNormals[2].Dot( ptOrigin + vExtent2 );
  1427. vOBBPlaneNormals[3] = -vOBBPlaneNormals[2];
  1428. fOBBPlaneDists[3] = vOBBPlaneNormals[3].Dot( ptOrigin );
  1429. vOBBPlaneNormals[4] = vExtent3;
  1430. vOBBPlaneNormals[4].NormalizeInPlace();
  1431. fOBBPlaneDists[4] = vOBBPlaneNormals[4].Dot( ptOrigin + vExtent3 );
  1432. vOBBPlaneNormals[5] = -vOBBPlaneNormals[4];
  1433. fOBBPlaneDists[5] = vOBBPlaneNormals[5].Dot( ptOrigin );
  1434. int iPropVectorSize = m_StaticProps.Count();
  1435. for ( counter = 0; counter != iPropVectorSize; ++counter )
  1436. {
  1437. CStaticProp *pProp = &m_StaticProps[counter];
  1438. Vector vPropMins, vPropMaxs;
  1439. pProp->WorldSpaceSurroundingBounds( &vPropMins, &vPropMaxs );
  1440. if( vPropMaxs.x < vAABBMins.x ) continue;
  1441. if( vPropMaxs.y < vAABBMins.y ) continue;
  1442. if( vPropMaxs.z < vAABBMins.z ) continue;
  1443. if( vPropMins.x > vAABBMaxs.x ) continue;
  1444. if( vPropMins.y > vAABBMaxs.y ) continue;
  1445. if( vPropMins.z > vAABBMaxs.z ) continue;
  1446. //static prop AABB and desired AABB intersect, do OBB tests
  1447. Vector vPropOBBMins = pProp->OBBMins();
  1448. Vector vPropOBBMaxs = pProp->OBBMaxs();
  1449. Vector ptPropExtents[8];
  1450. matrix3x4_t matPropWorld;
  1451. AngleMatrix( pProp->GetCollisionAngles(), pProp->GetCollisionOrigin(), matPropWorld );
  1452. int counter2, counter3;
  1453. //generate prop extents, TODO: update these to handle props with OBB's since it should be nearly trivial
  1454. for( counter2 = 0; counter2 != 8; ++counter2 )
  1455. {
  1456. /*ptPropExtents[counter2].x = (counter2 & (1<<0))?(vPropMaxs.x):(vPropMins.x);
  1457. ptPropExtents[counter2].y = (counter2 & (1<<1))?(vPropMaxs.y):(vPropMins.y);
  1458. ptPropExtents[counter2].z = (counter2 & (1<<2))?(vPropMaxs.z):(vPropMins.z);*/
  1459. Vector ptTemp;
  1460. ptTemp.x = (counter2 & (1<<0))?(vPropOBBMaxs.x):(vPropOBBMins.x);
  1461. ptTemp.y = (counter2 & (1<<1))?(vPropOBBMaxs.y):(vPropOBBMins.y);
  1462. ptTemp.z = (counter2 & (1<<2))?(vPropOBBMaxs.z):(vPropOBBMins.z);
  1463. VectorTransform( ptTemp, matPropWorld, ptPropExtents[counter2] );
  1464. }
  1465. for( counter2 = 0; counter2 != 6; ++counter2 ) //loop over OBB planes
  1466. {
  1467. for( counter3 = 0; counter3 != 8; ++counter3 ) //loop over prop extents
  1468. {
  1469. if( (ptPropExtents[counter3].Dot( vOBBPlaneNormals[counter2] ) - fOBBPlaneDists[counter2]) < 0.0f )
  1470. {
  1471. //an extent of the prop is within the OBB halfspace, this halfspace does not eliminate our prop, move to the next halfspace
  1472. break;
  1473. }
  1474. }
  1475. if( counter3 == 8 ) break; //if all 8 extents lie outside the halfspace, then the prop is not in the OBB
  1476. }
  1477. if( counter2 == 6 )
  1478. {
  1479. //if all 6 planes failed to eliminate the extents, the OBB and prop intersect
  1480. //FIXME: Halfspace elimination will never remove props that do intersect, but leaves some false positives in some cases.
  1481. pOutput->AddToTail( pProp );
  1482. }
  1483. }
  1484. }
  1485. //-----------------------------------------------------------------------------
  1486. // Are we a static prop?
  1487. //-----------------------------------------------------------------------------
  1488. bool CStaticPropMgr::IsStaticProp( IHandleEntity *pHandleEntity ) const
  1489. {
  1490. return (!pHandleEntity) || ( (pHandleEntity->GetRefEHandle().GetSerialNumber() == (STATICPROP_EHANDLE_MASK >> NUM_ENT_ENTRY_BITS) ) != 0 );
  1491. }
  1492. bool CStaticPropMgr::IsStaticProp( CBaseHandle handle ) const
  1493. {
  1494. return (handle.GetSerialNumber() == (STATICPROP_EHANDLE_MASK >> NUM_ENT_ENTRY_BITS));
  1495. }
  1496. int CStaticPropMgr::GetStaticPropIndex( IHandleEntity *pHandleEntity ) const
  1497. {
  1498. return HandleEntityToIndex( pHandleEntity );
  1499. }
  1500. bool CStaticPropMgr::PropHasBakedLightingDisabled( IHandleEntity *pHandleEntity ) const
  1501. {
  1502. // Strip off the bits
  1503. int nIndex = HandleEntityToIndex( pHandleEntity );
  1504. // Get the prop
  1505. const CStaticProp &prop = m_StaticProps[nIndex];
  1506. return ( (prop.Flags() & STATIC_PROP_NO_PER_VERTEX_LIGHTING ) != 0 );
  1507. }
  1508. //-----------------------------------------------------------------------------
  1509. // Compute static lighting
  1510. //-----------------------------------------------------------------------------
  1511. void CStaticPropMgr::PrecacheLighting()
  1512. {
  1513. COM_TimestampedLog( "CStaticPropMgr::PrecacheLighting - start");
  1514. int numVerts = 0;
  1515. if ( IsX360() )
  1516. {
  1517. if ( g_bLoadedMapHasBakedPropLighting && g_pMaterialSystemHardwareConfig->SupportsStreamOffset() )
  1518. {
  1519. // total the static prop verts
  1520. int i = m_StaticProps.Count();
  1521. while ( --i >= 0 )
  1522. {
  1523. if ( PropHasBakedLightingDisabled( m_StaticProps[i].GetEntityHandle() ) )
  1524. {
  1525. continue;
  1526. }
  1527. studiohwdata_t *pStudioHWData = g_pMDLCache->GetHardwareData( ( (model_t*)m_StaticProps[i].GetModel() )->studio );
  1528. for ( int lodID = pStudioHWData->m_RootLOD; lodID < pStudioHWData->m_NumLODs; lodID++ )
  1529. {
  1530. studioloddata_t *pLOD = &pStudioHWData->m_pLODs[lodID];
  1531. for ( int meshID = 0; meshID < pStudioHWData->m_NumStudioMeshes; meshID++ )
  1532. {
  1533. studiomeshdata_t *pMesh = &pLOD->m_pMeshData[meshID];
  1534. for ( int groupID = 0; groupID < pMesh->m_NumGroup; groupID++ )
  1535. {
  1536. numVerts += pMesh->m_pMeshGroup[groupID].m_NumVertices;
  1537. }
  1538. }
  1539. }
  1540. }
  1541. }
  1542. modelrender->SetupColorMeshes( numVerts );
  1543. }
  1544. int i = m_StaticProps.Count();
  1545. while ( --i >= 0 )
  1546. {
  1547. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  1548. if ( !m_StaticProps[i].ShouldDraw() )
  1549. continue;
  1550. m_StaticProps[i].PrecacheLighting();
  1551. }
  1552. COM_TimestampedLog( "CStaticPropMgr::PrecacheLighting - end");
  1553. }
  1554. void CStaticPropMgr::RecomputeStaticLighting( )
  1555. {
  1556. int i = m_StaticProps.Count();
  1557. while ( --i >= 0 )
  1558. {
  1559. if ( !m_StaticProps[i].ShouldDraw() )
  1560. continue;
  1561. m_StaticProps[i].RecomputeStaticLighting();
  1562. }
  1563. }
  1564. //-----------------------------------------------------------------------------
  1565. // Is the prop in the PVS?
  1566. //-----------------------------------------------------------------------------
  1567. bool CStaticPropMgr::IsPropInPVS( IHandleEntity *pHandleEntity, const byte *pVis ) const
  1568. {
  1569. // Strip off the bits
  1570. int nIndex = HandleEntityToIndex( pHandleEntity );
  1571. // Get the prop
  1572. const CStaticProp &prop = m_StaticProps[nIndex];
  1573. int i;
  1574. int end = prop.FirstLeaf() + prop.LeafCount();
  1575. for( i = prop.FirstLeaf(); i < end; i++ )
  1576. {
  1577. Assert( i >= 0 && i < m_StaticPropLeaves.Count() );
  1578. int clusterID = CM_LeafCluster( m_StaticPropLeaves[i].m_Leaf );
  1579. if( pVis[ clusterID >> 3 ] & ( 1 << ( clusterID & 7 ) ) )
  1580. {
  1581. return true;
  1582. }
  1583. }
  1584. return false;
  1585. }
  1586. void CStaticPropMgr::DrawStaticProps_Slow( IClientRenderable **pProps, int count, bool bShadowDepth, bool drawVCollideWireframe )
  1587. {
  1588. // slow mode
  1589. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  1590. int flags = STUDIO_RENDER;
  1591. if (bShadowDepth)
  1592. flags |= STUDIO_SHADOWDEPTHTEXTURE;
  1593. if ( drawVCollideWireframe )
  1594. flags |= STUDIO_WIREFRAME_VCOLLIDE;
  1595. for ( int i = 0; i < count; i++ )
  1596. {
  1597. CStaticProp *pProp = (CStaticProp *)(pProps[i]);
  1598. pProp->DrawModelSlow( flags );
  1599. }
  1600. }
  1601. void CStaticPropMgr::DrawStaticProps_Fast( IClientRenderable **pProps, int count, bool bShadowDepth )
  1602. {
  1603. #ifndef SWDS
  1604. float color[3];
  1605. color[0] = color[1] = color[2] = 1.0f;
  1606. g_pStudioRender->SetColorModulation(color);
  1607. g_pStudioRender->SetAlphaModulation(1.0f);
  1608. g_pStudioRender->SetViewState( CurrentViewOrigin(), CurrentViewRight(), CurrentViewUp(), CurrentViewForward() );
  1609. CMatRenderContextPtr pRenderContext( materials );
  1610. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1611. pRenderContext->PushMatrix();
  1612. pRenderContext->LoadIdentity();
  1613. ModelRenderInfo_t sInfo;
  1614. sInfo.flags = STUDIO_RENDER | STUDIO_STATIC_LIGHTING;
  1615. if (bShadowDepth)
  1616. sInfo.flags |= STUDIO_SHADOWDEPTHTEXTURE;
  1617. sInfo.entity_index = -1;
  1618. sInfo.body = 0;
  1619. sInfo.hitboxset = 0;
  1620. sInfo.pLightingOffset = NULL;
  1621. for ( int i = 0; i < count; i++ )
  1622. {
  1623. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  1624. CStaticProp *pProp = (CStaticProp *)(pProps[i]);
  1625. if ( !pProp->m_pModel )
  1626. continue;
  1627. sInfo.instance = pProp->m_ModelInstance;
  1628. sInfo.pModel = pProp->m_pModel;
  1629. sInfo.origin = pProp->m_Origin;
  1630. sInfo.angles = pProp->m_Angles;
  1631. sInfo.skin = pProp->m_Skin;
  1632. sInfo.pLightingOrigin = &pProp->m_LightingOrigin;
  1633. sInfo.pModelToWorld = &pProp->m_ModelToWorld;
  1634. sInfo.pRenderable = pProps[i];
  1635. modelrender->DrawModelExStaticProp( sInfo );
  1636. }
  1637. // Restore the matrices if we're skinning
  1638. pRenderContext->MatrixMode( MATERIAL_MODEL );
  1639. pRenderContext->PopMatrix();
  1640. #endif
  1641. }
  1642. // NOTE: This is a work in progress for a new static prop (eventually new model) rendering pipeline
  1643. void CStaticPropMgr::DrawStaticProps_FastPipeline( IClientRenderable **pProps, int count, bool bShadowDepth )
  1644. {
  1645. const int MAX_OBJECTS = 2048;
  1646. StaticPropRenderInfo_t propList[MAX_OBJECTS];
  1647. int listCount = 0;
  1648. if ( count > MAX_OBJECTS )
  1649. {
  1650. DrawStaticProps_FastPipeline( pProps + MAX_OBJECTS, count - MAX_OBJECTS, bShadowDepth );
  1651. }
  1652. for ( int i = 0; i < count; i++ )
  1653. {
  1654. CStaticProp *pProp = (CStaticProp *)(pProps[i]);
  1655. if ( !pProp->m_pModel )
  1656. continue;
  1657. propList[listCount].pModelToWorld = &pProp->m_ModelToWorld;
  1658. propList[listCount].pModel = pProp->m_pModel;
  1659. propList[listCount].instance = pProp->m_ModelInstance;
  1660. propList[listCount].skin = pProp->m_Skin;
  1661. propList[listCount].pRenderable = pProp;
  1662. propList[listCount].pLightingOrigin = &pProp->m_LightingOrigin;
  1663. listCount++;
  1664. }
  1665. modelrender->DrawStaticPropArrayFast( propList, listCount, bShadowDepth );
  1666. }
  1667. // NOTE: Set this to zero to revert to the previous static prop lighting behavior
  1668. ConVar pipeline_static_props("pipeline_static_props", "1");
  1669. void CStaticPropMgr::DrawStaticProps( IClientRenderable **pProps, int count, bool bShadowDepth, bool drawVCollideWireframe )
  1670. {
  1671. VPROF_BUDGET( "CStaticPropMgr::DrawStaticProps", VPROF_BUDGETGROUP_STATICPROP_RENDERING );
  1672. if ( !r_drawstaticprops.GetBool() )
  1673. return;
  1674. if ( IsUsingStaticPropDebugModes() || drawVCollideWireframe )
  1675. {
  1676. DrawStaticProps_Slow( pProps, count, bShadowDepth, drawVCollideWireframe );
  1677. }
  1678. else
  1679. {
  1680. // the fast pipeline is only supported on dx8+
  1681. if ( pipeline_static_props.GetBool() &&
  1682. g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 &&
  1683. g_pMaterialSystemHardwareConfig->SupportsColorOnSecondStream() &&
  1684. g_pMaterialSystemHardwareConfig->SupportsStaticPlusDynamicLighting() )
  1685. {
  1686. DrawStaticProps_FastPipeline( pProps, count, bShadowDepth );
  1687. }
  1688. else
  1689. {
  1690. DrawStaticProps_Fast( pProps, count, bShadowDepth );
  1691. }
  1692. }
  1693. }
  1694. //-----------------------------------------------------------------------------
  1695. // Returns the lightcache handle
  1696. //-----------------------------------------------------------------------------
  1697. LightCacheHandle_t CStaticPropMgr::GetLightCacheHandleForStaticProp( IHandleEntity *pHandleEntity )
  1698. {
  1699. int nIndex = HandleEntityToIndex(pHandleEntity);
  1700. return modelrender->GetStaticLighting( m_StaticProps[ nIndex ].GetModelInstance() );
  1701. }
  1702. //-----------------------------------------------------------------------------
  1703. // Computes fade from screen-space fading
  1704. //-----------------------------------------------------------------------------
  1705. unsigned char CStaticPropMgr::ComputeScreenFade( CStaticProp &prop, float flMinSize, float flMaxSize, float flFalloffFactor )
  1706. {
  1707. CMatRenderContextPtr pRenderContext( materials );
  1708. float flPixelWidth = pRenderContext->ComputePixelWidthOfSphere( prop.GetRenderOrigin(), prop.Radius() );
  1709. unsigned char alpha = 0;
  1710. if ( flPixelWidth > flMinSize )
  1711. {
  1712. if ( (flMaxSize >= 0) && (flPixelWidth < flMaxSize) )
  1713. {
  1714. int nAlpha = flFalloffFactor * (flPixelWidth - flMinSize);
  1715. alpha = clamp( nAlpha, 0, 255 );
  1716. }
  1717. else
  1718. {
  1719. alpha = 255;
  1720. }
  1721. }
  1722. return alpha;
  1723. }
  1724. //-----------------------------------------------------------------------------
  1725. // Changes the render group based on alpha
  1726. //-----------------------------------------------------------------------------
  1727. void CStaticPropMgr::ChangeRenderGroup( CStaticProp &prop )
  1728. {
  1729. #ifndef SWDS
  1730. static RenderGroup_t opaqueRenderGroup = ( g_bClientLeafSystemV1 ) ? RENDER_GROUP_OPAQUE_ENTITY : RENDER_GROUP_OPAQUE_STATIC;
  1731. ClientRenderHandle_t renderHandle = prop.GetRenderHandle();
  1732. Assert( renderHandle != INVALID_CLIENT_RENDER_HANDLE );
  1733. if ( prop.GetFxBlend() == 0 )
  1734. {
  1735. clientleafsystem->ChangeRenderableRenderGroup( renderHandle, opaqueRenderGroup );
  1736. }
  1737. else if ( prop.GetFxBlend() == 255 )
  1738. {
  1739. RenderGroup_t nRenderGroup = prop.IsTransparent() ? RENDER_GROUP_TRANSLUCENT_ENTITY : opaqueRenderGroup;
  1740. clientleafsystem->ChangeRenderableRenderGroup( renderHandle, nRenderGroup );
  1741. }
  1742. else
  1743. {
  1744. clientleafsystem->ChangeRenderableRenderGroup( renderHandle, RENDER_GROUP_TRANSLUCENT_ENTITY );
  1745. }
  1746. #endif
  1747. }
  1748. //-----------------------------------------------------------------------------
  1749. // System to update prop opacity
  1750. //-----------------------------------------------------------------------------
  1751. void CStaticPropMgr::ComputePropOpacity( CStaticProp &prop )
  1752. {
  1753. #ifndef SWDS
  1754. if (modelinfoclient->ModelHasMaterialProxy( prop.GetModel() ))
  1755. {
  1756. modelinfoclient->RecomputeTranslucency( prop.GetModel(), prop.GetSkin(), prop.GetBody(), prop.GetClientRenderable(), (float)(prop.GetFxBlend()) / 255.0f );
  1757. }
  1758. #endif
  1759. #ifdef LINUX
  1760. bool bVisionOverride = false;
  1761. #else
  1762. static ConVarRef localplayer_visionflags( "localplayer_visionflags" );
  1763. bool bVisionOverride = ( localplayer_visionflags.IsValid() && ( localplayer_visionflags.GetInt() & ( 0x01 ) ) ); // Pyro-vision Goggles
  1764. if ( !g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_0() )
  1765. {
  1766. bVisionOverride = false;
  1767. }
  1768. #endif
  1769. // If we're taking devshots, don't fade anything
  1770. if ( g_MakingDevShots || m_flLastViewFactor < 0 || bVisionOverride )
  1771. {
  1772. prop.SetAlpha( 255 );
  1773. ChangeRenderGroup( prop );
  1774. return;
  1775. }
  1776. if ( (prop.Flags() & STATIC_PROP_FLAG_FADES) != 0 )
  1777. {
  1778. // Distance-based fading.
  1779. // Step over the list of all things that want to be faded out and recompute alpha
  1780. // Not sure if this is a fast enough way of doing it
  1781. // but it's easy for now; we'll have to test later how large this list gets.
  1782. // If it's <100 or so, we should be fine
  1783. Assert( prop.FadeIndex() != INVALID_FADE_INDEX );
  1784. Vector v;
  1785. StaticPropFade_t& fade = m_StaticPropFade[prop.FadeIndex()];
  1786. unsigned char alpha;
  1787. // Calculate distance (badly)
  1788. if ( (prop.Flags() & STATIC_PROP_SCREEN_SPACE_FADE) == 0 )
  1789. {
  1790. VectorSubtract( prop.GetRenderOrigin(), m_vecLastViewOrigin, v );
  1791. VectorScale( v, m_flLastViewFactor, v );
  1792. alpha = 0;
  1793. float sqDist = v.LengthSqr();
  1794. if ( sqDist < fade.m_MaxDistSq )
  1795. {
  1796. if ( (fade.m_MinDistSq >= 0) && (sqDist > fade.m_MinDistSq) )
  1797. {
  1798. int nAlpha = fade.m_FalloffFactor * (fade.m_MaxDistSq - sqDist);
  1799. alpha = clamp( nAlpha, 0, 255 );
  1800. }
  1801. else
  1802. {
  1803. alpha = 255;
  1804. }
  1805. }
  1806. }
  1807. else
  1808. {
  1809. alpha = ComputeScreenFade( prop, fade.m_MinScreenWidth, fade.m_MaxScreenWidth, fade.m_FalloffFactor );
  1810. }
  1811. prop.SetAlpha( alpha );
  1812. ChangeRenderGroup( prop );
  1813. }
  1814. else
  1815. {
  1816. prop.SetAlpha( 255 );
  1817. ChangeRenderGroup( prop );
  1818. }
  1819. #ifndef SWDS
  1820. if ( !IsXbox() )
  1821. {
  1822. // Fade all props, if we have a default level setting
  1823. // But only change the fade if it's more translucent than any other fades we might have
  1824. unsigned char alpha = modelinfoclient->ComputeLevelScreenFade( prop.GetRenderOrigin(), prop.Radius(), prop.ForcedFadeScale() );
  1825. unsigned char nViewAlpha = modelinfoclient->ComputeViewScreenFade( prop.GetRenderOrigin(), prop.Radius(), prop.ForcedFadeScale() );
  1826. if ( nViewAlpha < alpha )
  1827. {
  1828. alpha = nViewAlpha;
  1829. }
  1830. if ( alpha < prop.GetFxBlend() )
  1831. {
  1832. prop.SetAlpha( alpha );
  1833. ChangeRenderGroup( prop );
  1834. }
  1835. }
  1836. #endif
  1837. }
  1838. //-----------------------------------------------------------------------------
  1839. // System to update prop opacity
  1840. //-----------------------------------------------------------------------------
  1841. void CStaticPropMgr::ComputePropOpacity( const Vector &viewOrigin, float factor )
  1842. {
  1843. // Cache these off for the call to ComputeFX blend which is compute later
  1844. m_vecLastViewOrigin = viewOrigin;
  1845. m_flLastViewFactor = factor;
  1846. }
  1847. //-----------------------------------------------------------------------------
  1848. // Purpose: Trace a ray against the specified static Prop. Returns point of intersection in trace_t
  1849. //-----------------------------------------------------------------------------
  1850. void CStaticPropMgr::TraceRayAgainstStaticProp( const Ray_t& ray, int staticPropIndex, trace_t& tr )
  1851. {
  1852. #ifndef SWDS
  1853. // Get the prop
  1854. CStaticProp& prop = m_StaticProps[staticPropIndex];
  1855. if (prop.GetSolid() != SOLID_NONE)
  1856. {
  1857. // FIXME: Better bloat?
  1858. // Bloat a little bit so we get the intersection
  1859. Ray_t temp = ray;
  1860. temp.m_Delta *= 1.1f;
  1861. g_pEngineTraceClient->ClipRayToEntity( temp, MASK_ALL, &prop, &tr );
  1862. }
  1863. else
  1864. {
  1865. // no collision
  1866. tr.fraction = 1.0f;
  1867. }
  1868. #endif
  1869. }
  1870. //-----------------------------------------------------------------------------
  1871. // Adds decals to static props, returns point of decal in trace_t
  1872. //-----------------------------------------------------------------------------
  1873. void CStaticPropMgr::AddDecalToStaticProp( Vector const& rayStart, Vector const& rayEnd,
  1874. int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr )
  1875. {
  1876. Color tempColor( 255, 255, 255 );
  1877. AddColorDecalToStaticProp( rayStart, rayEnd, staticPropIndex, decalIndex, doTrace, tr, false, tempColor );
  1878. }
  1879. //-----------------------------------------------------------------------------
  1880. void CStaticPropMgr::AddColorDecalToStaticProp( Vector const& rayStart, Vector const& rayEnd,
  1881. int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr, bool bUseColor, Color cColor )
  1882. {
  1883. #ifndef SWDS
  1884. // Invalid static prop? Blow it off!
  1885. if (staticPropIndex >= m_StaticProps.Size())
  1886. {
  1887. memset( &tr, 0, sizeof(trace_t) );
  1888. tr.fraction = 1.0f;
  1889. return;
  1890. }
  1891. Ray_t ray;
  1892. ray.Init( rayStart, rayEnd );
  1893. if (doTrace)
  1894. {
  1895. // Trace the ray against the prop
  1896. TraceRayAgainstStaticProp( ray, staticPropIndex, tr );
  1897. if (tr.fraction == 1.0f)
  1898. return;
  1899. }
  1900. if ( !r_drawmodeldecals.GetInt() )
  1901. return;
  1902. // Get the prop
  1903. CStaticProp& prop = m_StaticProps[staticPropIndex];
  1904. // Found the point, now lets apply the decals
  1905. Assert( prop.GetModelInstance() != MODEL_INSTANCE_INVALID );
  1906. // Choose a new ray along which to project the decal based on
  1907. // surface normal. This prevents decal skewing
  1908. bool noPokethru = false;
  1909. if (doTrace && (prop.GetSolid() == SOLID_VPHYSICS) && !tr.startsolid && !tr.allsolid)
  1910. {
  1911. Vector temp;
  1912. VectorSubtract( tr.endpos, tr.plane.normal, temp );
  1913. ray.Init( tr.endpos, temp );
  1914. noPokethru = true;
  1915. }
  1916. // FIXME: Pass in decal up?
  1917. // FIXME: What to do about the body parameter?
  1918. Vector up(0, 0, 1);
  1919. if ( bUseColor )
  1920. {
  1921. modelrender->AddColoredDecal( prop.GetModelInstance(), ray, up, decalIndex, 0, cColor, noPokethru );
  1922. }
  1923. else
  1924. {
  1925. modelrender->AddDecal( prop.GetModelInstance(), ray, up, decalIndex, 0, noPokethru );
  1926. }
  1927. #endif
  1928. }
  1929. //-----------------------------------------------------------------------------
  1930. // Adds/removes shadows from static props
  1931. //-----------------------------------------------------------------------------
  1932. void CStaticPropMgr::AddShadowToStaticProp( unsigned short shadowHandle, IClientRenderable* pRenderable )
  1933. {
  1934. #ifndef SWDS
  1935. Assert( dynamic_cast<CStaticProp*>(pRenderable) != 0 );
  1936. CStaticProp* pProp = static_cast<CStaticProp*>(pRenderable);
  1937. g_pShadowMgr->AddShadowToModel( shadowHandle, pProp->GetModelInstance() );
  1938. #endif
  1939. }
  1940. void CStaticPropMgr::RemoveAllShadowsFromStaticProp( IClientRenderable* pRenderable )
  1941. {
  1942. #ifndef SWDS
  1943. Assert( dynamic_cast<CStaticProp*>(pRenderable) != 0 );
  1944. CStaticProp* pProp = static_cast<CStaticProp*>(pRenderable);
  1945. if (pProp->GetModelInstance() != MODEL_INSTANCE_INVALID)
  1946. {
  1947. g_pShadowMgr->RemoveAllShadowsFromModel( pProp->GetModelInstance() );
  1948. }
  1949. #endif
  1950. }
  1951. //-----------------------------------------------------------------------------
  1952. // Gets the lighting + material color of a static prop
  1953. //-----------------------------------------------------------------------------
  1954. void CStaticPropMgr::GetStaticPropMaterialColorAndLighting( trace_t* pTrace,
  1955. int staticPropIndex, Vector& lighting, Vector& matColor )
  1956. {
  1957. #ifndef SWDS
  1958. // Invalid static prop? Blow it off!
  1959. if (staticPropIndex >= m_StaticProps.Size())
  1960. {
  1961. lighting.Init( 0, 0, 0 );
  1962. matColor.Init( 1, 1, 1 );
  1963. return;
  1964. }
  1965. // Get the prop
  1966. CStaticProp& prop = m_StaticProps[staticPropIndex];
  1967. // Ask the model info about what we need to know
  1968. modelinfoclient->GetModelMaterialColorAndLighting( (model_t*)prop.GetModel(),
  1969. prop.GetRenderOrigin(), prop.GetRenderAngles(), pTrace, lighting, matColor );
  1970. #endif
  1971. }
  1972. //-----------------------------------------------------------------------------
  1973. // Little debugger tool to report which prop we're looking at
  1974. //-----------------------------------------------------------------------------
  1975. void Cmd_PropCrosshair_f (void)
  1976. {
  1977. Vector endPoint;
  1978. VectorMA( MainViewOrigin(), COORD_EXTENT * 1.74f, MainViewForward(), endPoint );
  1979. Ray_t ray;
  1980. ray.Init( MainViewOrigin(), endPoint );
  1981. trace_t tr;
  1982. CTraceFilterWorldAndPropsOnly traceFilter;
  1983. g_pEngineTraceServer->TraceRay( ray, MASK_ALL, &traceFilter, &tr );
  1984. if ( tr.hitbox > 0 )
  1985. Msg( "hit prop %d\n", tr.hitbox - 1 );
  1986. else
  1987. Msg( "didn't hit a prop\n" );
  1988. }
  1989. static ConCommand prop_crosshair( "prop_crosshair", Cmd_PropCrosshair_f, "Shows name for prop looking at", FCVAR_CHEAT );