From 3bf9df6b2785fa6d951086978a3e66f49427166a Mon Sep 17 00:00:00 2001 From: FluorescentCIAAfricanAmerican <0934gj3049fk@protonmail.com> Date: Wed, 22 Apr 2020 12:56:21 -0400 Subject: 1 --- engine/staticpropmgr.cpp | 2359 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2359 insertions(+) create mode 100644 engine/staticpropmgr.cpp (limited to 'engine/staticpropmgr.cpp') diff --git a/engine/staticpropmgr.cpp b/engine/staticpropmgr.cpp new file mode 100644 index 0000000..d0af146 --- /dev/null +++ b/engine/staticpropmgr.cpp @@ -0,0 +1,2359 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $Revision: $ +// $NoKeywords: $ +// +// This file contains code to allow us to associate client data with bsp leaves. +// +//===========================================================================// + + +#include "staticpropmgr.h" +#include "convar.h" +#include "vcollide_parse.h" +#include "engine/ICollideable.h" +#include "iclientunknown.h" +#include "iclientrenderable.h" +#include "gamebspfile.h" +#include "engine/ivmodelrender.h" +#include "engine/IClientLeafSystem.h" +#include "ispatialpartitioninternal.h" +#include "utlbuffer.h" +#include "utlvector.h" +#include "filesystem.h" +#include "gl_model_private.h" +#include "gl_matsysiface.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "materialsystem/ivballoctracker.h" +#include "materialsystem/imesh.h" +#include "lightcache.h" +#include "tier0/vprof.h" +#include "render.h" +#include "cmodel_engine.h" +#include "datacache/imdlcache.h" +#include "ModelInfo.h" +#include "cdll_engine_int.h" +#include "tier0/dbg.h" +#include "debugoverlay.h" +#include "draw.h" +#include "client.h" +#include "server.h" +#include "l_studio.h" +#include "tier0/icommandline.h" +#include "sys_dll.h" +#include "generichash.h" +#include "tier2/renderutils.h" +#include "ipooledvballocator.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +//----------------------------------------------------------------------------- +// Convars! +//----------------------------------------------------------------------------- +static ConVar r_DrawSpecificStaticProp( "r_DrawSpecificStaticProp", "-1" ); +static ConVar r_drawstaticprops( "r_drawstaticprops", "1", FCVAR_CHEAT, "0=Off, 1=Normal, 2=Wireframe" ); +static ConVar r_colorstaticprops( "r_colorstaticprops", "0", FCVAR_CHEAT ); +ConVar r_staticpropinfo( "r_staticpropinfo", "0" ); +ConVar r_drawmodeldecals( "r_drawmodeldecals", "1" ); +extern ConVar mat_fullbright; +static bool g_MakingDevShots = false; +//----------------------------------------------------------------------------- +// Index into the fade list +//----------------------------------------------------------------------------- +enum +{ + INVALID_FADE_INDEX = (unsigned short)~0 +}; + + +//----------------------------------------------------------------------------- +// All static props have these bits set (to differentiate them from edict indices) +//----------------------------------------------------------------------------- +enum +{ + // This bit will be set in GetRefEHandle for all static props + STATICPROP_EHANDLE_MASK = 0x40000000 +}; + + +//----------------------------------------------------------------------------- +// A default physics property for non-vphysics static props +//----------------------------------------------------------------------------- +static const objectparams_t g_PhysDefaultObjectParams = +{ + NULL, + 1.0, //mass + 1.0, // inertia + 0.1f, // damping + 0.1f, // rotdamping + 0.05f, // rotIntertiaLimit + "DEFAULT", + NULL,// game data + 0.f, // volume (leave 0 if you don't have one or call physcollision->CollideVolume() to compute it) + 1.0f, // drag coefficient + true,// enable collisions? +}; + +// return true if the renderer should use the slow path that supports the various debug modes +inline bool IsUsingStaticPropDebugModes() +{ + if ( r_drawstaticprops.GetInt() != 1 || + r_DrawSpecificStaticProp.GetInt() >= 0 || + r_colorstaticprops.GetBool() || + r_staticpropinfo.GetInt() || + mat_fullbright.GetInt() || + r_drawmodellightorigin.GetBool() || + r_drawmodelstatsoverlay.GetBool() ) + return true; + return false; +} + +//----------------------------------------------------------------------------- +// A static prop +//----------------------------------------------------------------------------- +class CStaticProp : public IClientUnknown, public IClientRenderable, public ICollideable +{ +public: + CStaticProp(); + ~CStaticProp(); + + // IHandleEntity overrides +public: + virtual void SetRefEHandle( const CBaseHandle &handle ); + virtual const CBaseHandle& GetRefEHandle() const; + + // IClientUnknown overrides. +public: + virtual IClientUnknown* GetIClientUnknown() { return this; } + virtual ICollideable* GetCollideable() { return this; } + virtual IClientNetworkable* GetClientNetworkable() { return NULL; } + virtual IClientRenderable* GetClientRenderable() { return this; } + virtual IClientEntity* GetIClientEntity() { return NULL; } + virtual C_BaseEntity* GetBaseEntity() { return NULL; } + virtual IClientThinkable* GetClientThinkable() { return NULL; } + +public: + // These methods return a box defined in the space of the entity + virtual const Vector& OBBMinsPreScaled() const { return OBBMins(); } + virtual const Vector& OBBMaxsPreScaled() const { return OBBMaxs(); } + virtual const Vector& OBBMins() const; + virtual const Vector& OBBMaxs() const; + + // custom collision test + virtual bool TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ); + + // Perform hitbox test, returns true *if hitboxes were tested at all*!! + virtual bool TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ); + + // Returns the BRUSH model index if this is a brush model. Otherwise, returns -1. + virtual int GetCollisionModelIndex(); + + // Return the model, if it's a studio model. + virtual const model_t* GetCollisionModel(); + + // Get angles and origin. + virtual const Vector& GetCollisionOrigin() const; + virtual const QAngle& GetCollisionAngles() const; + virtual const matrix3x4_t& CollisionToWorldTransform() const; + + // Return a SOLID_ define. + virtual SolidType_t GetSolid() const; + virtual int GetSolidFlags() const; + + // Gets at the entity handle associated with the collideable + virtual IHandleEntity *GetEntityHandle() { return this; } + + virtual int GetCollisionGroup() const { return COLLISION_GROUP_NONE; } + + virtual void WorldSpaceTriggerBounds( Vector* pVecWorldMins, Vector *pVecWorldMaxs ) const; + virtual void WorldSpaceSurroundingBounds( Vector* pVecWorldMins, Vector *pVecWorldMaxs ); + virtual bool ShouldTouchTrigger( int triggerSolidFlags ) const { return false; } + virtual const matrix3x4_t *GetRootParentToWorldTransform() const { return NULL; } + + // IClientRenderable overrides. +public: + virtual int GetBody() { return 0; } + virtual int GetSkin() { return 0; } + virtual const Vector& GetRenderOrigin( ); + virtual const QAngle& GetRenderAngles( ); + virtual bool ShouldDraw(); + virtual bool IsTransparent( void ); + virtual bool IsTwoPass( void ); + virtual void OnThreadedDrawSetup() {} + virtual const model_t* GetModel( ) const; + virtual int DrawModel( int flags ); + virtual void ComputeFxBlend( ); + virtual int GetFxBlend( ); + virtual void GetColorModulation( float* color ); + virtual bool LODTest() { return true; } // NOTE: UNUSED + virtual bool SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime ); + virtual void SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ); + virtual bool UsesFlexDelayedWeights() { return false; } + virtual void DoAnimationEvents( void ); + virtual IPVSNotify* GetPVSNotifyInterface(); + virtual void GetRenderBounds( Vector& mins, Vector& maxs ); + virtual void GetRenderBoundsWorldspace( Vector& mins, Vector& maxs ); + virtual bool ShouldCacheRenderInfo(); + virtual bool ShouldReceiveProjectedTextures( int flags ); + virtual bool GetShadowCastDistance( float *pDist, ShadowType_t shadowType ) const { return false; } + virtual bool GetShadowCastDirection( Vector *pDirection, ShadowType_t shadowType ) const { return false; } + virtual bool UsesPowerOfTwoFrameBufferTexture(); + virtual bool UsesFullFrameBufferTexture(); + virtual ClientShadowHandle_t GetShadowHandle() const { return CLIENTSHADOW_INVALID_HANDLE; } + virtual ClientRenderHandle_t& RenderHandle(); + virtual void RecordToolMessage() {} + + // These normally call through to GetRenderAngles/GetRenderBounds, but some entities custom implement them. + virtual void GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType ) + { + GetRenderBounds( mins, maxs ); + } + + // Other methods related to shadow rendering + virtual bool IsShadowDirty( ) { return false; } + virtual void MarkShadowDirty( bool bDirty ) {} + + // Iteration over shadow hierarchy + virtual IClientRenderable *GetShadowParent() { return NULL; } + virtual IClientRenderable *FirstShadowChild() { return NULL; } + virtual IClientRenderable *NextShadowPeer() { return NULL; } + + // Returns the shadow cast type + virtual ShadowType_t ShadowCastType() { return SHADOWS_NONE; } + + // Create/get/destroy model instance + virtual void CreateModelInstance() { Assert(0); } + virtual ModelInstanceHandle_t GetModelInstance(); + + // Attachments + virtual int LookupAttachment( const char *pAttachmentName ) { return -1; } + virtual bool GetAttachment( int number, Vector &origin, QAngle &angles ); + virtual bool GetAttachment( int number, matrix3x4_t &matrix ); + virtual bool IgnoresZBuffer( void ) const { return false; } + + // Rendering clip plane, should be 4 floats, return value of NULL indicates a disabled render clip plane + virtual float *GetRenderClipPlane( void ) { return NULL; } + + // Returns the transform from RenderOrigin/RenderAngles to world + virtual const matrix3x4_t &RenderableToWorldTransform() + { + return m_ModelToWorld; + } + +public: + bool Init( int index, StaticPropLump_t &lump, model_t *pModel ); + // KD Tree + void InsertPropIntoKDTree(); + void RemovePropFromKDTree(); + + void PrecacheLighting(); + void RecomputeStaticLighting(); + + int LeafCount() const; + int FirstLeaf() const; + LightCacheHandle_t GetLightCacheHandle() const; + void SetModelInstance( ModelInstanceHandle_t handle ); + void SetRenderHandle( ClientRenderHandle_t handle ); + void CleanUpRenderHandle( ); + ClientRenderHandle_t GetRenderHandle() const; + void SetAlpha( unsigned char alpha ); + + // Create VPhysics representation + void CreateVPhysics( IPhysicsEnvironment *physenv, IVPhysicsKeyHandler *pDefaults, void *pGameData ); + + float Radius() const { return m_flRadius; } + int Flags() const { return m_Flags; } + + void SetFadeIndex( unsigned short nIndex ) { m_FadeIndex = nIndex; } + unsigned short FadeIndex() const { return m_FadeIndex; } + float ForcedFadeScale() const { return m_flForcedFadeScale; } + int DrawModelSlow( int flags ); + +private: + // Diagnostic information for static props + void DisplayStaticPropInfo( int nInfoType ); + inline void InitModelRenderInfo( ModelRenderInfo_t &sInfo, int flags ) + { + sInfo.origin = m_Origin; + sInfo.angles = m_Angles; + sInfo.pRenderable = this; + sInfo.pModel = m_pModel; + sInfo.pModelToWorld = &m_ModelToWorld; + sInfo.pLightingOffset = NULL; + sInfo.pLightingOrigin = &m_LightingOrigin; + sInfo.flags = flags; + sInfo.entity_index = -1; + sInfo.skin = m_Skin; + sInfo.body = 0; + sInfo.hitboxset = 0; + sInfo.instance = m_ModelInstance; + } + +private: + friend class CStaticPropMgr; + Vector m_Origin; + QAngle m_Angles; + model_t* m_pModel; + SpatialPartitionHandle_t m_Partition; + ModelInstanceHandle_t m_ModelInstance; + unsigned char m_Alpha; + unsigned char m_nSolidType; + unsigned char m_Skin; + unsigned char m_Flags; + unsigned short m_FirstLeaf; + unsigned short m_LeafCount; + CBaseHandle m_EntHandle; // FIXME: Do I need client + server handles? + ClientRenderHandle_t m_RenderHandle; + unsigned short m_FadeIndex; // Index into the m_StaticPropFade dictionary + float m_flForcedFadeScale; + + // bbox is the same for both GetBounds and GetRenderBounds since static props never move. + // GetRenderBounds is interpolated data, and GetBounds is last networked. + Vector m_RenderBBoxMin; + Vector m_RenderBBoxMax; + matrix3x4_t m_ModelToWorld; + float m_flRadius; + + Vector m_WorldRenderBBoxMin; + Vector m_WorldRenderBBoxMax; + + // FIXME: This sucks. Need to store the lighting origin off + // because the time at which the static props are unserialized + // doesn't necessarily match the time at which we can initialize the light cache + Vector m_LightingOrigin; +}; + + +//----------------------------------------------------------------------------- +// The engine's static prop manager +//----------------------------------------------------------------------------- +class CStaticPropMgr : public IStaticPropMgrEngine, public IStaticPropMgrClient, public IStaticPropMgrServer +{ +public: + // constructor, destructor + CStaticPropMgr(); + virtual ~CStaticPropMgr(); + + // methods of IStaticPropMgrEngine + virtual bool Init(); + virtual void Shutdown(); + virtual void LevelInit(); + virtual void LevelInitClient(); + virtual void LevelShutdown(); + virtual void LevelShutdownClient(); + virtual bool IsPropInPVS( IHandleEntity *pHandleEntity, const byte *pVis ) const; + virtual ICollideable *GetStaticProp( IHandleEntity *pHandleEntity ); + virtual void RecomputeStaticLighting( ); + virtual LightCacheHandle_t GetLightCacheHandleForStaticProp( IHandleEntity *pHandleEntity ); + virtual bool IsStaticProp( IHandleEntity *pHandleEntity ) const; + virtual bool IsStaticProp( CBaseHandle handle ) const; + virtual int GetStaticPropIndex( IHandleEntity *pHandleEntity ) const; + virtual ICollideable *GetStaticPropByIndex( int propIndex ); + + // methods of IStaticPropMgrClient + virtual void ComputePropOpacity( const Vector &viewOrigin, float factor ); + virtual void TraceRayAgainstStaticProp( const Ray_t& ray, int staticPropIndex, trace_t& tr ); + virtual void AddDecalToStaticProp( Vector const& rayStart, Vector const& rayEnd, + int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr ); + virtual void AddColorDecalToStaticProp( Vector const& rayStart, Vector const& rayEnd, + int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr, bool bUseColor, Color cColor ); + virtual void AddShadowToStaticProp( unsigned short shadowHandle, IClientRenderable* pRenderable ); + virtual void RemoveAllShadowsFromStaticProp( IClientRenderable* pRenderable ); + virtual void GetStaticPropMaterialColorAndLighting( trace_t* pTrace, + int staticPropIndex, Vector& lighting, Vector& matColor ); + virtual void CreateVPhysicsRepresentations( IPhysicsEnvironment *physenv, IVPhysicsKeyHandler *pDefaults, void *pGameData ); + + // methods of IStaticPropMgrServer + + //Changes made specifically to support the Portal mod (smack Dave Kircher if something breaks) + //=================================================================== + virtual void GetAllStaticProps( CUtlVector *pOutput ); + virtual void GetAllStaticPropsInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector *pOutput ); + virtual void GetAllStaticPropsInOBB( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, CUtlVector *pOutput ); + //=================================================================== + + virtual bool PropHasBakedLightingDisabled( IHandleEntity *pHandleEntity) const; + + // Internal methods + const Vector &ViewOrigin() const { return m_vecLastViewOrigin; } + + // Computes the opacity for a single static prop + void ComputePropOpacity( CStaticProp &prop ); + void DrawStaticProps( IClientRenderable **pProps, int count, bool bShadowDepth, bool drawVCollideWireframe ); + void DrawStaticProps_Slow( IClientRenderable **pProps, int count, bool bShadowDepth, bool drawVCollideWireframe ); + void DrawStaticProps_Fast( IClientRenderable **pProps, int count, bool bShadowDepth ); + void DrawStaticProps_FastPipeline( IClientRenderable **pProps, int count, bool bShadowDepth ); + +private: + void OutputLevelStats( void ); + void PrecacheLighting(); + + // Methods associated with unserializing static props + void UnserializeModelDict( CUtlBuffer& buf ); + void UnserializeLeafList( CUtlBuffer& buf ); + void UnserializeModels( CUtlBuffer& buf ); + void UnserializeStaticProps(); + + int HandleEntityToIndex( IHandleEntity *pHandleEntity ) const; + + // Computes fade from screen-space fading + unsigned char ComputeScreenFade( CStaticProp &prop, float flMinSize, float flMaxSize, float flFalloffFactor ); + void ChangeRenderGroup( CStaticProp &prop ); + +private: + // Unique static prop models + struct StaticPropDict_t + { + model_t* m_pModel; + MDLHandle_t m_hMDL; + }; + + // Static props that fade use this data to fade + struct StaticPropFade_t + { + int m_Model; + union + { + float m_MinDistSq; + float m_MaxScreenWidth; + }; + union + { + float m_MaxDistSq; + float m_MinScreenWidth; + }; + float m_FalloffFactor; + }; + + // The list of all static props + CUtlVector m_StaticPropDict; + CUtlVector m_StaticProps; + CUtlVector m_StaticPropLeaves; + + // Static props that fade... + CUtlVector m_StaticPropFade; + + bool m_bLevelInitialized; + bool m_bClientInitialized; + Vector m_vecLastViewOrigin; + float m_flLastViewFactor; +}; + + +//----------------------------------------------------------------------------- +// Expose Interface to the game + client DLLs. +//----------------------------------------------------------------------------- +static CStaticPropMgr s_StaticPropMgr; +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CStaticPropMgr, IStaticPropMgrClient, INTERFACEVERSION_STATICPROPMGR_CLIENT, s_StaticPropMgr); +EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CStaticPropMgr, IStaticPropMgrServer, INTERFACEVERSION_STATICPROPMGR_SERVER, s_StaticPropMgr); + + +//----------------------------------------------------------------------------- +// +// Static prop +// +//----------------------------------------------------------------------------- +CStaticProp::CStaticProp() : m_pModel(0), m_Alpha(255) +{ + m_ModelInstance = MODEL_INSTANCE_INVALID; + m_Partition = PARTITION_INVALID_HANDLE; + m_EntHandle = INVALID_EHANDLE_INDEX; + m_RenderHandle = INVALID_CLIENT_RENDER_HANDLE; +} + +CStaticProp::~CStaticProp() +{ + RemovePropFromKDTree( ); + if (m_ModelInstance != MODEL_INSTANCE_INVALID) + { + modelrender->DestroyInstance( m_ModelInstance ); + } +} + + +//----------------------------------------------------------------------------- +// Initialization +//----------------------------------------------------------------------------- +bool CStaticProp::Init( int index, StaticPropLump_t &lump, model_t *pModel ) +{ + m_EntHandle.Init(index, STATICPROP_EHANDLE_MASK >> NUM_ENT_ENTRY_BITS); + m_Partition = PARTITION_INVALID_HANDLE; + m_flForcedFadeScale = lump.m_flForcedFadeScale; + VectorCopy( lump.m_Origin, m_Origin ); + VectorCopy( lump.m_Angles, m_Angles ); + m_pModel = pModel; + m_FirstLeaf = lump.m_FirstLeaf; + m_LeafCount = lump.m_LeafCount; + m_nSolidType = lump.m_Solid; + m_FadeIndex = INVALID_FADE_INDEX; + + MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); + + studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( m_pModel ); + + if ( pStudioHdr ) + { + if ( !( pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP ) ) + { + static int nBitchCount = 0; + if( nBitchCount < 100 ) + { + Warning( "model %s used as a static prop, but not compiled as a static prop\n", pStudioHdr->pszName() ); + nBitchCount++; + } + } + + if ( pStudioHdr->flags & STUDIOHDR_FLAGS_NO_FORCED_FADE ) + { + m_flForcedFadeScale = 0.0f; + } + } + + switch ( m_nSolidType ) + { + // These are valid + case SOLID_VPHYSICS: + case SOLID_BBOX: + case SOLID_NONE: + break; + + default: + { + char szModel[MAX_PATH]; + Q_strncpy( szModel, m_pModel ? modelloader->GetName( m_pModel ) : "unknown model", sizeof( szModel ) ); + Warning( "CStaticProp::Init: Map error, static_prop with bogus SOLID_ flag (%d)! (%s)\n", m_nSolidType, szModel ); + m_nSolidType = SOLID_NONE; + } + break; + } + + m_Alpha = 255; + m_Skin = (unsigned char)lump.m_Skin; + m_Flags = ( lump.m_Flags & (STATIC_PROP_SCREEN_SPACE_FADE | STATIC_PROP_FLAG_FADES | STATIC_PROP_NO_PER_VERTEX_LIGHTING) ); + + int nCurrentDXLevel = g_pMaterialSystemHardwareConfig->GetDXSupportLevel(); + bool bNoDraw = ( lump.m_nMinDXLevel && lump.m_nMinDXLevel > nCurrentDXLevel ); + bNoDraw = bNoDraw || ( lump.m_nMaxDXLevel && lump.m_nMaxDXLevel < nCurrentDXLevel ); + if ( bNoDraw ) + { + m_Flags |= STATIC_PROP_NO_DRAW; + } + + // Cache the model to world matrix since it never changes. + AngleMatrix( lump.m_Angles, lump.m_Origin, m_ModelToWorld ); + + // Cache the collision bounding box since it'll never change. + modelinfo->GetModelRenderBounds( m_pModel, m_RenderBBoxMin, m_RenderBBoxMax ); + m_flRadius = m_RenderBBoxMin.DistTo( m_RenderBBoxMax ) * 0.5f; + TransformAABB( m_ModelToWorld, m_RenderBBoxMin, m_RenderBBoxMax, m_WorldRenderBBoxMin, m_WorldRenderBBoxMax ); + + // FIXME: Sucky, but unless we want to re-read the static prop lump when the client is + // initialized (possible, but also gross), we need to cache off the illum center now + if (lump.m_Flags & STATIC_PROP_USE_LIGHTING_ORIGIN) + { + m_LightingOrigin = lump.m_LightingOrigin; + } + else + { + modelinfo->GetIlluminationPoint( m_pModel, this, m_Origin, m_Angles, &m_LightingOrigin ); + } + g_MakingDevShots = CommandLine()->FindParm( "-makedevshots" ) ? true : false; + + // If we do Mod_SetMaterialVarFlag() while running with the dedicated server, we crash. + // RJ said he'd save my butt and look into this. (Hip hip horray! We love RJ!) + if ( !sv.IsDedicated() && m_pModel ) + { + Mod_SetMaterialVarFlag( pModel, MATERIAL_VAR_IGNORE_ALPHA_MODULATION, true ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// EHandle +//----------------------------------------------------------------------------- +void CStaticProp::SetRefEHandle( const CBaseHandle &handle ) +{ + // Only the static prop mgr should be setting this... + Assert( 0 ); +} + + +const CBaseHandle& CStaticProp::GetRefEHandle() const +{ + return m_EntHandle; +} + + +//----------------------------------------------------------------------------- +// These methods return a box defined in the space of the entity +//----------------------------------------------------------------------------- +const Vector& CStaticProp::OBBMins( ) const +{ + if ( GetSolid() == SOLID_VPHYSICS ) + { + return m_pModel->mins; + } + Vector& tv = AllocTempVector(); + // FIXME: why doesn't this just return m_RenderBBoxMin? + VectorSubtract( m_WorldRenderBBoxMin, GetCollisionOrigin(), tv ); + return tv; +} + +const Vector& CStaticProp::OBBMaxs( ) const +{ + if ( GetSolid() == SOLID_VPHYSICS ) + { + return m_pModel->maxs; + } + Vector& tv = AllocTempVector(); + // FIXME: why doesn't this just return m_RenderBBoxMax? + VectorSubtract( m_WorldRenderBBoxMax, GetCollisionOrigin(), tv ); + return tv; +} + +void CStaticProp::WorldSpaceTriggerBounds( Vector* pVecWorldMins, Vector *pVecWorldMaxs ) const +{ + // This should never be called.. + Assert(0); +} + + +//----------------------------------------------------------------------------- +// Surrounding box +//----------------------------------------------------------------------------- +void CStaticProp::WorldSpaceSurroundingBounds( Vector* pVecWorldMins, Vector *pVecWorldMaxs ) +{ + *pVecWorldMins = m_WorldRenderBBoxMin; + *pVecWorldMaxs = m_WorldRenderBBoxMax; +} + + +//----------------------------------------------------------------------------- +// Data accessors +//----------------------------------------------------------------------------- +const Vector& CStaticProp::GetRenderOrigin( void ) +{ + return m_Origin; +} + +const QAngle& CStaticProp::GetRenderAngles( void ) +{ + return m_Angles; +} + +bool CStaticProp::GetAttachment( int number, Vector &origin, QAngle &angles ) +{ + origin = m_Origin; + angles = m_Angles; + return true; +} + +bool CStaticProp::GetAttachment( int number, matrix3x4_t &matrix ) +{ + MatrixCopy( RenderableToWorldTransform(), matrix ); + return true; +} + + +bool CStaticProp::IsTransparent( void ) +{ + return (m_Alpha < 255) || modelinfo->IsTranslucent(m_pModel); +} + +bool CStaticProp::IsTwoPass( void ) +{ + return modelinfo->IsTranslucentTwoPass(m_pModel); +} + +bool CStaticProp::ShouldDraw() +{ + return ( m_Flags & STATIC_PROP_NO_DRAW ) == 0; +} + + +//----------------------------------------------------------------------------- +// Render setup +//----------------------------------------------------------------------------- +bool CStaticProp::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime ) +{ + if (!m_pModel) + return false; + + MatrixCopy( m_ModelToWorld, pBoneToWorldOut[0] ); + return true; +} + +void CStaticProp::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights ) +{ +} + +void CStaticProp::DoAnimationEvents( void ) +{ +} + + +//----------------------------------------------------------------------------- +// Render baby! +//----------------------------------------------------------------------------- +const model_t* CStaticProp::GetModel( ) const +{ + return m_pModel; +} + + +//----------------------------------------------------------------------------- +// Accessors +//----------------------------------------------------------------------------- +inline int CStaticProp::LeafCount() const +{ + return m_LeafCount; +} + +inline int CStaticProp::FirstLeaf() const +{ + return m_FirstLeaf; +} + +inline ModelInstanceHandle_t CStaticProp::GetModelInstance() +{ + return m_ModelInstance; +} + +inline void CStaticProp::SetModelInstance( ModelInstanceHandle_t handle ) +{ + m_ModelInstance = handle; +} + +inline void CStaticProp::SetRenderHandle( ClientRenderHandle_t handle ) +{ + m_RenderHandle = handle; +} + +inline ClientRenderHandle_t CStaticProp::GetRenderHandle() const +{ + return m_RenderHandle; +} + +void CStaticProp::CleanUpRenderHandle( ) +{ + if ( m_RenderHandle != INVALID_CLIENT_RENDER_HANDLE ) + { +#ifndef SWDS + clientleafsystem->RemoveRenderable( m_RenderHandle ); +#endif + m_RenderHandle = INVALID_CLIENT_RENDER_HANDLE; + } +} + + +//----------------------------------------------------------------------------- +// Determine alpha and blend amount for transparent objects based on render state info +//----------------------------------------------------------------------------- +inline void CStaticProp::SetAlpha( unsigned char alpha ) +{ + m_Alpha = alpha; +} + +void CStaticProp::ComputeFxBlend( ) +{ + s_StaticPropMgr.ComputePropOpacity( *this ); +} + +int CStaticProp::GetFxBlend( ) +{ + return m_Alpha; +} + +void CStaticProp::GetColorModulation( float* color ) +{ + color[0] = color[1] = color[2] = 1.0f; +} + + +//----------------------------------------------------------------------------- +// custom collision test +//----------------------------------------------------------------------------- +bool CStaticProp::TestCollision( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + Assert(0); + return false; +} + + +//----------------------------------------------------------------------------- +// Perform hitbox test, returns true *if hitboxes were tested at all*!! +//----------------------------------------------------------------------------- +bool CStaticProp::TestHitboxes( const Ray_t &ray, unsigned int fContentsMask, trace_t& tr ) +{ + return false; +} + + +//----------------------------------------------------------------------------- +// Returns the BRUSH model index if this is a brush model. Otherwise, returns -1. +//----------------------------------------------------------------------------- +int CStaticProp::GetCollisionModelIndex() +{ + return -1; +} + + +//----------------------------------------------------------------------------- +// Return the model, if it's a studio model. +//----------------------------------------------------------------------------- +const model_t* CStaticProp::GetCollisionModel() +{ + return m_pModel; +} + + +//----------------------------------------------------------------------------- +// Get angles and origin. +//----------------------------------------------------------------------------- +const Vector& CStaticProp::GetCollisionOrigin() const +{ + return m_Origin; +} + +const QAngle& CStaticProp::GetCollisionAngles() const +{ + if ( GetSolid() == SOLID_VPHYSICS ) + { + return m_Angles; + } + + return vec3_angle; +} + +const matrix3x4_t& CStaticProp::CollisionToWorldTransform() const +{ + return m_ModelToWorld; +} + + +//----------------------------------------------------------------------------- +// Return a SOLID_ define. +//----------------------------------------------------------------------------- +SolidType_t CStaticProp::GetSolid() const +{ + return (SolidType_t)m_nSolidType; +} + +int CStaticProp::GetSolidFlags() const +{ + return 0; +} + +bool CStaticProp::UsesPowerOfTwoFrameBufferTexture( void ) +{ + if ( !m_pModel ) + return false; + + return ( m_pModel->flags & MODELFLAG_STUDIOHDR_USES_FB_TEXTURE ) ? true : false; +} + +bool CStaticProp::UsesFullFrameBufferTexture( void ) +{ + return false; +} + +ClientRenderHandle_t& CStaticProp::RenderHandle() +{ + return m_RenderHandle; +} + +IPVSNotify* CStaticProp::GetPVSNotifyInterface() +{ + return NULL; +} + + +void CStaticProp::GetRenderBounds( Vector& mins, Vector& maxs ) +{ + mins = m_RenderBBoxMin; + maxs = m_RenderBBoxMax; +} + +void CStaticProp::GetRenderBoundsWorldspace( Vector& mins, Vector& maxs ) +{ + mins = m_WorldRenderBBoxMin; + maxs = m_WorldRenderBBoxMax; +} + +bool CStaticProp::ShouldReceiveProjectedTextures( int flags ) +{ + if( flags & SHADOW_FLAGS_FLASHLIGHT ) + { + return true; + } + else + { + return false; + } +} + +bool CStaticProp::ShouldCacheRenderInfo() +{ + return true; +} + + +void CStaticProp::PrecacheLighting() +{ +#ifndef SWDS + if ( m_ModelInstance == MODEL_INSTANCE_INVALID ) + { + LightCacheHandle_t lightCacheHandle = CreateStaticLightingCache( m_LightingOrigin, m_WorldRenderBBoxMin, m_WorldRenderBBoxMax ); + m_ModelInstance = modelrender->CreateInstance( this, &lightCacheHandle ); + } +#endif +} + + +void CStaticProp::RecomputeStaticLighting( void ) +{ +#ifndef SWDS + modelrender->RecomputeStaticLighting( m_ModelInstance ); +#endif +} + + +//----------------------------------------------------------------------------- +// Diagnostic information for static props +//----------------------------------------------------------------------------- +void CStaticProp::DisplayStaticPropInfo( int nInfoType ) +{ +#ifndef SWDS + char buf[512]; + switch( nInfoType ) + { + case 1: + Q_snprintf( buf, sizeof( buf ), "%s", modelloader->GetName( m_pModel ) ); + break; + + case 2: + Q_snprintf(buf, sizeof( buf ), "%d", (m_EntHandle.ToInt() & (~STATICPROP_EHANDLE_MASK)) ); + break; + + case 3: + { + float flDist = GetRenderOrigin().DistTo( s_StaticPropMgr.ViewOrigin() ); + Q_snprintf(buf, sizeof( buf ), "%.1f", flDist ); + } + break; + + case 4: + { + CMatRenderContextPtr pRenderContext( materials ); + float flPixelWidth = pRenderContext->ComputePixelWidthOfSphere( GetRenderOrigin(), Radius() ); + Q_snprintf(buf, sizeof( buf ), "%.1f", flPixelWidth ); + } + break; + } + + Vector vecTextBox = ( m_WorldRenderBBoxMax + m_WorldRenderBBoxMin ) * 0.5f; + vecTextBox.z = m_WorldRenderBBoxMax.z + 10; + CDebugOverlay::AddTextOverlay( vecTextBox, 0.0f, buf ); +#endif +} + + +//----------------------------------------------------------------------------- +// Draws the model +//----------------------------------------------------------------------------- +int CStaticProp::DrawModelSlow( int flags ) +{ +#ifndef SWDS + VPROF_BUDGET( "CStaticProp::DrawModel", VPROF_BUDGETGROUP_STATICPROP_RENDERING ); + + if ( !r_drawstaticprops.GetBool() ) + return 0; + + if ( r_drawstaticprops.GetInt() == 2 ) + { + flags |= STUDIO_WIREFRAME; + } + +#ifdef _DEBUG + if ( r_DrawSpecificStaticProp.GetInt() >= 0 ) + { + if ( (m_EntHandle.ToInt() & (~STATICPROP_EHANDLE_MASK) ) != r_DrawSpecificStaticProp.GetInt() ) + return 0; + } +#endif + + if ( (m_Alpha == 0) || !m_pModel ) + return 0; + +#ifdef _DEBUG + studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( m_pModel ); + Assert( pStudioHdr ); + if ( !( pStudioHdr->flags & STUDIOHDR_FLAGS_STATIC_PROP ) ) + { + return 0; + } +#endif + + if ( r_colorstaticprops.GetBool() ) + { + // deterministic random sequence + unsigned short hash[3]; + hash[0] = HashItem( m_ModelInstance ); + hash[1] = HashItem( hash[0] ); + hash[2] = HashItem( hash[1] ); + r_colormod[0] = (float)hash[0] * 1.0f/65535.0f; + r_colormod[1] = (float)hash[1] * 1.0f/65535.0f; + r_colormod[2] = (float)hash[2] * 1.0f/65535.0f; + VectorNormalize( r_colormod ); + } + + flags |= STUDIO_STATIC_LIGHTING; + + int nInfoType = r_staticpropinfo.GetInt(); + if ( nInfoType ) + { + DisplayStaticPropInfo( nInfoType ); + } + + // CDebugOverlay::AddBoxOverlay( vec3_origin, m_WorldRenderBBoxMin, m_WorldRenderBBoxMax, vec3_angle, 255, 0, 0, 32, 0.01 ); + // CDebugOverlay::AddBoxOverlay( GetRenderOrigin(), m_RenderBBoxMin, m_RenderBBoxMax, GetRenderAngles(), 0, 255, 0, 32, 0.01 ); + + ModelRenderInfo_t sInfo; + InitModelRenderInfo( sInfo, flags ); + g_pStudioRender->SetColorModulation( r_colormod ); + g_pStudioRender->SetAlphaModulation( r_blend ); + // Restore the matrices if we're skinning + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + int drawn = modelrender->DrawModelEx( sInfo ); + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PopMatrix(); + + if ( m_pModel && (flags & STUDIO_WIREFRAME_VCOLLIDE) ) + { + if ( m_nSolidType == SOLID_VPHYSICS ) + { + // This works because VCollideForModel only uses modelindex for mod_brush + // and props are always mod_Studio. + vcollide_t * pCollide = CM_VCollideForModel( -1, m_pModel ); + if ( pCollide && pCollide->solidCount == 1 ) + { + static color32 debugColor = {0,255,255,0}; + DebugDrawPhysCollide( pCollide->solids[0], NULL, m_ModelToWorld, debugColor, false ); + } + } + else if ( m_nSolidType == SOLID_BBOX ) + { + static Color debugColor( 0, 255, 255, 255 ); + RenderWireframeBox( m_Origin, vec3_angle, m_pModel->mins, m_pModel->maxs, debugColor, true ); + } + } + + return drawn; +#else + return 0; +#endif +} + +int CStaticProp::DrawModel( int flags ) +{ +#ifndef SWDS + VPROF_BUDGET( "CStaticProp::DrawModel", VPROF_BUDGETGROUP_STATICPROP_RENDERING ); + + if ( (m_Alpha == 0) || !m_pModel ) + return 0; + + if ( IsUsingStaticPropDebugModes() || (flags & STUDIO_WIREFRAME_VCOLLIDE) ) + return DrawModelSlow(flags); + + flags |= STUDIO_STATIC_LIGHTING; + + ModelRenderInfo_t sInfo; + InitModelRenderInfo( sInfo, flags ); + g_pStudioRender->SetColorModulation( r_colormod ); + g_pStudioRender->SetAlphaModulation( r_blend ); + // Restore the matrices if we're skinning + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + int drawn = modelrender->DrawModelExStaticProp( sInfo ); + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PopMatrix(); + + return drawn; +#else + return 0; +#endif +} + +//----------------------------------------------------------------------------- +// KD Tree +//----------------------------------------------------------------------------- +void CStaticProp::InsertPropIntoKDTree() +{ + Assert( m_Partition == PARTITION_INVALID_HANDLE ); + if ( m_nSolidType == SOLID_NONE ) + return; + + // Compute the bbox of the prop + Vector mins, maxs; + matrix3x4_t propToWorld; + AngleMatrix( m_Angles, m_Origin, propToWorld ); + TransformAABB( propToWorld, m_pModel->mins, m_pModel->maxs, mins, maxs ); + + // If it's using vphysics, get a good AABB + if ( m_nSolidType == SOLID_VPHYSICS ) + { + vcollide_t *pCollide = CM_VCollideForModel( -1, m_pModel ); + if ( pCollide && pCollide->solidCount ) + { + physcollision->CollideGetAABB( &mins, &maxs, pCollide->solids[0], m_Origin, m_Angles ); + } + else + { + char szModel[MAX_PATH]; + Q_strncpy( szModel, m_pModel ? modelloader->GetName( m_pModel ) : "unknown model", sizeof( szModel ) ); + Warning( "SOLID_VPHYSICS static prop with no vphysics model! (%s)\n", szModel ); + m_nSolidType = SOLID_NONE; + return; + } + } + + // add the entity to the KD tree so we will collide against it + m_Partition = SpatialPartition()->CreateHandle( this, + PARTITION_CLIENT_SOLID_EDICTS | PARTITION_CLIENT_STATIC_PROPS | + PARTITION_ENGINE_SOLID_EDICTS | PARTITION_ENGINE_STATIC_PROPS, + mins, maxs ); + + Assert( m_Partition != PARTITION_INVALID_HANDLE ); +} + +void CStaticProp::RemovePropFromKDTree() +{ + // Release the spatial partition handle + if ( m_Partition != PARTITION_INVALID_HANDLE ) + { + SpatialPartition()->DestroyHandle( m_Partition ); + m_Partition = PARTITION_INVALID_HANDLE; + } +} + + +//----------------------------------------------------------------------------- +// Create VPhysics representation +//----------------------------------------------------------------------------- +void CStaticProp::CreateVPhysics( IPhysicsEnvironment *pPhysEnv, IVPhysicsKeyHandler *pDefaults, void *pGameData ) +{ + if ( m_nSolidType == SOLID_NONE ) + return; + + vcollide_t *pVCollide = NULL; + solid_t solid; + CPhysCollide* pPhysCollide = NULL; + + if ( m_pModel && m_nSolidType == SOLID_VPHYSICS ) + { + // This works because VCollideForModel only uses modelindex for mod_brush + // and props are always mod_Studio. + pVCollide = CM_VCollideForModel( -1, m_pModel ); + } + + if (pVCollide) + { + pPhysCollide = pVCollide->solids[0]; + + IVPhysicsKeyParser *pParse = physcollision->VPhysicsKeyParserCreate( pVCollide->pKeyValues ); + while ( !pParse->Finished() ) + { + const char *pBlock = pParse->GetCurrentBlockName(); + if ( !strcmpi( pBlock, "solid" ) ) + { + pParse->ParseSolid( &solid, pDefaults ); + break; + } + else + { + pParse->SkipBlock(); + } + } + physcollision->VPhysicsKeyParserDestroy( pParse ); + } + else + { + if ( m_nSolidType != SOLID_BBOX ) + { + char szModel[MAX_PATH]; + Q_strncpy( szModel, m_pModel ? modelloader->GetName( m_pModel ) : "unknown model", sizeof( szModel ) ); + Warning( "Map Error: Static prop with bogus solid type %d! (%s)\n", m_nSolidType, szModel ); + m_nSolidType = SOLID_NONE; + return; + } +#ifdef _XBOX + else + solid.surfaceprop[0] = '\0'; +#endif + + // If there's no collide, we need a bbox... + pPhysCollide = physcollision->BBoxToCollide( m_pModel->mins, m_pModel->maxs ); + solid.params = g_PhysDefaultObjectParams; + } + + Assert(pPhysCollide); + solid.params.enableCollisions = true; + solid.params.pGameData = pGameData; + solid.params.pName = "prop_static"; + + int surfaceData = physprop->GetSurfaceIndex( solid.surfaceprop ); + pPhysEnv->CreatePolyObjectStatic( pPhysCollide, + surfaceData, m_Origin, m_Angles, &solid.params ); + //PhysCheckAdd( pPhys, "Static" ); +} + + +//----------------------------------------------------------------------------- +// Expose IStaticPropMgr to the engine +//----------------------------------------------------------------------------- +IStaticPropMgrEngine* StaticPropMgr() +{ + return &s_StaticPropMgr; +} + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CStaticPropMgr::CStaticPropMgr() +{ + m_bLevelInitialized = false; + m_bClientInitialized = false; +} + +CStaticPropMgr::~CStaticPropMgr() +{ + Assert( !m_bLevelInitialized ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool CStaticPropMgr::Init() +{ + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CStaticPropMgr::Shutdown() +{ + if ( !m_bLevelInitialized ) + return; + + LevelShutdown(); +} + +//----------------------------------------------------------------------------- +// Unserialize static prop model dictionary +//----------------------------------------------------------------------------- +void CStaticPropMgr::UnserializeModelDict( CUtlBuffer& buf ) +{ + int count = buf.GetInt(); + m_StaticPropDict.AddMultipleToTail( count ); + + for ( int i=0; i < count; i++ ) + { + StaticPropDictLump_t lump; + buf.Get( &lump, sizeof(StaticPropDictLump_t) ); + + StaticPropDict_t &dict = m_StaticPropDict[i]; + + dict.m_pModel = (model_t *)modelloader->GetModelForName( + lump.m_Name, IModelLoader::FMODELLOADER_STATICPROP ); + dict.m_hMDL = modelinfo->GetCacheHandle( dict.m_pModel ); + g_pMDLCache->LockStudioHdr( dict.m_hMDL ); + } +} + +void CStaticPropMgr::UnserializeLeafList( CUtlBuffer& buf ) +{ + int nCount = buf.GetInt(); + m_StaticPropLeaves.Purge(); + if ( nCount > 0 ) + { + m_StaticPropLeaves.AddMultipleToTail( nCount ); + buf.Get( m_StaticPropLeaves.Base(), nCount * sizeof(StaticPropLeafLump_t) ); + } +} + +template +void UnserializeLump( StaticPropLump_t* _output, CUtlBuffer& buf ) +{ + Assert(_output != NULL); + + SerializedLumpType srcLump; + buf.Get( &srcLump, sizeof(SerializedLumpType) ); + + (*_output) = srcLump; +} + +// Specialization for current version. +template <> +void UnserializeLump(StaticPropLump_t* _output, CUtlBuffer& buf) +{ + Assert(_output != NULL); + + buf.Get(_output, sizeof(StaticPropLump_t)); +} + +void CStaticPropMgr::UnserializeModels( CUtlBuffer& buf ) +{ + // Version check + int nLumpVersion = Mod_GameLumpVersion( GAMELUMP_STATIC_PROPS ); + if ( nLumpVersion < 4 ) + { + Warning("Really old map format! Static props can't be loaded...\n"); + return; + } + + int count = buf.GetInt(); + + // Gotta preallocate the static props here so no rellocations take place + // the leaf list stores pointers to these tricky little guys. + m_StaticProps.AddMultipleToTail(count); + for ( int i = 0; i < count; ++i ) + { + StaticPropLump_t lump; + switch ( nLumpVersion ) + { + case 4: UnserializeLump(&lump, buf); break; + case 5: UnserializeLump(&lump, buf); break; + case 6: UnserializeLump(&lump, buf); break; + case 7: // Falls down to version 10. We promoted TF to version 10 to deal with SFM. + case 10: UnserializeLump(&lump, buf); break; + + break; + default: + Assert("Unexpected version while deserializing lumps."); + } + + m_StaticProps[i].Init( i, lump, m_StaticPropDict[lump.m_PropType].m_pModel ); + + // For distance-based fading, keep a list of the things that need + // to be faded out. Not sure if this is the optimal way of doing it + // but it's easy for now; we'll have to test later how large this list gets. + // If it's <100 or so, we should be fine + if (lump.m_Flags & STATIC_PROP_FLAG_FADES) + { + int idx = m_StaticPropFade.AddToTail(); + m_StaticProps[i].SetFadeIndex( (unsigned short)idx ); + StaticPropFade_t& fade = m_StaticPropFade[idx]; + fade.m_Model = i; + fade.m_MinDistSq = lump.m_FadeMinDist; + fade.m_MaxDistSq = lump.m_FadeMaxDist; + + if ( (lump.m_Flags & STATIC_PROP_SCREEN_SPACE_FADE) == 0 ) + { + fade.m_MinDistSq *= fade.m_MinDistSq; + fade.m_MaxDistSq *= fade.m_MaxDistSq; + } + + if (fade.m_MaxDistSq != fade.m_MinDistSq) + { + if (lump.m_Flags & STATIC_PROP_SCREEN_SPACE_FADE) + { + fade.m_FalloffFactor = 255.0f / (fade.m_MaxScreenWidth - fade.m_MinScreenWidth); + } + else + { + fade.m_FalloffFactor = 255.0f / (fade.m_MaxDistSq - fade.m_MinDistSq); + } + } + else + { + fade.m_FalloffFactor = 255.0f; + } + } + + // Add the prop to the K-D tree for collision + m_StaticProps[i].InsertPropIntoKDTree( ); + } +} + +void CStaticPropMgr::OutputLevelStats( void ) +{ + // STATS + int i; + int totalVerts = 0; + for( i = 0; i < m_StaticProps.Count(); i++ ) + { + CStaticProp *pStaticProp = &m_StaticProps[i]; + model_t *pModel = (model_t*)pStaticProp->GetModel(); + if( !pModel ) + { + continue; + } + Assert( pModel->type == mod_studio ); + studiohdr_t *pStudioHdr = ( studiohdr_t * )modelloader->GetExtraData( pModel ); + int bodyPart; + for( bodyPart = 0; bodyPart < pStudioHdr->numbodyparts; bodyPart++ ) + { + mstudiobodyparts_t *pBodyPart = pStudioHdr->pBodypart( bodyPart ); + int model; + for( model = 0; model < pBodyPart->nummodels; model++ ) + { + mstudiomodel_t *pStudioModel = pBodyPart->pModel( model ); + totalVerts += pStudioModel->numvertices; + } + } + } + Warning( "%d static prop instances in map\n", ( int )m_StaticProps.Count() ); + Warning( "%d static prop models in map\n", ( int )m_StaticPropDict.Count() ); + Warning( "%d static prop verts in map\n", ( int )totalVerts ); +} + + +//----------------------------------------------------------------------------- +// Unserialize static props +//----------------------------------------------------------------------------- +void CStaticPropMgr::UnserializeStaticProps() +{ + // Unserialize static props, insert them into the appropriate leaves + int size = Mod_GameLumpSize( GAMELUMP_STATIC_PROPS ); + if (!size) + return; + + COM_TimestampedLog( "UnserializeStaticProps - start"); + + MEM_ALLOC_CREDIT(); + CUtlBuffer buf( 0, size ); + if ( Mod_LoadGameLump( GAMELUMP_STATIC_PROPS, buf.PeekPut(), size )) + { + buf.SeekPut( CUtlBuffer::SEEK_HEAD, size ); + COM_TimestampedLog( "UnserializeModelDict" ); + UnserializeModelDict( buf ); + COM_TimestampedLog( "UnserializeLeafList" ); + UnserializeLeafList( buf ); + COM_TimestampedLog( "UnserializeModels" ); + UnserializeModels( buf ); + } + + COM_TimestampedLog( "UnserializeStaticProps - end"); +} + + +//----------------------------------------------------------------------------- +// Level init, shutdown +//----------------------------------------------------------------------------- +void CStaticPropMgr::LevelInit() +{ + if ( m_bLevelInitialized ) + return; + + Assert( !m_bClientInitialized ); + m_bLevelInitialized = true; + + // Read in static props that have been compiled into the bsp file + UnserializeStaticProps(); + + // OutputLevelStats(); +} + +void CStaticPropMgr::LevelShutdown() +{ + if ( !m_bLevelInitialized ) + return; + + // Deal with client-side stuff, if appropriate + if ( m_bClientInitialized ) + { + LevelShutdownClient(); + } + + m_bLevelInitialized = false; + + FOR_EACH_VEC( m_StaticPropDict, i ) + { + g_pMDLCache->UnlockStudioHdr( m_StaticPropDict[i].m_hMDL ); + } + + m_StaticProps.Purge(); + m_StaticPropDict.Purge(); + m_StaticPropFade.Purge(); +} + +void CStaticPropMgr::LevelInitClient() +{ +#ifndef SWDS + if ( sv.IsDedicated() ) + return; + + extern ConVar r_proplightingfromdisk; + + bool bNeedsMapAccess = r_proplightingfromdisk.GetBool(); + if ( bNeedsMapAccess ) + { + g_pFileSystem->BeginMapAccess(); + } + + Assert( m_bLevelInitialized ); + Assert( !m_bClientInitialized ); + + // Since the client will be ready at a later time than the server + // to set up its data, we need a separate call to handle that + int nCount = m_StaticProps.Count(); + for ( int i = 0; i < nCount; ++i ) + { + CStaticProp &prop = m_StaticProps[i]; + clientleafsystem->CreateRenderableHandle( &m_StaticProps[i], true ); + if ( !prop.ShouldDraw() ) + continue; + + ClientRenderHandle_t handle = m_StaticProps[i].RenderHandle(); + if ( prop.LeafCount() > 0 ) + { + // Add the prop to all the leaves it lies in + clientleafsystem->AddRenderableToLeaves( handle, prop.LeafCount(), (unsigned short*)&m_StaticPropLeaves[prop.FirstLeaf()] ); + } + else + { + Vector origin = prop.GetCollisionOrigin(); + Vector mins = prop.OBBMins(); + Vector maxs = prop.OBBMaxs(); + DevMsg( 1, "Static prop in 0 leaves! %s, @ %.1f, %.1f, %.1f\n", modelloader->GetName( prop.GetModel() ), origin.x, origin.y, origin.z ); + } + } + + PrecacheLighting(); + + m_bClientInitialized = true; + + if ( bNeedsMapAccess ) + { + g_pFileSystem->EndMapAccess(); + } +#endif +} + +void CStaticPropMgr::LevelShutdownClient() +{ + if ( !m_bClientInitialized ) + return; + + Assert( m_bLevelInitialized ); + + for (int i = m_StaticProps.Count(); --i >= 0; ) + { + m_StaticProps[i].CleanUpRenderHandle( ); + modelrender->SetStaticLighting( m_StaticProps[i].GetModelInstance(), NULL ); + } + +#ifndef SWDS + // Make sure static prop lightcache is reset + ClearStaticLightingCache(); +#endif + + m_bClientInitialized = false; +} + +//----------------------------------------------------------------------------- +// Create physics representations of props +//----------------------------------------------------------------------------- +void CStaticPropMgr::CreateVPhysicsRepresentations( IPhysicsEnvironment *pPhysEnv, IVPhysicsKeyHandler *pDefaults, void *pGameData ) +{ + // Walk through the static props + make collideable thingies for them. + int nCount = m_StaticProps.Count(); + for ( int i = nCount; --i >= 0; ) + { + m_StaticProps[i].CreateVPhysics( pPhysEnv, pDefaults, pGameData ); + } +} + + +//----------------------------------------------------------------------------- +// Handles to props +//----------------------------------------------------------------------------- +inline int CStaticPropMgr::HandleEntityToIndex( IHandleEntity *pHandleEntity ) const +{ + Assert( IsStaticProp( pHandleEntity ) ); + return pHandleEntity->GetRefEHandle().GetEntryIndex(); +} + +ICollideable *CStaticPropMgr::GetStaticProp( IHandleEntity *pHandleEntity ) +{ + if ( !IsStaticProp( pHandleEntity ) ) + { + return NULL; + } + + int nIndex = pHandleEntity ? pHandleEntity->GetRefEHandle().GetEntryIndex() : -1; + if ( nIndex < 0 || nIndex > m_StaticProps.Count() ) + { + return NULL; + } + return &m_StaticProps[nIndex]; +} + + +ICollideable *CStaticPropMgr::GetStaticPropByIndex( int propIndex ) +{ + if ( propIndex < m_StaticProps.Count() ) + { + return &m_StaticProps[propIndex]; + } + Assert(0); + return NULL; +} + + + +//----------------------------------------------------------------------------- +// Get large amounts of handles to static props +//----------------------------------------------------------------------------- + +void CStaticPropMgr::GetAllStaticProps( CUtlVector *pOutput ) +{ + if ( pOutput == NULL ) return; + int iPropVectorSize = m_StaticProps.Count(); + + int counter; + for ( counter = 0; counter != iPropVectorSize; ++counter ) + { + pOutput->AddToTail( &m_StaticProps[counter] ); + } +} + +void CStaticPropMgr::GetAllStaticPropsInAABB( const Vector &vMins, const Vector &vMaxs, CUtlVector *pOutput ) +{ + if ( pOutput == NULL ) return; + int iPropVectorSize = m_StaticProps.Count(); + + int counter; + for ( counter = 0; counter != iPropVectorSize; ++counter ) + { + CStaticProp *pProp = &m_StaticProps[counter]; + + Vector vPropMins, vPropMaxs; + pProp->WorldSpaceSurroundingBounds( &vPropMins, &vPropMaxs ); + + if( vPropMaxs.x < vMins.x ) continue; + if( vPropMaxs.y < vMins.y ) continue; + if( vPropMaxs.z < vMins.z ) continue; + + if( vPropMins.x > vMaxs.x ) continue; + if( vPropMins.y > vMaxs.y ) continue; + if( vPropMins.z > vMaxs.z ) continue; + + pOutput->AddToTail( pProp ); + } +} + +void CStaticPropMgr::GetAllStaticPropsInOBB( const Vector &ptOrigin, const Vector &vExtent1, const Vector &vExtent2, const Vector &vExtent3, CUtlVector *pOutput ) +{ + if ( pOutput == NULL ) return; + int counter; + + Vector vAABBMins, vAABBMaxs; + vAABBMins = ptOrigin; + vAABBMaxs = ptOrigin; + Vector ptAABBExtents[8]; + + Vector ptOBBExtents[8]; + for( counter = 0; counter != 8; ++counter ) + { + ptOBBExtents[counter] = ptOrigin; + if( counter & (1<<0) ) ptOBBExtents[counter] += vExtent1; + if( counter & (1<<1) ) ptOBBExtents[counter] += vExtent2; + if( counter & (1<<2) ) ptOBBExtents[counter] += vExtent3; + + //expand AABB extents + if( ptOBBExtents[counter].x < vAABBMins.x ) vAABBMins.x = ptOBBExtents[counter].x; + if( ptOBBExtents[counter].x > vAABBMaxs.x ) vAABBMaxs.x = ptOBBExtents[counter].x; + if( ptOBBExtents[counter].y < vAABBMins.y ) vAABBMins.y = ptOBBExtents[counter].y; + if( ptOBBExtents[counter].y > vAABBMaxs.y ) vAABBMaxs.y = ptOBBExtents[counter].y; + if( ptOBBExtents[counter].z < vAABBMins.z ) vAABBMins.z = ptOBBExtents[counter].z; + if( ptOBBExtents[counter].z > vAABBMaxs.z ) vAABBMaxs.z = ptOBBExtents[counter].z; + } + + //generate planes for the obb so we can use halfspace elimination + Vector vOBBPlaneNormals[6]; + float fOBBPlaneDists[6]; + + vOBBPlaneNormals[0] = vExtent1; + vOBBPlaneNormals[0].NormalizeInPlace(); + fOBBPlaneDists[0] = vOBBPlaneNormals[0].Dot( ptOrigin + vExtent1 ); + vOBBPlaneNormals[1] = -vOBBPlaneNormals[0]; + fOBBPlaneDists[1] = vOBBPlaneNormals[1].Dot( ptOrigin ); + + vOBBPlaneNormals[2] = vExtent2; + vOBBPlaneNormals[2].NormalizeInPlace(); + fOBBPlaneDists[2] = vOBBPlaneNormals[2].Dot( ptOrigin + vExtent2 ); + vOBBPlaneNormals[3] = -vOBBPlaneNormals[2]; + fOBBPlaneDists[3] = vOBBPlaneNormals[3].Dot( ptOrigin ); + + vOBBPlaneNormals[4] = vExtent3; + vOBBPlaneNormals[4].NormalizeInPlace(); + fOBBPlaneDists[4] = vOBBPlaneNormals[4].Dot( ptOrigin + vExtent3 ); + vOBBPlaneNormals[5] = -vOBBPlaneNormals[4]; + fOBBPlaneDists[5] = vOBBPlaneNormals[5].Dot( ptOrigin ); + + int iPropVectorSize = m_StaticProps.Count(); + + for ( counter = 0; counter != iPropVectorSize; ++counter ) + { + CStaticProp *pProp = &m_StaticProps[counter]; + + Vector vPropMins, vPropMaxs; + pProp->WorldSpaceSurroundingBounds( &vPropMins, &vPropMaxs ); + + if( vPropMaxs.x < vAABBMins.x ) continue; + if( vPropMaxs.y < vAABBMins.y ) continue; + if( vPropMaxs.z < vAABBMins.z ) continue; + + if( vPropMins.x > vAABBMaxs.x ) continue; + if( vPropMins.y > vAABBMaxs.y ) continue; + if( vPropMins.z > vAABBMaxs.z ) continue; + + + + //static prop AABB and desired AABB intersect, do OBB tests + + Vector vPropOBBMins = pProp->OBBMins(); + Vector vPropOBBMaxs = pProp->OBBMaxs(); + + Vector ptPropExtents[8]; + + matrix3x4_t matPropWorld; + AngleMatrix( pProp->GetCollisionAngles(), pProp->GetCollisionOrigin(), matPropWorld ); + + int counter2, counter3; + //generate prop extents, TODO: update these to handle props with OBB's since it should be nearly trivial + for( counter2 = 0; counter2 != 8; ++counter2 ) + { + /*ptPropExtents[counter2].x = (counter2 & (1<<0))?(vPropMaxs.x):(vPropMins.x); + ptPropExtents[counter2].y = (counter2 & (1<<1))?(vPropMaxs.y):(vPropMins.y); + ptPropExtents[counter2].z = (counter2 & (1<<2))?(vPropMaxs.z):(vPropMins.z);*/ + + Vector ptTemp; + ptTemp.x = (counter2 & (1<<0))?(vPropOBBMaxs.x):(vPropOBBMins.x); + ptTemp.y = (counter2 & (1<<1))?(vPropOBBMaxs.y):(vPropOBBMins.y); + ptTemp.z = (counter2 & (1<<2))?(vPropOBBMaxs.z):(vPropOBBMins.z); + + VectorTransform( ptTemp, matPropWorld, ptPropExtents[counter2] ); + } + + + + for( counter2 = 0; counter2 != 6; ++counter2 ) //loop over OBB planes + { + for( counter3 = 0; counter3 != 8; ++counter3 ) //loop over prop extents + { + if( (ptPropExtents[counter3].Dot( vOBBPlaneNormals[counter2] ) - fOBBPlaneDists[counter2]) < 0.0f ) + { + //an extent of the prop is within the OBB halfspace, this halfspace does not eliminate our prop, move to the next halfspace + break; + } + } + if( counter3 == 8 ) break; //if all 8 extents lie outside the halfspace, then the prop is not in the OBB + } + if( counter2 == 6 ) + { + //if all 6 planes failed to eliminate the extents, the OBB and prop intersect + + //FIXME: Halfspace elimination will never remove props that do intersect, but leaves some false positives in some cases. + + pOutput->AddToTail( pProp ); + } + } +} + +//----------------------------------------------------------------------------- +// Are we a static prop? +//----------------------------------------------------------------------------- +bool CStaticPropMgr::IsStaticProp( IHandleEntity *pHandleEntity ) const +{ + return (!pHandleEntity) || ( (pHandleEntity->GetRefEHandle().GetSerialNumber() == (STATICPROP_EHANDLE_MASK >> NUM_ENT_ENTRY_BITS) ) != 0 ); +} + +bool CStaticPropMgr::IsStaticProp( CBaseHandle handle ) const +{ + return (handle.GetSerialNumber() == (STATICPROP_EHANDLE_MASK >> NUM_ENT_ENTRY_BITS)); +} + +int CStaticPropMgr::GetStaticPropIndex( IHandleEntity *pHandleEntity ) const +{ + return HandleEntityToIndex( pHandleEntity ); +} + +bool CStaticPropMgr::PropHasBakedLightingDisabled( IHandleEntity *pHandleEntity ) const +{ + // Strip off the bits + int nIndex = HandleEntityToIndex( pHandleEntity ); + + // Get the prop + const CStaticProp &prop = m_StaticProps[nIndex]; + + return ( (prop.Flags() & STATIC_PROP_NO_PER_VERTEX_LIGHTING ) != 0 ); +} + +//----------------------------------------------------------------------------- +// Compute static lighting +//----------------------------------------------------------------------------- +void CStaticPropMgr::PrecacheLighting() +{ + COM_TimestampedLog( "CStaticPropMgr::PrecacheLighting - start"); + + int numVerts = 0; + if ( IsX360() ) + { + if ( g_bLoadedMapHasBakedPropLighting && g_pMaterialSystemHardwareConfig->SupportsStreamOffset() ) + { + // total the static prop verts + int i = m_StaticProps.Count(); + while ( --i >= 0 ) + { + if ( PropHasBakedLightingDisabled( m_StaticProps[i].GetEntityHandle() ) ) + { + continue; + } + + studiohwdata_t *pStudioHWData = g_pMDLCache->GetHardwareData( ( (model_t*)m_StaticProps[i].GetModel() )->studio ); + for ( int lodID = pStudioHWData->m_RootLOD; lodID < pStudioHWData->m_NumLODs; lodID++ ) + { + studioloddata_t *pLOD = &pStudioHWData->m_pLODs[lodID]; + for ( int meshID = 0; meshID < pStudioHWData->m_NumStudioMeshes; meshID++ ) + { + studiomeshdata_t *pMesh = &pLOD->m_pMeshData[meshID]; + for ( int groupID = 0; groupID < pMesh->m_NumGroup; groupID++ ) + { + numVerts += pMesh->m_pMeshGroup[groupID].m_NumVertices; + } + } + } + } + } + modelrender->SetupColorMeshes( numVerts ); + } + + int i = m_StaticProps.Count(); + while ( --i >= 0 ) + { + MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); + if ( !m_StaticProps[i].ShouldDraw() ) + continue; + m_StaticProps[i].PrecacheLighting(); + } + + COM_TimestampedLog( "CStaticPropMgr::PrecacheLighting - end"); +} + +void CStaticPropMgr::RecomputeStaticLighting( ) +{ + int i = m_StaticProps.Count(); + while ( --i >= 0 ) + { + if ( !m_StaticProps[i].ShouldDraw() ) + continue; + m_StaticProps[i].RecomputeStaticLighting(); + } +} + + +//----------------------------------------------------------------------------- +// Is the prop in the PVS? +//----------------------------------------------------------------------------- +bool CStaticPropMgr::IsPropInPVS( IHandleEntity *pHandleEntity, const byte *pVis ) const +{ + // Strip off the bits + int nIndex = HandleEntityToIndex( pHandleEntity ); + + // Get the prop + const CStaticProp &prop = m_StaticProps[nIndex]; + + int i; + int end = prop.FirstLeaf() + prop.LeafCount(); + for( i = prop.FirstLeaf(); i < end; i++ ) + { + Assert( i >= 0 && i < m_StaticPropLeaves.Count() ); + int clusterID = CM_LeafCluster( m_StaticPropLeaves[i].m_Leaf ); + if( pVis[ clusterID >> 3 ] & ( 1 << ( clusterID & 7 ) ) ) + { + return true; + } + } + return false; +} + + +void CStaticPropMgr::DrawStaticProps_Slow( IClientRenderable **pProps, int count, bool bShadowDepth, bool drawVCollideWireframe ) +{ + // slow mode + MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); + int flags = STUDIO_RENDER; + if (bShadowDepth) + flags |= STUDIO_SHADOWDEPTHTEXTURE; + if ( drawVCollideWireframe ) + flags |= STUDIO_WIREFRAME_VCOLLIDE; + + for ( int i = 0; i < count; i++ ) + { + CStaticProp *pProp = (CStaticProp *)(pProps[i]); + pProp->DrawModelSlow( flags ); + } +} + +void CStaticPropMgr::DrawStaticProps_Fast( IClientRenderable **pProps, int count, bool bShadowDepth ) +{ +#ifndef SWDS + float color[3]; + color[0] = color[1] = color[2] = 1.0f; + g_pStudioRender->SetColorModulation(color); + g_pStudioRender->SetAlphaModulation(1.0f); + g_pStudioRender->SetViewState( CurrentViewOrigin(), CurrentViewRight(), CurrentViewUp(), CurrentViewForward() ); + CMatRenderContextPtr pRenderContext( materials ); + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PushMatrix(); + pRenderContext->LoadIdentity(); + ModelRenderInfo_t sInfo; + sInfo.flags = STUDIO_RENDER | STUDIO_STATIC_LIGHTING; + if (bShadowDepth) + sInfo.flags |= STUDIO_SHADOWDEPTHTEXTURE; + + sInfo.entity_index = -1; + sInfo.body = 0; + sInfo.hitboxset = 0; + sInfo.pLightingOffset = NULL; + for ( int i = 0; i < count; i++ ) + { + MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); + CStaticProp *pProp = (CStaticProp *)(pProps[i]); + if ( !pProp->m_pModel ) + continue; + sInfo.instance = pProp->m_ModelInstance; + sInfo.pModel = pProp->m_pModel; + sInfo.origin = pProp->m_Origin; + sInfo.angles = pProp->m_Angles; + sInfo.skin = pProp->m_Skin; + sInfo.pLightingOrigin = &pProp->m_LightingOrigin; + sInfo.pModelToWorld = &pProp->m_ModelToWorld; + sInfo.pRenderable = pProps[i]; + modelrender->DrawModelExStaticProp( sInfo ); + } + // Restore the matrices if we're skinning + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->PopMatrix(); +#endif +} + + +// NOTE: This is a work in progress for a new static prop (eventually new model) rendering pipeline +void CStaticPropMgr::DrawStaticProps_FastPipeline( IClientRenderable **pProps, int count, bool bShadowDepth ) +{ + const int MAX_OBJECTS = 2048; + StaticPropRenderInfo_t propList[MAX_OBJECTS]; + int listCount = 0; + if ( count > MAX_OBJECTS ) + { + DrawStaticProps_FastPipeline( pProps + MAX_OBJECTS, count - MAX_OBJECTS, bShadowDepth ); + } + + for ( int i = 0; i < count; i++ ) + { + CStaticProp *pProp = (CStaticProp *)(pProps[i]); + if ( !pProp->m_pModel ) + continue; + propList[listCount].pModelToWorld = &pProp->m_ModelToWorld; + propList[listCount].pModel = pProp->m_pModel; + propList[listCount].instance = pProp->m_ModelInstance; + propList[listCount].skin = pProp->m_Skin; + propList[listCount].pRenderable = pProp; + propList[listCount].pLightingOrigin = &pProp->m_LightingOrigin; + listCount++; + } + modelrender->DrawStaticPropArrayFast( propList, listCount, bShadowDepth ); +} + +// NOTE: Set this to zero to revert to the previous static prop lighting behavior +ConVar pipeline_static_props("pipeline_static_props", "1"); +void CStaticPropMgr::DrawStaticProps( IClientRenderable **pProps, int count, bool bShadowDepth, bool drawVCollideWireframe ) +{ + VPROF_BUDGET( "CStaticPropMgr::DrawStaticProps", VPROF_BUDGETGROUP_STATICPROP_RENDERING ); + + if ( !r_drawstaticprops.GetBool() ) + return; + + if ( IsUsingStaticPropDebugModes() || drawVCollideWireframe ) + { + DrawStaticProps_Slow( pProps, count, bShadowDepth, drawVCollideWireframe ); + } + else + { + // the fast pipeline is only supported on dx8+ + if ( pipeline_static_props.GetBool() && + g_pMaterialSystemHardwareConfig->GetDXSupportLevel() >= 80 && + g_pMaterialSystemHardwareConfig->SupportsColorOnSecondStream() && + g_pMaterialSystemHardwareConfig->SupportsStaticPlusDynamicLighting() ) + { + DrawStaticProps_FastPipeline( pProps, count, bShadowDepth ); + } + else + { + DrawStaticProps_Fast( pProps, count, bShadowDepth ); + } + } +} + + +//----------------------------------------------------------------------------- +// Returns the lightcache handle +//----------------------------------------------------------------------------- +LightCacheHandle_t CStaticPropMgr::GetLightCacheHandleForStaticProp( IHandleEntity *pHandleEntity ) +{ + int nIndex = HandleEntityToIndex(pHandleEntity); + return modelrender->GetStaticLighting( m_StaticProps[ nIndex ].GetModelInstance() ); +} + + +//----------------------------------------------------------------------------- +// Computes fade from screen-space fading +//----------------------------------------------------------------------------- +unsigned char CStaticPropMgr::ComputeScreenFade( CStaticProp &prop, float flMinSize, float flMaxSize, float flFalloffFactor ) +{ + CMatRenderContextPtr pRenderContext( materials ); + + float flPixelWidth = pRenderContext->ComputePixelWidthOfSphere( prop.GetRenderOrigin(), prop.Radius() ); + + unsigned char alpha = 0; + if ( flPixelWidth > flMinSize ) + { + if ( (flMaxSize >= 0) && (flPixelWidth < flMaxSize) ) + { + int nAlpha = flFalloffFactor * (flPixelWidth - flMinSize); + alpha = clamp( nAlpha, 0, 255 ); + } + else + { + alpha = 255; + } + } + + return alpha; +} + + +//----------------------------------------------------------------------------- +// Changes the render group based on alpha +//----------------------------------------------------------------------------- +void CStaticPropMgr::ChangeRenderGroup( CStaticProp &prop ) +{ +#ifndef SWDS + static RenderGroup_t opaqueRenderGroup = ( g_bClientLeafSystemV1 ) ? RENDER_GROUP_OPAQUE_ENTITY : RENDER_GROUP_OPAQUE_STATIC; + ClientRenderHandle_t renderHandle = prop.GetRenderHandle(); + Assert( renderHandle != INVALID_CLIENT_RENDER_HANDLE ); + if ( prop.GetFxBlend() == 0 ) + { + clientleafsystem->ChangeRenderableRenderGroup( renderHandle, opaqueRenderGroup ); + } + else if ( prop.GetFxBlend() == 255 ) + { + RenderGroup_t nRenderGroup = prop.IsTransparent() ? RENDER_GROUP_TRANSLUCENT_ENTITY : opaqueRenderGroup; + clientleafsystem->ChangeRenderableRenderGroup( renderHandle, nRenderGroup ); + } + else + { + clientleafsystem->ChangeRenderableRenderGroup( renderHandle, RENDER_GROUP_TRANSLUCENT_ENTITY ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// System to update prop opacity +//----------------------------------------------------------------------------- +void CStaticPropMgr::ComputePropOpacity( CStaticProp &prop ) +{ +#ifndef SWDS + if (modelinfoclient->ModelHasMaterialProxy( prop.GetModel() )) + { + modelinfoclient->RecomputeTranslucency( prop.GetModel(), prop.GetSkin(), prop.GetBody(), prop.GetClientRenderable(), (float)(prop.GetFxBlend()) / 255.0f ); + } +#endif + +#ifdef LINUX + bool bVisionOverride = false; +#else + static ConVarRef localplayer_visionflags( "localplayer_visionflags" ); + bool bVisionOverride = ( localplayer_visionflags.IsValid() && ( localplayer_visionflags.GetInt() & ( 0x01 ) ) ); // Pyro-vision Goggles + if ( !g_pMaterialSystemHardwareConfig->SupportsPixelShaders_2_0() ) + { + bVisionOverride = false; + } +#endif + + // If we're taking devshots, don't fade anything + if ( g_MakingDevShots || m_flLastViewFactor < 0 || bVisionOverride ) + { + prop.SetAlpha( 255 ); + ChangeRenderGroup( prop ); + return; + } + + if ( (prop.Flags() & STATIC_PROP_FLAG_FADES) != 0 ) + { + // Distance-based fading. + // Step over the list of all things that want to be faded out and recompute alpha + + // Not sure if this is a fast enough way of doing it + // but it's easy for now; we'll have to test later how large this list gets. + // If it's <100 or so, we should be fine + Assert( prop.FadeIndex() != INVALID_FADE_INDEX ); + + Vector v; + + StaticPropFade_t& fade = m_StaticPropFade[prop.FadeIndex()]; + + unsigned char alpha; + + // Calculate distance (badly) + if ( (prop.Flags() & STATIC_PROP_SCREEN_SPACE_FADE) == 0 ) + { + VectorSubtract( prop.GetRenderOrigin(), m_vecLastViewOrigin, v ); + VectorScale( v, m_flLastViewFactor, v ); + + alpha = 0; + float sqDist = v.LengthSqr(); + if ( sqDist < fade.m_MaxDistSq ) + { + if ( (fade.m_MinDistSq >= 0) && (sqDist > fade.m_MinDistSq) ) + { + int nAlpha = fade.m_FalloffFactor * (fade.m_MaxDistSq - sqDist); + alpha = clamp( nAlpha, 0, 255 ); + } + else + { + alpha = 255; + } + } + } + else + { + alpha = ComputeScreenFade( prop, fade.m_MinScreenWidth, fade.m_MaxScreenWidth, fade.m_FalloffFactor ); + } + + prop.SetAlpha( alpha ); + ChangeRenderGroup( prop ); + } + else + { + prop.SetAlpha( 255 ); + ChangeRenderGroup( prop ); + } + +#ifndef SWDS + if ( !IsXbox() ) + { + // Fade all props, if we have a default level setting + // But only change the fade if it's more translucent than any other fades we might have + unsigned char alpha = modelinfoclient->ComputeLevelScreenFade( prop.GetRenderOrigin(), prop.Radius(), prop.ForcedFadeScale() ); + unsigned char nViewAlpha = modelinfoclient->ComputeViewScreenFade( prop.GetRenderOrigin(), prop.Radius(), prop.ForcedFadeScale() ); + if ( nViewAlpha < alpha ) + { + alpha = nViewAlpha; + } + + if ( alpha < prop.GetFxBlend() ) + { + prop.SetAlpha( alpha ); + ChangeRenderGroup( prop ); + } + } +#endif +} + + +//----------------------------------------------------------------------------- +// System to update prop opacity +//----------------------------------------------------------------------------- +void CStaticPropMgr::ComputePropOpacity( const Vector &viewOrigin, float factor ) +{ + // Cache these off for the call to ComputeFX blend which is compute later + m_vecLastViewOrigin = viewOrigin; + m_flLastViewFactor = factor; +} + + +//----------------------------------------------------------------------------- +// Purpose: Trace a ray against the specified static Prop. Returns point of intersection in trace_t +//----------------------------------------------------------------------------- +void CStaticPropMgr::TraceRayAgainstStaticProp( const Ray_t& ray, int staticPropIndex, trace_t& tr ) +{ +#ifndef SWDS + // Get the prop + CStaticProp& prop = m_StaticProps[staticPropIndex]; + + if (prop.GetSolid() != SOLID_NONE) + { + // FIXME: Better bloat? + // Bloat a little bit so we get the intersection + Ray_t temp = ray; + temp.m_Delta *= 1.1f; + g_pEngineTraceClient->ClipRayToEntity( temp, MASK_ALL, &prop, &tr ); + } + else + { + // no collision + tr.fraction = 1.0f; + } +#endif +} + + +//----------------------------------------------------------------------------- +// Adds decals to static props, returns point of decal in trace_t +//----------------------------------------------------------------------------- +void CStaticPropMgr::AddDecalToStaticProp( Vector const& rayStart, Vector const& rayEnd, + int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr ) +{ + Color tempColor( 255, 255, 255 ); + AddColorDecalToStaticProp( rayStart, rayEnd, staticPropIndex, decalIndex, doTrace, tr, false, tempColor ); +} + +//----------------------------------------------------------------------------- +void CStaticPropMgr::AddColorDecalToStaticProp( Vector const& rayStart, Vector const& rayEnd, + int staticPropIndex, int decalIndex, bool doTrace, trace_t& tr, bool bUseColor, Color cColor ) +{ +#ifndef SWDS + // Invalid static prop? Blow it off! + if (staticPropIndex >= m_StaticProps.Size()) + { + memset( &tr, 0, sizeof(trace_t) ); + tr.fraction = 1.0f; + return; + } + + Ray_t ray; + ray.Init( rayStart, rayEnd ); + if (doTrace) + { + // Trace the ray against the prop + TraceRayAgainstStaticProp( ray, staticPropIndex, tr ); + if (tr.fraction == 1.0f) + return; + } + + if ( !r_drawmodeldecals.GetInt() ) + return; + + // Get the prop + CStaticProp& prop = m_StaticProps[staticPropIndex]; + + // Found the point, now lets apply the decals + Assert( prop.GetModelInstance() != MODEL_INSTANCE_INVALID ); + + // Choose a new ray along which to project the decal based on + // surface normal. This prevents decal skewing + bool noPokethru = false; + if (doTrace && (prop.GetSolid() == SOLID_VPHYSICS) && !tr.startsolid && !tr.allsolid) + { + Vector temp; + VectorSubtract( tr.endpos, tr.plane.normal, temp ); + ray.Init( tr.endpos, temp ); + noPokethru = true; + } + + // FIXME: Pass in decal up? + // FIXME: What to do about the body parameter? + Vector up(0, 0, 1); + if ( bUseColor ) + { + modelrender->AddColoredDecal( prop.GetModelInstance(), ray, up, decalIndex, 0, cColor, noPokethru ); + } + else + { + modelrender->AddDecal( prop.GetModelInstance(), ray, up, decalIndex, 0, noPokethru ); + } + +#endif +} +//----------------------------------------------------------------------------- +// Adds/removes shadows from static props +//----------------------------------------------------------------------------- +void CStaticPropMgr::AddShadowToStaticProp( unsigned short shadowHandle, IClientRenderable* pRenderable ) +{ +#ifndef SWDS + Assert( dynamic_cast(pRenderable) != 0 ); + + CStaticProp* pProp = static_cast(pRenderable); + + g_pShadowMgr->AddShadowToModel( shadowHandle, pProp->GetModelInstance() ); +#endif +} + +void CStaticPropMgr::RemoveAllShadowsFromStaticProp( IClientRenderable* pRenderable ) +{ +#ifndef SWDS + Assert( dynamic_cast(pRenderable) != 0 ); + CStaticProp* pProp = static_cast(pRenderable); + if (pProp->GetModelInstance() != MODEL_INSTANCE_INVALID) + { + g_pShadowMgr->RemoveAllShadowsFromModel( pProp->GetModelInstance() ); + } +#endif +} + + +//----------------------------------------------------------------------------- +// Gets the lighting + material color of a static prop +//----------------------------------------------------------------------------- +void CStaticPropMgr::GetStaticPropMaterialColorAndLighting( trace_t* pTrace, + int staticPropIndex, Vector& lighting, Vector& matColor ) +{ +#ifndef SWDS + // Invalid static prop? Blow it off! + if (staticPropIndex >= m_StaticProps.Size()) + { + lighting.Init( 0, 0, 0 ); + matColor.Init( 1, 1, 1 ); + return; + } + + // Get the prop + CStaticProp& prop = m_StaticProps[staticPropIndex]; + + // Ask the model info about what we need to know + modelinfoclient->GetModelMaterialColorAndLighting( (model_t*)prop.GetModel(), + prop.GetRenderOrigin(), prop.GetRenderAngles(), pTrace, lighting, matColor ); +#endif +} + +//----------------------------------------------------------------------------- +// Little debugger tool to report which prop we're looking at +//----------------------------------------------------------------------------- +void Cmd_PropCrosshair_f (void) +{ + Vector endPoint; + VectorMA( MainViewOrigin(), COORD_EXTENT * 1.74f, MainViewForward(), endPoint ); + + Ray_t ray; + ray.Init( MainViewOrigin(), endPoint ); + + trace_t tr; + CTraceFilterWorldAndPropsOnly traceFilter; + g_pEngineTraceServer->TraceRay( ray, MASK_ALL, &traceFilter, &tr ); + + if ( tr.hitbox > 0 ) + Msg( "hit prop %d\n", tr.hitbox - 1 ); + else + Msg( "didn't hit a prop\n" ); +} + +static ConCommand prop_crosshair( "prop_crosshair", Cmd_PropCrosshair_f, "Shows name for prop looking at", FCVAR_CHEAT ); + -- cgit v1.2.3