diff options
| author | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
|---|---|---|
| committer | Joe Ludwig <[email protected]> | 2013-06-26 15:22:04 -0700 |
| commit | 39ed87570bdb2f86969d4be821c94b722dc71179 (patch) | |
| tree | abc53757f75f40c80278e87650ea92808274aa59 /mp/src/game/client/detailobjectsystem.cpp | |
| download | source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.tar.xz source-sdk-2013-39ed87570bdb2f86969d4be821c94b722dc71179.zip | |
First version of the SOurce SDK 2013
Diffstat (limited to 'mp/src/game/client/detailobjectsystem.cpp')
| -rw-r--r-- | mp/src/game/client/detailobjectsystem.cpp | 2811 |
1 files changed, 2811 insertions, 0 deletions
diff --git a/mp/src/game/client/detailobjectsystem.cpp b/mp/src/game/client/detailobjectsystem.cpp new file mode 100644 index 00000000..ede990de --- /dev/null +++ b/mp/src/game/client/detailobjectsystem.cpp @@ -0,0 +1,2811 @@ +//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Draws grasses and other small objects
+//
+// $Revision: $
+// $NoKeywords: $
+//===========================================================================//
+#include "cbase.h"
+#include "detailobjectsystem.h"
+#include "gamebspfile.h"
+#include "tier1/utlbuffer.h"
+#include "tier1/utlmap.h"
+#include "view.h"
+#include "clientmode.h"
+#include "iviewrender.h"
+#include "bsptreedata.h"
+#include "tier0/vprof.h"
+#include "engine/ivmodelinfo.h"
+#include "materialsystem/imesh.h"
+#include "model_types.h"
+#include "env_detail_controller.h"
+#include "tier0/icommandline.h"
+#include "c_world.h"
+
+#include "tier0/valve_minmax_off.h"
+#include <algorithm>
+#include "tier0/valve_minmax_on.h"
+
+#if defined(DOD_DLL) || defined(CSTRIKE_DLL)
+#define USE_DETAIL_SHAPES
+#endif
+
+#ifdef USE_DETAIL_SHAPES
+#include "engine/ivdebugoverlay.h"
+#include "playerenumerator.h"
+#endif
+
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define DETAIL_SPRITE_MATERIAL "detail/detailsprites"
+
+//-----------------------------------------------------------------------------
+// forward declarations
+//-----------------------------------------------------------------------------
+struct model_t;
+
+
+ConVar cl_detaildist( "cl_detaildist", "1200", 0, "Distance at which detail props are no longer visible" );
+ConVar cl_detailfade( "cl_detailfade", "400", 0, "Distance across which detail props fade in" );
+#if defined( USE_DETAIL_SHAPES )
+ConVar cl_detail_max_sway( "cl_detail_max_sway", "0", FCVAR_ARCHIVE, "Amplitude of the detail prop sway" );
+ConVar cl_detail_avoid_radius( "cl_detail_avoid_radius", "0", FCVAR_ARCHIVE, "radius around detail sprite to avoid players" );
+ConVar cl_detail_avoid_force( "cl_detail_avoid_force", "0", FCVAR_ARCHIVE, "force with which to avoid players ( in units, percentage of the width of the detail sprite )" );
+ConVar cl_detail_avoid_recover_speed( "cl_detail_avoid_recover_speed", "0", FCVAR_ARCHIVE, "how fast to recover position after avoiding players" );
+#endif
+
+// Per detail instance information
+struct DetailModelAdvInfo_t
+{
+ // precaculated angles for shaped sprites
+ Vector m_vecAnglesForward[3];
+ Vector m_vecAnglesRight[3]; // better to save this mem and calc per sprite ?
+ Vector m_vecAnglesUp[3];
+
+ // direction we're avoiding the player
+ Vector m_vecCurrentAvoid;
+
+ // yaw to sway on
+ float m_flSwayYaw;
+
+ // size of the shape
+ float m_flShapeSize;
+
+ int m_iShapeAngle;
+ float m_flSwayAmount;
+
+};
+
+class CDetailObjectSystemPerLeafData
+{
+ unsigned short m_FirstDetailProp;
+ unsigned short m_DetailPropCount;
+ int m_DetailPropRenderFrame;
+
+ CDetailObjectSystemPerLeafData( void )
+ {
+ m_FirstDetailProp = 0;
+ m_DetailPropCount = 0;
+ m_DetailPropRenderFrame = -1;
+ }
+};
+
+//-----------------------------------------------------------------------------
+// Detail models
+//-----------------------------------------------------------------------------
+struct SptrintInfo_t
+{
+ unsigned short m_nSpriteIndex;
+ float16 m_flScale;
+};
+
+class CDetailModel : public IClientUnknown, public IClientRenderable
+{
+ DECLARE_CLASS_NOBASE( CDetailModel );
+
+public:
+ CDetailModel();
+ ~CDetailModel();
+
+
+ // Initialization
+ bool InitCommon( int index, const Vector& org, const QAngle& angles );
+ bool Init( int index, const Vector& org, const QAngle& angles, model_t* pModel,
+ ColorRGBExp32 lighting, int lightstyle, unsigned char lightstylecount, int orientation );
+
+ bool InitSprite( int index, bool bFlipped, const Vector& org, const QAngle& angles,
+ unsigned short nSpriteIndex,
+ ColorRGBExp32 lighting, int lightstyle, unsigned char lightstylecount,
+ int orientation, float flScale, unsigned char type,
+ unsigned char shapeAngle, unsigned char shapeSize, unsigned char swayAmount );
+
+ void SetAlpha( unsigned char alpha ) { m_Alpha = alpha; }
+
+
+ // IClientUnknown overrides.
+public:
+
+ virtual IClientUnknown* GetIClientUnknown() { return this; }
+ virtual ICollideable* GetCollideable() { return 0; } // Static props DO implement this.
+ virtual IClientNetworkable* GetClientNetworkable() { return 0; }
+ virtual IClientRenderable* GetClientRenderable() { return this; }
+ virtual IClientEntity* GetIClientEntity() { return 0; }
+ virtual C_BaseEntity* GetBaseEntity() { return 0; }
+ virtual IClientThinkable* GetClientThinkable() { return 0; }
+
+
+ // IClientRenderable overrides.
+public:
+
+ virtual int GetBody() { return 0; }
+ virtual const Vector& GetRenderOrigin( );
+ virtual const QAngle& GetRenderAngles( );
+ virtual const matrix3x4_t & RenderableToWorldTransform();
+ virtual bool ShouldDraw();
+ virtual bool IsTwoPass( void ) { return false; }
+ virtual void OnThreadedDrawSetup() {}
+ virtual bool IsTransparent( void );
+ virtual const model_t* GetModel( ) const;
+ virtual int DrawModel( int flags );
+ virtual void ComputeFxBlend( );
+ virtual int GetFxBlend( );
+ 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 void GetRenderBounds( Vector& mins, Vector& maxs );
+ virtual IPVSNotify* GetPVSNotifyInterface();
+ virtual void GetRenderBoundsWorldspace( Vector& mins, Vector& maxs );
+ 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 bool IgnoresZBuffer( void ) const { return false; }
+ virtual bool LODTest() { return true; }
+
+ virtual ClientShadowHandle_t GetShadowHandle() const;
+ virtual ClientRenderHandle_t& RenderHandle();
+ virtual void GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType );
+ virtual bool IsShadowDirty( ) { return false; }
+ virtual void MarkShadowDirty( bool bDirty ) {}
+ virtual IClientRenderable *GetShadowParent() { return NULL; }
+ virtual IClientRenderable *FirstShadowChild(){ return NULL; }
+ virtual IClientRenderable *NextShadowPeer() { return NULL; }
+ virtual ShadowType_t ShadowCastType() { return SHADOWS_NONE; }
+ virtual void CreateModelInstance() {}
+ virtual ModelInstanceHandle_t GetModelInstance() { return MODEL_INSTANCE_INVALID; }
+ virtual int LookupAttachment( const char *pAttachmentName ) { return -1; }
+ virtual bool GetAttachment( int number, matrix3x4_t &matrix );
+ virtual bool GetAttachment( int number, Vector &origin, QAngle &angles );
+ virtual float * GetRenderClipPlane() { return NULL; }
+ virtual int GetSkin() { return 0; }
+ virtual void RecordToolMessage() {}
+
+ void GetColorModulation( float* color );
+
+ // Computes the render angles for screen alignment
+ void ComputeAngles( void );
+
+ // Calls the correct rendering func
+ void DrawSprite( CMeshBuilder &meshBuilder );
+
+ // Returns the number of quads the sprite will draw
+ int QuadsToDraw() const;
+
+ // Draw functions for the different types of sprite
+ void DrawTypeSprite( CMeshBuilder &meshBuilder );
+
+
+#ifdef USE_DETAIL_SHAPES
+ void DrawTypeShapeCross( CMeshBuilder &meshBuilder );
+ void DrawTypeShapeTri( CMeshBuilder &meshBuilder );
+
+ // check for players nearby and angle away from them
+ void UpdatePlayerAvoid( void );
+
+ void InitShapedSprite( unsigned char shapeAngle, unsigned char shapeSize, unsigned char swayAmount );
+ void InitShapeTri();
+ void InitShapeCross();
+
+ void DrawSwayingQuad( CMeshBuilder &meshBuilder, Vector vecOrigin, Vector vecSway, Vector2D texul, Vector2D texlr, unsigned char *color,
+ Vector width, Vector height );
+#endif
+
+ int GetType() const { return m_Type; }
+ unsigned char GetAlpha() const { return m_Alpha; }
+
+ bool IsDetailModelTranslucent();
+
+ // IHandleEntity stubs.
+public:
+ virtual void SetRefEHandle( const CBaseHandle &handle ) { Assert( false ); }
+ virtual const CBaseHandle& GetRefEHandle() const { Assert( false ); return *((CBaseHandle*)0); }
+
+ //---------------------------------
+ struct LightStyleInfo_t
+ {
+ unsigned int m_LightStyle:24;
+ unsigned int m_LightStyleCount:8;
+ };
+
+protected:
+ Vector m_Origin;
+ QAngle m_Angles;
+
+ ColorRGBExp32 m_Color;
+
+ unsigned char m_Orientation:2;
+ unsigned char m_Type:2;
+ unsigned char m_bHasLightStyle:1;
+ unsigned char m_bFlipped:1;
+
+ unsigned char m_Alpha;
+
+ static CUtlMap<CDetailModel *, LightStyleInfo_t> gm_LightStylesMap;
+
+#pragma warning( disable : 4201 ) //warning C4201: nonstandard extension used : nameless struct/union
+ union
+ {
+ model_t* m_pModel;
+ SptrintInfo_t m_SpriteInfo;
+ };
+#pragma warning( default : 4201 )
+
+#ifdef USE_DETAIL_SHAPES
+ // pointer to advanced properties
+ DetailModelAdvInfo_t *m_pAdvInfo;
+#endif
+};
+
+static ConVar mat_fullbright( "mat_fullbright", "0", FCVAR_CHEAT ); // hook into engine's cvars..
+extern ConVar r_DrawDetailProps;
+
+
+//-----------------------------------------------------------------------------
+// Dictionary for detail sprites
+//-----------------------------------------------------------------------------
+struct DetailPropSpriteDict_t
+{
+ Vector2D m_UL; // Coordinate of upper left
+ Vector2D m_LR; // Coordinate of lower right
+ Vector2D m_TexUL; // Texcoords of upper left
+ Vector2D m_TexLR; // Texcoords of lower left
+};
+
+struct FastSpriteX4_t
+{
+ // mess with this structure without care and you'll be in a world of trouble. layout matters.
+ FourVectors m_Pos;
+ fltx4 m_HalfWidth;
+ fltx4 m_Height;
+ uint8 m_RGBColor[4][4];
+ DetailPropSpriteDict_t *m_pSpriteDefs[4];
+
+ void ReplicateFirstEntryToOthers( void )
+ {
+ m_HalfWidth = ReplicateX4( SubFloat( m_HalfWidth, 0 ) );
+ m_Height = ReplicateX4( SubFloat( m_Height, 0 ) );
+
+ for( int i = 1; i < 4; i++ )
+ for( int j = 0; j < 4; j++ )
+ {
+ m_RGBColor[i][j] = m_RGBColor[0][j];
+ }
+ m_Pos.x = ReplicateX4( SubFloat( m_Pos.x, 0 ) );
+ m_Pos.y = ReplicateX4( SubFloat( m_Pos.y, 0 ) );
+ m_Pos.z = ReplicateX4( SubFloat( m_Pos.z, 0 ) );
+ }
+
+};
+
+
+struct FastSpriteQuadBuildoutBufferX4_t
+{
+ // mess with this structure without care and you'll be in a world of trouble. layout matters.
+ FourVectors m_Coords[4];
+ uint8 m_RGBColor[4][4];
+ fltx4 m_Alpha;
+ DetailPropSpriteDict_t *m_pSpriteDefs[4];
+};
+
+struct FastSpriteQuadBuildoutBufferNonSIMDView_t
+{
+ // mess with this structure without care and you'll be in a world of trouble. layout matters.
+ float m_flX0[4], m_flY0[4], m_flZ0[4];
+ float m_flX1[4], m_flY1[4], m_flZ1[4];
+ float m_flX2[4], m_flY2[4], m_flZ2[4];
+ float m_flX3[4], m_flY3[4], m_flZ3[4];
+
+ uint8 m_RGBColor[4][4];
+ float m_Alpha[4];
+ DetailPropSpriteDict_t *m_pSpriteDefs[4];
+};
+
+
+class CFastDetailLeafSpriteList : public CClientLeafSubSystemData
+{
+ friend class CDetailObjectSystem;
+ int m_nNumSprites;
+ int m_nNumSIMDSprites; // #sprites/4, rounded up
+ // simd pointers into larger array - don't free individually or you will be sad
+ FastSpriteX4_t *m_pSprites;
+
+ // state for partially drawn sprite lists
+ int m_nNumPendingSprites;
+ int m_nStartSpriteIndex;
+
+ CFastDetailLeafSpriteList( void )
+ {
+ m_nNumPendingSprites = 0;
+ m_nStartSpriteIndex = 0;
+ }
+
+};
+
+
+
+
+//-----------------------------------------------------------------------------
+// Responsible for managing detail objects
+//-----------------------------------------------------------------------------
+class CDetailObjectSystem : public IDetailObjectSystem, public ISpatialLeafEnumerator
+{
+public:
+ char const *Name() { return "DetailObjectSystem"; }
+
+ // constructor, destructor
+ CDetailObjectSystem();
+ ~CDetailObjectSystem();
+
+ bool IsPerFrame() { return false; }
+
+ // Init, shutdown
+ bool Init()
+ {
+ m_flDefaultFadeStart = cl_detailfade.GetFloat();
+ m_flDefaultFadeEnd = cl_detaildist.GetFloat();
+ return true;
+ }
+ void PostInit() {}
+ void Shutdown() {}
+
+ // Level init, shutdown
+ void LevelInitPreEntity();
+ void LevelInitPostEntity();
+ void LevelShutdownPreEntity();
+ void LevelShutdownPostEntity();
+
+ void OnSave() {}
+ void OnRestore() {}
+ void SafeRemoveIfDesired() {}
+
+ // Gets a particular detail object
+ IClientRenderable* GetDetailModel( int idx );
+
+ // Prepares detail for rendering
+ void BuildDetailObjectRenderLists( const Vector &vViewOrigin );
+
+ // Renders all opaque detail objects in a particular set of leaves
+ void RenderOpaqueDetailObjects( int nLeafCount, LeafIndex_t *pLeafList );
+
+ // Renders all translucent detail objects in a particular set of leaves
+ void RenderTranslucentDetailObjects( const Vector &viewOrigin, const Vector &viewForward, const Vector &viewRight, const Vector &viewUp, int nLeafCount, LeafIndex_t *pLeafList );
+
+ // Renders all translucent detail objects in a particular leaf up to a particular point
+ void RenderTranslucentDetailObjectsInLeaf( const Vector &viewOrigin, const Vector &viewForward, const Vector &viewRight, const Vector &viewUp, int nLeaf, const Vector *pVecClosestPoint );
+ void RenderFastTranslucentDetailObjectsInLeaf( const Vector &viewOrigin, const Vector &viewForward, const Vector &viewRight, const Vector &viewUp, int nLeaf, const Vector *pVecClosestPoint );
+
+
+
+ // Call this before rendering translucent detail objects
+ void BeginTranslucentDetailRendering( );
+
+ // Method of ISpatialLeafEnumerator
+ bool EnumerateLeaf( int leaf, int context );
+
+ DetailPropLightstylesLump_t& DetailLighting( int i ) { return m_DetailLighting[i]; }
+ DetailPropSpriteDict_t& DetailSpriteDict( int i ) { return m_DetailSpriteDict[i]; }
+
+private:
+ struct DetailModelDict_t
+ {
+ model_t* m_pModel;
+ };
+
+ struct EnumContext_t
+ {
+ Vector m_vViewOrigin;
+ int m_BuildWorldListNumber;
+ };
+
+ struct SortInfo_t
+ {
+ int m_nIndex;
+ float m_flDistance;
+ };
+
+ int BuildOutSortedSprites( CFastDetailLeafSpriteList *pData,
+ Vector const &viewOrigin,
+ Vector const &viewForward,
+ Vector const &viewRight,
+ Vector const &viewUp );
+
+ void RenderFastSprites( const Vector &viewOrigin, const Vector &viewForward, const Vector &viewRight, const Vector &viewUp, int nLeafCount, LeafIndex_t const * pLeafList );
+
+ void UnserializeFastSprite( FastSpriteX4_t *pSpritex4, int nSubField, DetailObjectLump_t const &lump, bool bFlipped, Vector const &posOffset );
+
+ // Unserialization
+ void ScanForCounts( CUtlBuffer& buf, int *pNumOldStyleObjects,
+ int *pNumFastSpritesToAllocate, int *nMaxOldInLeaf,
+ int *nMaxFastInLeaf ) const;
+
+ void UnserializeModelDict( CUtlBuffer& buf );
+ void UnserializeDetailSprites( CUtlBuffer& buf );
+ void UnserializeModels( CUtlBuffer& buf );
+ void UnserializeModelLighting( CUtlBuffer& buf );
+
+ Vector GetSpriteMiddleBottomPosition( DetailObjectLump_t const &lump ) const;
+ // Count the number of detail sprites in the leaf list
+ int CountSpritesInLeafList( int nLeafCount, LeafIndex_t *pLeafList ) const;
+
+ // Count the number of detail sprite quads in the leaf list
+ int CountSpriteQuadsInLeafList( int nLeafCount, LeafIndex_t *pLeafList ) const;
+
+ int CountFastSpritesInLeafList( int nLeafCount, LeafIndex_t const *pLeafList, int *nMaxInLeaf ) const;
+
+ void FreeSortBuffers( void );
+
+ // Sorts sprites in back-to-front order
+ static bool SortLessFunc( const SortInfo_t &left, const SortInfo_t &right );
+ int SortSpritesBackToFront( int nLeaf, const Vector &viewOrigin, const Vector &viewForward, SortInfo_t *pSortInfo );
+
+ // For fast detail object insertion
+ IterationRetval_t EnumElement( int userId, int context );
+
+ CUtlVector<DetailModelDict_t> m_DetailObjectDict;
+ CUtlVector<CDetailModel> m_DetailObjects;
+ CUtlVector<DetailPropSpriteDict_t> m_DetailSpriteDict;
+ CUtlVector<DetailPropSpriteDict_t> m_DetailSpriteDictFlipped;
+ CUtlVector<DetailPropLightstylesLump_t> m_DetailLighting;
+ FastSpriteX4_t *m_pFastSpriteData;
+
+ // Necessary to get sprites to batch correctly
+ CMaterialReference m_DetailSpriteMaterial;
+ CMaterialReference m_DetailWireframeMaterial;
+
+ // State stored off for rendering detail sprites in a single leaf
+ int m_nSpriteCount;
+ int m_nFirstSprite;
+ int m_nSortedLeaf;
+ int m_nSortedFastLeaf;
+ SortInfo_t *m_pSortInfo;
+ SortInfo_t *m_pFastSortInfo;
+ FastSpriteQuadBuildoutBufferX4_t *m_pBuildoutBuffer;
+
+ float m_flDefaultFadeStart;
+ float m_flDefaultFadeEnd;
+
+
+ // pre calcs for the current render frame
+ float m_flCurMaxSqDist;
+ float m_flCurFadeSqDist;
+ float m_flCurFalloffFactor;
+
+};
+
+
+//-----------------------------------------------------------------------------
+// System for dealing with detail objects
+//-----------------------------------------------------------------------------
+static CDetailObjectSystem s_DetailObjectSystem;
+
+IDetailObjectSystem* DetailObjectSystem()
+{
+ return &s_DetailObjectSystem;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Initialization
+//-----------------------------------------------------------------------------
+
+CUtlMap<CDetailModel *, CDetailModel::LightStyleInfo_t> CDetailModel::gm_LightStylesMap( DefLessFunc( CDetailModel * ) );
+
+bool CDetailModel::InitCommon( int index, const Vector& org, const QAngle& angles )
+{
+ VectorCopy( org, m_Origin );
+ VectorCopy( angles, m_Angles );
+ m_Alpha = 255;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Inline methods
+//-----------------------------------------------------------------------------
+
+// NOTE: If DetailPropType_t enum changes, change CDetailModel::QuadsToDraw
+static int s_pQuadCount[4] =
+{
+ 0, //DETAIL_PROP_TYPE_MODEL
+ 1, //DETAIL_PROP_TYPE_SPRITE
+ 4, //DETAIL_PROP_TYPE_SHAPE_CROSS
+ 3, //DETAIL_PROP_TYPE_SHAPE_TRI
+};
+
+inline int CDetailModel::QuadsToDraw() const
+{
+ return s_pQuadCount[m_Type];
+}
+
+
+//-----------------------------------------------------------------------------
+// Data accessors
+//-----------------------------------------------------------------------------
+const Vector& CDetailModel::GetRenderOrigin( void )
+{
+ return m_Origin;
+}
+
+const QAngle& CDetailModel::GetRenderAngles( void )
+{
+ return m_Angles;
+}
+
+const matrix3x4_t &CDetailModel::RenderableToWorldTransform()
+{
+ // Setup our transform.
+ static matrix3x4_t mat;
+ AngleMatrix( GetRenderAngles(), GetRenderOrigin(), mat );
+ return mat;
+}
+
+bool CDetailModel::GetAttachment( int number, matrix3x4_t &matrix )
+{
+ MatrixCopy( RenderableToWorldTransform(), matrix );
+ return true;
+}
+
+bool CDetailModel::GetAttachment( int number, Vector &origin, QAngle &angles )
+{
+ origin = m_Origin;
+ angles = m_Angles;
+ return true;
+}
+
+bool CDetailModel::IsTransparent( void )
+{
+ return (m_Alpha < 255) || modelinfo->IsTranslucent(m_pModel);
+}
+
+bool CDetailModel::ShouldDraw()
+{
+ // Don't draw in commander mode
+ return g_pClientMode->ShouldDrawDetailObjects();
+}
+
+void CDetailModel::GetRenderBounds( Vector& mins, Vector& maxs )
+{
+ int nModelType = modelinfo->GetModelType( m_pModel );
+ if (nModelType == mod_studio || nModelType == mod_brush)
+ {
+ modelinfo->GetModelRenderBounds( GetModel(), mins, maxs );
+ }
+ else
+ {
+ mins.Init( 0,0,0 );
+ maxs.Init( 0,0,0 );
+ }
+}
+
+IPVSNotify* CDetailModel::GetPVSNotifyInterface()
+{
+ return NULL;
+}
+
+void CDetailModel::GetRenderBoundsWorldspace( Vector& mins, Vector& maxs )
+{
+ DefaultRenderBoundsWorldspace( this, mins, maxs );
+}
+
+bool CDetailModel::ShouldReceiveProjectedTextures( int flags )
+{
+ return false;
+}
+
+bool CDetailModel::UsesPowerOfTwoFrameBufferTexture()
+{
+ return false;
+}
+
+bool CDetailModel::UsesFullFrameBufferTexture()
+{
+ return false;
+}
+
+void CDetailModel::GetShadowRenderBounds( Vector &mins, Vector &maxs, ShadowType_t shadowType )
+{
+ GetRenderBounds( mins, maxs );
+}
+
+ClientShadowHandle_t CDetailModel::GetShadowHandle() const
+{
+ return CLIENTSHADOW_INVALID_HANDLE;
+}
+
+ClientRenderHandle_t& CDetailModel::RenderHandle()
+{
+ AssertMsg( 0, "CDetailModel has no render handle" );
+ return *((ClientRenderHandle_t*)NULL);
+}
+
+
+//-----------------------------------------------------------------------------
+// Render setup
+//-----------------------------------------------------------------------------
+bool CDetailModel::SetupBones( matrix3x4_t *pBoneToWorldOut, int nMaxBones, int boneMask, float currentTime )
+{
+ if (!m_pModel)
+ return false;
+
+ // Setup our transform.
+ matrix3x4_t parentTransform;
+ const QAngle &vRenderAngles = GetRenderAngles();
+ const Vector &vRenderOrigin = GetRenderOrigin();
+ AngleMatrix( vRenderAngles, parentTransform );
+ parentTransform[0][3] = vRenderOrigin.x;
+ parentTransform[1][3] = vRenderOrigin.y;
+ parentTransform[2][3] = vRenderOrigin.z;
+
+ // Just copy it on down baby
+ studiohdr_t *pStudioHdr = modelinfo->GetStudiomodel( m_pModel );
+ for (int i = 0; i < pStudioHdr->numbones; i++)
+ {
+ MatrixCopy( parentTransform, pBoneToWorldOut[i] );
+ }
+
+ return true;
+}
+
+void CDetailModel::SetupWeights( const matrix3x4_t *pBoneToWorld, int nFlexWeightCount, float *pFlexWeights, float *pFlexDelayedWeights )
+{
+}
+
+void CDetailModel::DoAnimationEvents( void )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Render baby!
+//-----------------------------------------------------------------------------
+const model_t* CDetailModel::GetModel( ) const
+{
+ return m_pModel;
+}
+
+int CDetailModel::DrawModel( int flags )
+{
+ if ((m_Alpha == 0) || (!m_pModel))
+ return 0;
+
+ int drawn = modelrender->DrawModel(
+ flags,
+ this,
+ MODEL_INSTANCE_INVALID,
+ -1, // no entity index
+ m_pModel,
+ m_Origin,
+ m_Angles,
+ 0, // skin
+ 0, // body
+ 0 // hitboxset
+ );
+ return drawn;
+}
+
+
+//-----------------------------------------------------------------------------
+// Determine alpha and blend amount for transparent objects based on render state info
+//-----------------------------------------------------------------------------
+void CDetailModel::ComputeFxBlend( )
+{
+ // Do nothing, it's already calculate in our m_Alpha
+}
+
+int CDetailModel::GetFxBlend( )
+{
+ return m_Alpha;
+}
+
+//-----------------------------------------------------------------------------
+// Detail models stuff
+//-----------------------------------------------------------------------------
+CDetailModel::CDetailModel()
+{
+ m_Color.r = m_Color.g = m_Color.b = 255;
+ m_Color.exponent = 0;
+ m_bFlipped = 0;
+ m_bHasLightStyle = 0;
+
+#ifdef USE_DETAIL_SHAPES
+ m_pAdvInfo = NULL;
+#endif
+}
+
+//-----------------------------------------------------------------------------
+// Destructor
+//-----------------------------------------------------------------------------
+CDetailModel::~CDetailModel()
+{
+#ifdef USE_DETAIL_SHAPES
+ // delete advanced
+ if ( m_pAdvInfo )
+ {
+ delete m_pAdvInfo;
+ m_pAdvInfo = NULL;
+ }
+#endif
+
+ if ( m_bHasLightStyle )
+ gm_LightStylesMap.Remove( this );
+}
+
+
+//-----------------------------------------------------------------------------
+// Initialization
+//-----------------------------------------------------------------------------
+bool CDetailModel::Init( int index, const Vector& org, const QAngle& angles,
+ model_t* pModel, ColorRGBExp32 lighting, int lightstyle, unsigned char lightstylecount,
+ int orientation)
+{
+ m_Color = lighting;
+ if ( lightstylecount > 0)
+ {
+ m_bHasLightStyle = 1;
+ int iInfo = gm_LightStylesMap.Insert( this );
+ if ( lightstyle >= 0x1000000 || lightstylecount >= 100 )
+ Error( "Light style overflow\n" );
+ gm_LightStylesMap[iInfo].m_LightStyle = lightstyle;
+ gm_LightStylesMap[iInfo].m_LightStyleCount = lightstylecount;
+ }
+ m_Orientation = orientation;
+ m_Type = DETAIL_PROP_TYPE_MODEL;
+ m_pModel = pModel;
+ return InitCommon( index, org, angles );
+}
+
+bool CDetailModel::InitSprite( int index, bool bFlipped, const Vector& org, const QAngle& angles, unsigned short nSpriteIndex,
+ ColorRGBExp32 lighting, int lightstyle, unsigned char lightstylecount, int orientation, float flScale,
+ unsigned char type, unsigned char shapeAngle, unsigned char shapeSize, unsigned char swayAmount )
+{
+ m_Color = lighting;
+ if ( lightstylecount > 0)
+ {
+ m_bHasLightStyle = 1;
+ int iInfo = gm_LightStylesMap.Insert( this );
+ if ( lightstyle >= 0x1000000 || lightstylecount >= 100 )
+ Error( "Light style overflow\n" );
+ gm_LightStylesMap[iInfo].m_LightStyle = lightstyle;
+ gm_LightStylesMap[iInfo].m_LightStyleCount = lightstylecount;
+ }
+ m_Orientation = orientation;
+ m_SpriteInfo.m_nSpriteIndex = nSpriteIndex;
+ m_Type = type;
+ m_SpriteInfo.m_flScale.SetFloat( flScale );
+
+#ifdef USE_DETAIL_SHAPES
+ m_pAdvInfo = NULL;
+ Assert( type <= 3 );
+ // precalculate angles for shapes
+ if ( type == DETAIL_PROP_TYPE_SHAPE_TRI || type == DETAIL_PROP_TYPE_SHAPE_CROSS || swayAmount > 0 )
+ {
+ m_Angles = angles;
+ InitShapedSprite( shapeAngle, shapeSize, swayAmount);
+ }
+
+#endif
+
+ m_bFlipped = bFlipped;
+ return InitCommon( index, org, angles );
+}
+
+#ifdef USE_DETAIL_SHAPES
+void CDetailModel::InitShapedSprite( unsigned char shapeAngle, unsigned char shapeSize, unsigned char swayAmount )
+{
+ // Set up pointer to advanced shape properties object ( per instance )
+ Assert( m_pAdvInfo == NULL );
+ m_pAdvInfo = new DetailModelAdvInfo_t;
+ Assert( m_pAdvInfo );
+
+ if ( m_pAdvInfo )
+ {
+ m_pAdvInfo->m_iShapeAngle = shapeAngle;
+ m_pAdvInfo->m_flSwayAmount = (float)swayAmount / 255.0f;
+ m_pAdvInfo->m_flShapeSize = (float)shapeSize / 255.0f;
+ m_pAdvInfo->m_vecCurrentAvoid = vec3_origin;
+ m_pAdvInfo->m_flSwayYaw = random->RandomFloat( 0, 180 );
+ }
+
+ switch ( m_Type )
+ {
+ case DETAIL_PROP_TYPE_SHAPE_TRI:
+ InitShapeTri();
+ break;
+
+ case DETAIL_PROP_TYPE_SHAPE_CROSS:
+ InitShapeCross();
+ break;
+
+ default: // sprite will get here
+ break;
+ }
+}
+
+void CDetailModel::InitShapeTri( void )
+{
+ // store the three sets of directions
+ matrix3x4_t matrix;
+
+ // Convert roll/pitch only to matrix
+ AngleMatrix( m_Angles, matrix );
+
+ // calculate the vectors for the three sides so they can be used in the sorting test
+ // as well as in drawing
+ for ( int i=0; i<3; i++ )
+ {
+ // Convert desired rotation to angles
+ QAngle anglesRotated( m_pAdvInfo->m_iShapeAngle, i*120, 0 );
+
+ Vector rotForward, rotRight, rotUp;
+ AngleVectors( anglesRotated, &rotForward, &rotRight, &rotUp );
+
+ // Rotate direction vectors
+ VectorRotate( rotForward, matrix, m_pAdvInfo->m_vecAnglesForward[i] );
+ VectorRotate( rotRight, matrix, m_pAdvInfo->m_vecAnglesRight[i] );
+ VectorRotate( rotUp, matrix, m_pAdvInfo->m_vecAnglesUp[i] );
+ }
+}
+
+void CDetailModel::InitShapeCross( void )
+{
+ AngleVectors( m_Angles,
+ &m_pAdvInfo->m_vecAnglesForward[0],
+ &m_pAdvInfo->m_vecAnglesRight[0],
+ &m_pAdvInfo->m_vecAnglesUp[0] );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// Color, alpha modulation
+//-----------------------------------------------------------------------------
+void CDetailModel::GetColorModulation( float *color )
+{
+ if (mat_fullbright.GetInt() == 1)
+ {
+ color[0] = color[1] = color[2] = 1.0f;
+ return;
+ }
+
+ Vector tmp;
+ Vector normal( 1, 0, 0);
+ engine->ComputeDynamicLighting( m_Origin, &normal, tmp );
+
+ float val = engine->LightStyleValue( 0 );
+ color[0] = tmp[0] + val * TexLightToLinear( m_Color.r, m_Color.exponent );
+ color[1] = tmp[1] + val * TexLightToLinear( m_Color.g, m_Color.exponent );
+ color[2] = tmp[2] + val * TexLightToLinear( m_Color.b, m_Color.exponent );
+
+ // Add in the lightstyles
+ if ( m_bHasLightStyle )
+ {
+ int iInfo = gm_LightStylesMap.Find( this );
+ Assert( iInfo != gm_LightStylesMap.InvalidIndex() );
+ if ( iInfo != gm_LightStylesMap.InvalidIndex() )
+ {
+ int nLightStyles = gm_LightStylesMap[iInfo].m_LightStyleCount;
+ int iLightStyle = gm_LightStylesMap[iInfo].m_LightStyle;
+ for (int i = 0; i < nLightStyles; ++i)
+ {
+ DetailPropLightstylesLump_t& lighting = s_DetailObjectSystem.DetailLighting( iLightStyle + i );
+ val = engine->LightStyleValue( lighting.m_Style );
+ if (val != 0)
+ {
+ color[0] += val * TexLightToLinear( lighting.m_Lighting.r, lighting.m_Lighting.exponent );
+ color[1] += val * TexLightToLinear( lighting.m_Lighting.g, lighting.m_Lighting.exponent );
+ color[2] += val * TexLightToLinear( lighting.m_Lighting.b, lighting.m_Lighting.exponent );
+ }
+ }
+ }
+ }
+
+ // Gamma correct....
+ engine->LinearToGamma( color, color );
+}
+
+
+//-----------------------------------------------------------------------------
+// Is the model itself translucent, regardless of modulation?
+//-----------------------------------------------------------------------------
+bool CDetailModel::IsDetailModelTranslucent()
+{
+ // FIXME: This is only true for my first pass of this feature
+ if (m_Type >= DETAIL_PROP_TYPE_SPRITE)
+ return true;
+
+ return modelinfo->IsTranslucent(GetModel());
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes the render angles for screen alignment
+//-----------------------------------------------------------------------------
+void CDetailModel::ComputeAngles( void )
+{
+ switch( m_Orientation )
+ {
+ case 0:
+ break;
+
+ case 1:
+ {
+ Vector vecDir;
+ VectorSubtract( CurrentViewOrigin(), m_Origin, vecDir );
+ VectorAngles( vecDir, m_Angles );
+ }
+ break;
+
+ case 2:
+ {
+ Vector vecDir;
+ VectorSubtract( CurrentViewOrigin(), m_Origin, vecDir );
+ vecDir.z = 0.0f;
+ VectorAngles( vecDir, m_Angles );
+ }
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Select which rendering func to call
+//-----------------------------------------------------------------------------
+void CDetailModel::DrawSprite( CMeshBuilder &meshBuilder )
+{
+ switch( m_Type )
+ {
+#ifdef USE_DETAIL_SHAPES
+ case DETAIL_PROP_TYPE_SHAPE_CROSS:
+ DrawTypeShapeCross( meshBuilder );
+ break;
+
+ case DETAIL_PROP_TYPE_SHAPE_TRI:
+ DrawTypeShapeTri( meshBuilder );
+ break;
+#endif
+ case DETAIL_PROP_TYPE_SPRITE:
+ DrawTypeSprite( meshBuilder );
+ break;
+
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the single sprite type
+//-----------------------------------------------------------------------------
+void CDetailModel::DrawTypeSprite( CMeshBuilder &meshBuilder )
+{
+ Assert( m_Type == DETAIL_PROP_TYPE_SPRITE );
+
+ Vector vecColor;
+ GetColorModulation( vecColor.Base() );
+
+ unsigned char color[4];
+ color[0] = (unsigned char)(vecColor[0] * 255.0f);
+ color[1] = (unsigned char)(vecColor[1] * 255.0f);
+ color[2] = (unsigned char)(vecColor[2] * 255.0f);
+ color[3] = m_Alpha;
+
+ DetailPropSpriteDict_t &dict = s_DetailObjectSystem.DetailSpriteDict( m_SpriteInfo.m_nSpriteIndex );
+
+ Vector vecOrigin, dx, dy;
+ AngleVectors( m_Angles, NULL, &dx, &dy );
+
+ Vector2D ul, lr;
+ float scale = m_SpriteInfo.m_flScale.GetFloat();
+ Vector2DMultiply( dict.m_UL, scale, ul );
+ Vector2DMultiply( dict.m_LR, scale, lr );
+
+#ifdef USE_DETAIL_SHAPES
+ UpdatePlayerAvoid();
+
+ Vector vecSway = vec3_origin;
+
+ if ( m_pAdvInfo )
+ {
+ vecSway = m_pAdvInfo->m_vecCurrentAvoid * m_SpriteInfo.m_flScale.GetFloat();
+ float flSwayAmplitude = m_pAdvInfo->m_flSwayAmount * cl_detail_max_sway.GetFloat();
+ if ( flSwayAmplitude > 0 )
+ {
+ // sway based on time plus a random seed that is constant for this instance of the sprite
+ vecSway += dx * sin(gpGlobals->curtime+m_Origin.x) * flSwayAmplitude;
+ }
+ }
+#endif
+
+ VectorMA( m_Origin, ul.x, dx, vecOrigin );
+ VectorMA( vecOrigin, ul.y, dy, vecOrigin );
+ dx *= (lr.x - ul.x);
+ dy *= (lr.y - ul.y);
+
+ Vector2D texul, texlr;
+ texul = dict.m_TexUL;
+ texlr = dict.m_TexLR;
+
+ if ( !m_bFlipped )
+ {
+ texul.x = dict.m_TexLR.x;
+ texlr.x = dict.m_TexUL.x;
+ }
+
+#ifndef USE_DETAIL_SHAPES
+ meshBuilder.Position3fv( vecOrigin.Base() );
+#else
+ meshBuilder.Position3fv( (vecOrigin+vecSway).Base() );
+#endif
+
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2fv( 0, texul.Base() );
+ meshBuilder.AdvanceVertex();
+
+ vecOrigin += dy;
+ meshBuilder.Position3fv( vecOrigin.Base() );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, texul.x, texlr.y );
+ meshBuilder.AdvanceVertex();
+
+ vecOrigin += dx;
+ meshBuilder.Position3fv( vecOrigin.Base() );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2fv( 0, texlr.Base() );
+ meshBuilder.AdvanceVertex();
+
+ vecOrigin -= dy;
+#ifndef USE_DETAIL_SHAPES
+ meshBuilder.Position3fv( vecOrigin.Base() );
+#else
+ meshBuilder.Position3fv( (vecOrigin+vecSway).Base() );
+#endif
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, texlr.x, texul.y );
+ meshBuilder.AdvanceVertex();
+}
+
+//-----------------------------------------------------------------------------
+// draws a procedural model, cross shape
+// two perpendicular sprites
+//-----------------------------------------------------------------------------
+#ifdef USE_DETAIL_SHAPES
+void CDetailModel::DrawTypeShapeCross( CMeshBuilder &meshBuilder )
+{
+ Assert( m_Type == DETAIL_PROP_TYPE_SHAPE_CROSS );
+
+ Vector vecColor;
+ GetColorModulation( vecColor.Base() );
+
+ unsigned char color[4];
+ color[0] = (unsigned char)(vecColor[0] * 255.0f);
+ color[1] = (unsigned char)(vecColor[1] * 255.0f);
+ color[2] = (unsigned char)(vecColor[2] * 255.0f);
+ color[3] = m_Alpha;
+
+ DetailPropSpriteDict_t &dict = s_DetailObjectSystem.DetailSpriteDict( m_SpriteInfo.m_nSpriteIndex );
+
+ Vector2D texul, texlr;
+ texul = dict.m_TexUL;
+ texlr = dict.m_TexLR;
+
+ // What a shameless re-use of bits (m_pModel == 0 when it should be flipped horizontally)
+ if ( !m_pModel )
+ {
+ texul.x = dict.m_TexLR.x;
+ texlr.x = dict.m_TexUL.x;
+ }
+
+ Vector2D texumid, texlmid;
+ texumid.y = texul.y;
+ texlmid.y = texlr.y;
+ texumid.x = texlmid.x = ( texul.x + texlr.x ) / 2;
+
+ Vector2D texll;
+ texll.x = texul.x;
+ texll.y = texlr.y;
+
+ Vector2D ul, lr;
+ float flScale = m_SpriteInfo.m_flScale.GetFloat();
+ Vector2DMultiply( dict.m_UL, flScale, ul );
+ Vector2DMultiply( dict.m_LR, flScale, lr );
+
+ float flSizeX = ( lr.x - ul.x ) / 2;
+ float flSizeY = ( lr.y - ul.y );
+
+ UpdatePlayerAvoid();
+
+ // sway based on time plus a random seed that is constant for this instance of the sprite
+ Vector vecSway = ( m_pAdvInfo->m_vecCurrentAvoid * flSizeX * 2 );
+ float flSwayAmplitude = m_pAdvInfo->m_flSwayAmount * cl_detail_max_sway.GetFloat();
+ if ( flSwayAmplitude > 0 )
+ {
+ vecSway += UTIL_YawToVector( m_pAdvInfo->m_flSwayYaw ) * sin(gpGlobals->curtime+m_Origin.x) * flSwayAmplitude;
+ }
+
+ Vector vecOrigin;
+ VectorMA( m_Origin, ul.y, m_pAdvInfo->m_vecAnglesUp[0], vecOrigin );
+
+ Vector forward, right, up;
+ forward = m_pAdvInfo->m_vecAnglesForward[0] * flSizeX;
+ right = m_pAdvInfo->m_vecAnglesRight[0] * flSizeX;
+ up = m_pAdvInfo->m_vecAnglesUp[0] * flSizeY;
+
+ // figure out drawing order so the branches sort properly
+ // do dot products with the forward and right vectors to determine the quadrant the viewer is in
+ // assume forward points North , right points East
+ /*
+ N
+ |
+ 3 | 0
+ W---------E
+ 2 | 1
+ |
+ S
+ */
+ // eg if they are in quadrant 0, set iBranch to 0, and the draw order will be
+ // 0, 1, 2, 3, or South, west, north, east
+ Vector viewOffset = CurrentViewOrigin() - m_Origin;
+ bool bForward = ( DotProduct( forward, viewOffset ) > 0 );
+ bool bRight = ( DotProduct( right, viewOffset ) > 0 );
+ int iBranch = bForward ? ( bRight ? 0 : 3 ) : ( bRight ? 1 : 2 );
+
+ //debugoverlay->AddLineOverlay( m_Origin, m_Origin + right * 20, 255, 0, 0, true, 0.01 );
+ //debugoverlay->AddLineOverlay( m_Origin, m_Origin + forward * 20, 0, 0, 255, true, 0.01 );
+
+ int iDrawn = 0;
+ while( iDrawn < 4 )
+ {
+ switch( iBranch )
+ {
+ case 0: // south
+ DrawSwayingQuad( meshBuilder, vecOrigin, vecSway, texumid, texlr, color, -forward, up );
+ break;
+ case 1: // west
+ DrawSwayingQuad( meshBuilder, vecOrigin, vecSway, texumid, texll, color, -right, up );
+ break;
+ case 2: // north
+ DrawSwayingQuad( meshBuilder, vecOrigin, vecSway, texumid, texll, color, forward, up );
+ break;
+ case 3: // east
+ DrawSwayingQuad( meshBuilder, vecOrigin, vecSway, texumid, texlr, color, right, up );
+ break;
+ }
+
+ iDrawn++;
+ iBranch++;
+ if ( iBranch > 3 )
+ iBranch = 0;
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// draws a procedural model, tri shape
+//-----------------------------------------------------------------------------
+#ifdef USE_DETAIL_SHAPES
+void CDetailModel::DrawTypeShapeTri( CMeshBuilder &meshBuilder )
+{
+ Assert( m_Type == DETAIL_PROP_TYPE_SHAPE_TRI );
+
+ Vector vecColor;
+ GetColorModulation( vecColor.Base() );
+
+ unsigned char color[4];
+ color[0] = (unsigned char)(vecColor[0] * 255.0f);
+ color[1] = (unsigned char)(vecColor[1] * 255.0f);
+ color[2] = (unsigned char)(vecColor[2] * 255.0f);
+ color[3] = m_Alpha;
+
+ DetailPropSpriteDict_t &dict = s_DetailObjectSystem.DetailSpriteDict( m_SpriteInfo.m_nSpriteIndex );
+
+ Vector2D texul, texlr;
+ texul = dict.m_TexUL;
+ texlr = dict.m_TexLR;
+
+ // What a shameless re-use of bits (m_pModel == 0 when it should be flipped horizontally)
+ if ( !m_pModel )
+ {
+ texul.x = dict.m_TexLR.x;
+ texlr.x = dict.m_TexUL.x;
+ }
+
+ Vector2D ul, lr;
+ float flScale = m_SpriteInfo.m_flScale.GetFloat();
+ Vector2DMultiply( dict.m_UL, flScale, ul );
+ Vector2DMultiply( dict.m_LR, flScale, lr );
+
+ // sort the sides relative to the view origin
+ Vector viewOffset = CurrentViewOrigin() - m_Origin;
+
+ // three sides, A, B, C, counter-clockwise from A is the unrotated side
+ bool bOutsideA = DotProduct( m_pAdvInfo->m_vecAnglesForward[0], viewOffset ) > 0;
+ bool bOutsideB = DotProduct( m_pAdvInfo->m_vecAnglesForward[1], viewOffset ) > 0;
+ bool bOutsideC = DotProduct( m_pAdvInfo->m_vecAnglesForward[2], viewOffset ) > 0;
+
+ int iBranch = 0;
+ if ( bOutsideA && !bOutsideB )
+ iBranch = 1;
+ else if ( bOutsideB && !bOutsideC )
+ iBranch = 2;
+
+ float flHeight, flWidth;
+ flHeight = (lr.y - ul.y);
+ flWidth = (lr.x - ul.x);
+
+ Vector vecSway;
+ Vector vecOrigin;
+ Vector vecHeight, vecWidth;
+
+ UpdatePlayerAvoid();
+
+ Vector vecSwayYaw = UTIL_YawToVector( m_pAdvInfo->m_flSwayYaw );
+ float flSwayAmplitude = m_pAdvInfo->m_flSwayAmount * cl_detail_max_sway.GetFloat();
+
+ int iDrawn = 0;
+ while( iDrawn < 3 )
+ {
+ vecHeight = m_pAdvInfo->m_vecAnglesUp[iBranch] * flHeight;
+ vecWidth = m_pAdvInfo->m_vecAnglesRight[iBranch] * flWidth;
+
+ VectorMA( m_Origin, ul.x, m_pAdvInfo->m_vecAnglesRight[iBranch], vecOrigin );
+ VectorMA( vecOrigin, ul.y, m_pAdvInfo->m_vecAnglesUp[iBranch], vecOrigin );
+ VectorMA( vecOrigin, m_pAdvInfo->m_flShapeSize*flWidth, m_pAdvInfo->m_vecAnglesForward[iBranch], vecOrigin );
+
+ // sway is calculated per side so they don't sway exactly the same
+ Vector vecSway = ( m_pAdvInfo->m_vecCurrentAvoid * flWidth ) +
+ vecSwayYaw * sin(gpGlobals->curtime+m_Origin.x+iBranch) * flSwayAmplitude;
+
+ DrawSwayingQuad( meshBuilder, vecOrigin, vecSway, texul, texlr, color, vecWidth, vecHeight );
+
+ iDrawn++;
+ iBranch++;
+ if ( iBranch > 2 )
+ iBranch = 0;
+ }
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// checks for nearby players and pushes the detail to the side
+//-----------------------------------------------------------------------------
+#ifdef USE_DETAIL_SHAPES
+void CDetailModel::UpdatePlayerAvoid( void )
+{
+ float flForce = cl_detail_avoid_force.GetFloat();
+
+ if ( flForce < 0.1 )
+ return;
+
+ if ( m_pAdvInfo == NULL )
+ return;
+
+ // get players in a radius
+ float flRadius = cl_detail_avoid_radius.GetFloat();
+ float flRecoverSpeed = cl_detail_avoid_recover_speed.GetFloat();
+
+ Vector vecAvoid;
+ C_BaseEntity *pEnt;
+
+ float flMaxForce = 0;
+ Vector vecMaxAvoid(0,0,0);
+
+ CPlayerEnumerator avoid( flRadius, m_Origin );
+ ::partition->EnumerateElementsInSphere( PARTITION_CLIENT_SOLID_EDICTS, m_Origin, flRadius, false, &avoid );
+
+ // Okay, decide how to avoid if there's anything close by
+ int c = avoid.GetObjectCount();
+ for ( int i=0; i<c+1; i++ ) // +1 for the local player we tack on the end
+ {
+ if ( i == c )
+ {
+ pEnt = C_BasePlayer::GetLocalPlayer();
+ if ( !pEnt ) continue;
+ }
+ else
+ pEnt = avoid.GetObject( i );
+
+ vecAvoid = m_Origin - pEnt->GetAbsOrigin();
+ vecAvoid.z = 0;
+
+ float flDist = vecAvoid.Length2D();
+
+ if ( flDist > flRadius )
+ continue;
+
+ float flForceScale = RemapValClamped( flDist, 0, flRadius, flForce, 0.0 );
+
+ if ( flForceScale > flMaxForce )
+ {
+ flMaxForce = flForceScale;
+ vecAvoid.NormalizeInPlace();
+ vecAvoid *= flMaxForce;
+ vecMaxAvoid = vecAvoid;
+ }
+ }
+
+ // if we are being moved, move fast. Else we recover at a slow rate
+ if ( vecMaxAvoid.Length2D() > m_pAdvInfo->m_vecCurrentAvoid.Length2D() )
+ flRecoverSpeed = 10; // fast approach
+
+ m_pAdvInfo->m_vecCurrentAvoid[0] = Approach( vecMaxAvoid[0], m_pAdvInfo->m_vecCurrentAvoid[0], flRecoverSpeed );
+ m_pAdvInfo->m_vecCurrentAvoid[1] = Approach( vecMaxAvoid[1], m_pAdvInfo->m_vecCurrentAvoid[1], flRecoverSpeed );
+ m_pAdvInfo->m_vecCurrentAvoid[2] = Approach( vecMaxAvoid[2], m_pAdvInfo->m_vecCurrentAvoid[2], flRecoverSpeed );
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// draws a quad that sways on the top two vertices
+// pass vecOrigin as the top left vertex position
+//-----------------------------------------------------------------------------
+#ifdef USE_DETAIL_SHAPES
+void CDetailModel::DrawSwayingQuad( CMeshBuilder &meshBuilder, Vector vecOrigin, Vector vecSway, Vector2D texul, Vector2D texlr, unsigned char *color,
+ Vector width, Vector height )
+{
+ meshBuilder.Position3fv( (vecOrigin + vecSway).Base() );
+ meshBuilder.TexCoord2fv( 0, texul.Base() );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.AdvanceVertex();
+
+ vecOrigin += height;
+ meshBuilder.Position3fv( vecOrigin.Base() );
+ meshBuilder.TexCoord2f( 0, texul.x, texlr.y );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.AdvanceVertex();
+
+ vecOrigin += width;
+ meshBuilder.Position3fv( vecOrigin.Base() );
+ meshBuilder.TexCoord2fv( 0, texlr.Base() );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.AdvanceVertex();
+
+ vecOrigin -= height;
+ meshBuilder.Position3fv( (vecOrigin + vecSway).Base() );
+ meshBuilder.TexCoord2f( 0, texlr.x, texul.y );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.AdvanceVertex();
+}
+#endif
+
+//-----------------------------------------------------------------------------
+// constructor, destructor
+//-----------------------------------------------------------------------------
+CDetailObjectSystem::CDetailObjectSystem() : m_DetailSpriteDict( 0, 32 ), m_DetailObjectDict( 0, 32 ), m_DetailSpriteDictFlipped( 0, 32 )
+{
+ m_pFastSpriteData = NULL;
+ m_pSortInfo = NULL;
+ m_pFastSortInfo = NULL;
+ m_pBuildoutBuffer = NULL;
+}
+
+void CDetailObjectSystem::FreeSortBuffers( void )
+{
+ if ( m_pSortInfo )
+ {
+ MemAlloc_FreeAligned( m_pSortInfo );
+ m_pSortInfo = NULL;
+ }
+ if ( m_pFastSortInfo )
+ {
+ MemAlloc_FreeAligned( m_pFastSortInfo );
+ m_pFastSortInfo = NULL;
+ }
+ if ( m_pBuildoutBuffer )
+ {
+ MemAlloc_FreeAligned( m_pBuildoutBuffer );
+ m_pBuildoutBuffer = NULL;
+ }
+}
+
+CDetailObjectSystem::~CDetailObjectSystem()
+{
+ if ( m_pFastSpriteData )
+ {
+ MemAlloc_FreeAligned( m_pFastSpriteData );
+ m_pFastSpriteData = NULL;
+ }
+ FreeSortBuffers();
+
+}
+
+
+//-----------------------------------------------------------------------------
+// Level init, shutdown
+//-----------------------------------------------------------------------------
+void CDetailObjectSystem::LevelInitPreEntity()
+{
+ // Prepare the translucent detail sprite material; we only have 1!
+ m_DetailSpriteMaterial.Init( "detail/detailsprites", TEXTURE_GROUP_OTHER );
+ m_DetailWireframeMaterial.Init( "debug/debugspritewireframe", TEXTURE_GROUP_OTHER );
+
+ // Version check
+ if (engine->GameLumpVersion( GAMELUMP_DETAIL_PROPS ) < 4)
+ {
+ Warning("Map uses old detail prop file format.. ignoring detail props\n");
+ return;
+ }
+
+ MEM_ALLOC_CREDIT();
+
+ // Unserialize
+ int size = engine->GameLumpSize( GAMELUMP_DETAIL_PROPS );
+ CUtlMemory<unsigned char> fileMemory;
+ fileMemory.EnsureCapacity( size );
+ if (engine->LoadGameLump( GAMELUMP_DETAIL_PROPS, fileMemory.Base(), size ))
+ {
+ CUtlBuffer buf( fileMemory.Base(), size, CUtlBuffer::READ_ONLY );
+ UnserializeModelDict( buf );
+
+ switch (engine->GameLumpVersion( GAMELUMP_DETAIL_PROPS ) )
+ {
+ case 4:
+ UnserializeDetailSprites( buf );
+ UnserializeModels( buf );
+ break;
+ }
+ }
+
+ if ( m_DetailObjects.Count() || m_DetailSpriteDict.Count() )
+ {
+ // There are detail objects in the level, so precache the material
+ PrecacheMaterial( DETAIL_SPRITE_MATERIAL );
+ IMaterial *pMat = m_DetailSpriteMaterial;
+ // adjust for non-square textures (cropped)
+ float flRatio = pMat->GetMappingWidth() / pMat->GetMappingHeight();
+ if ( flRatio > 1.0 )
+ {
+ for( int i = 0; i<m_DetailSpriteDict.Count(); i++ )
+ {
+ m_DetailSpriteDict[i].m_TexUL.y *= flRatio;
+ m_DetailSpriteDict[i].m_TexLR.y *= flRatio;
+ m_DetailSpriteDictFlipped[i].m_TexUL.y *= flRatio;
+ m_DetailSpriteDictFlipped[i].m_TexLR.y *= flRatio;
+ }
+ }
+ }
+
+ int detailPropLightingLump;
+ if( g_pMaterialSystemHardwareConfig->GetHDRType() != HDR_TYPE_NONE )
+ {
+ detailPropLightingLump = GAMELUMP_DETAIL_PROP_LIGHTING_HDR;
+ }
+ else
+ {
+ detailPropLightingLump = GAMELUMP_DETAIL_PROP_LIGHTING;
+ }
+ size = engine->GameLumpSize( detailPropLightingLump );
+
+ fileMemory.EnsureCapacity( size );
+ if (engine->LoadGameLump( detailPropLightingLump, fileMemory.Base(), size ))
+ {
+ CUtlBuffer buf( fileMemory.Base(), size, CUtlBuffer::READ_ONLY );
+ UnserializeModelLighting( buf );
+ }
+}
+
+
+void CDetailObjectSystem::LevelInitPostEntity()
+{
+ const char *pDetailSpriteMaterial = DETAIL_SPRITE_MATERIAL;
+ C_World *pWorld = GetClientWorldEntity();
+ if ( pWorld && pWorld->GetDetailSpriteMaterial() && *(pWorld->GetDetailSpriteMaterial()) )
+ {
+ pDetailSpriteMaterial = pWorld->GetDetailSpriteMaterial();
+ }
+ m_DetailSpriteMaterial.Init( pDetailSpriteMaterial, TEXTURE_GROUP_OTHER );
+
+ if ( GetDetailController() )
+ {
+ cl_detailfade.SetValue( MIN( m_flDefaultFadeStart, GetDetailController()->m_flFadeStartDist ) );
+ cl_detaildist.SetValue( MIN( m_flDefaultFadeEnd, GetDetailController()->m_flFadeEndDist ) );
+ }
+ else
+ {
+ // revert to default values if the map doesn't specify
+ cl_detailfade.SetValue( m_flDefaultFadeStart );
+ cl_detaildist.SetValue( m_flDefaultFadeEnd );
+ }
+}
+
+void CDetailObjectSystem::LevelShutdownPreEntity()
+{
+ m_DetailObjects.Purge();
+ m_DetailObjectDict.Purge();
+ m_DetailSpriteDict.Purge();
+ m_DetailSpriteDictFlipped.Purge();
+ m_DetailLighting.Purge();
+ m_DetailSpriteMaterial.Shutdown();
+ if ( m_pFastSpriteData )
+ {
+ MemAlloc_FreeAligned( m_pFastSpriteData );
+ m_pFastSpriteData = NULL;
+ }
+ FreeSortBuffers();
+
+}
+
+void CDetailObjectSystem::LevelShutdownPostEntity()
+{
+ m_DetailWireframeMaterial.Shutdown();
+}
+
+//-----------------------------------------------------------------------------
+// Before each view, blat out the stored detail sprite state
+//-----------------------------------------------------------------------------
+void CDetailObjectSystem::BeginTranslucentDetailRendering( )
+{
+ m_nSortedLeaf = -1;
+ m_nSortedFastLeaf = -1;
+ m_nSpriteCount = m_nFirstSprite = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets a particular detail object
+//-----------------------------------------------------------------------------
+IClientRenderable* CDetailObjectSystem::GetDetailModel( int idx )
+{
+ // FIXME: This is necessary because we have intermixed models + sprites
+ // in a single list (m_DetailObjects)
+ if (m_DetailObjects[idx].GetType() != DETAIL_PROP_TYPE_MODEL)
+ return NULL;
+
+ return &m_DetailObjects[idx];
+}
+
+
+//-----------------------------------------------------------------------------
+// Unserialization
+//-----------------------------------------------------------------------------
+void CDetailObjectSystem::UnserializeModelDict( CUtlBuffer& buf )
+{
+ int count = buf.GetInt();
+ m_DetailObjectDict.EnsureCapacity( count );
+ while ( --count >= 0 )
+ {
+ DetailObjectDictLump_t lump;
+ buf.Get( &lump, sizeof(DetailObjectDictLump_t) );
+
+ DetailModelDict_t dict;
+ dict.m_pModel = (model_t *)engine->LoadModel( lump.m_Name, true );
+
+ // Don't allow vertex-lit models
+ if (modelinfo->IsModelVertexLit(dict.m_pModel))
+ {
+ Warning("Detail prop model %s is using vertex-lit materials!\nIt must use unlit materials!\n", lump.m_Name );
+ dict.m_pModel = (model_t *)engine->LoadModel( "models/error.mdl" );
+ }
+
+ m_DetailObjectDict.AddToTail( dict );
+ }
+}
+
+void CDetailObjectSystem::UnserializeDetailSprites( CUtlBuffer& buf )
+{
+ int count = buf.GetInt();
+ m_DetailSpriteDict.EnsureCapacity( count );
+ m_DetailSpriteDictFlipped.EnsureCapacity( count );
+ while ( --count >= 0 )
+ {
+ int i = m_DetailSpriteDict.AddToTail();
+ buf.Get( &m_DetailSpriteDict[i], sizeof(DetailSpriteDictLump_t) );
+ int flipi = m_DetailSpriteDictFlipped.AddToTail();
+ m_DetailSpriteDictFlipped[flipi] = m_DetailSpriteDict[i];
+ ::V_swap( m_DetailSpriteDictFlipped[flipi].m_TexUL.x, m_DetailSpriteDictFlipped[flipi].m_TexLR.x );
+ }
+}
+
+
+void CDetailObjectSystem::UnserializeModelLighting( CUtlBuffer& buf )
+{
+ int count = buf.GetInt();
+ m_DetailLighting.EnsureCapacity( count );
+ while ( --count >= 0 )
+ {
+ int i = m_DetailLighting.AddToTail();
+ buf.Get( &m_DetailLighting[i], sizeof(DetailPropLightstylesLump_t) );
+ }
+}
+
+
+ConVar cl_detail_multiplier( "cl_detail_multiplier", "1", FCVAR_CHEAT, "extra details to create" );
+
+#define SPRITE_MULTIPLIER ( cl_detail_multiplier.GetInt() )
+
+ConVar cl_fastdetailsprites( "cl_fastdetailsprites", "1", FCVAR_CHEAT, "whether to use new detail sprite system");
+
+static bool DetailObjectIsFastSprite( DetailObjectLump_t const & lump )
+{
+ return (
+ ( cl_fastdetailsprites.GetInt() ) &&
+ ( lump.m_Type == DETAIL_PROP_TYPE_SPRITE ) &&
+ ( lump.m_LightStyleCount == 0 ) &&
+ ( lump.m_Orientation == 2 ) &&
+ ( lump.m_ShapeAngle == 0 ) &&
+ ( lump.m_ShapeSize == 0 ) &&
+ ( lump.m_SwayAmount == 0 ) );
+}
+
+
+void CDetailObjectSystem::ScanForCounts( CUtlBuffer& buf,
+ int *pNumOldStyleObjects,
+ int *pNumFastSpritesToAllocate,
+ int *nMaxNumOldSpritesInLeaf,
+ int *nMaxNumFastSpritesInLeaf
+ ) const
+{
+ int oldpos = buf.TellGet(); // we need to seek back
+ int count = buf.GetInt();
+
+ int nOld = 0;
+ int nFast = 0;
+ int detailObjectLeaf = -1;
+
+ int nNumOldInLeaf = 0;
+ int nNumFastInLeaf = 0;
+ int nMaxOld = 0;
+ int nMaxFast = 0;
+ while ( --count >= 0 )
+ {
+ DetailObjectLump_t lump;
+ buf.Get( &lump, sizeof(DetailObjectLump_t) );
+
+ // We rely on the fact that details objects are sorted by leaf in the
+ // bsp file for this
+ if ( detailObjectLeaf != lump.m_Leaf )
+ {
+ // need to pad nfast to next sse boundary
+ nFast += ( 0 - nFast ) & 3;
+ nMaxFast = MAX( nMaxFast, nNumFastInLeaf );
+ nMaxOld = MAX( nMaxOld, nNumOldInLeaf );
+ nNumOldInLeaf = 0;
+ nNumFastInLeaf = 0;
+ detailObjectLeaf = lump.m_Leaf;
+
+ }
+
+ if ( DetailObjectIsFastSprite( lump ) )
+ {
+ nFast += SPRITE_MULTIPLIER;
+ nNumFastInLeaf += SPRITE_MULTIPLIER;
+ }
+ else
+ {
+ nOld += SPRITE_MULTIPLIER;
+ nNumOldInLeaf += SPRITE_MULTIPLIER;
+ }
+ }
+
+ // need to pad nfast to next sse boundary
+ nFast += ( 0 - nFast ) & 3;
+ nMaxFast = MAX( nMaxFast, nNumFastInLeaf );
+ nMaxOld = MAX( nMaxOld, nNumOldInLeaf );
+
+ buf.SeekGet( CUtlBuffer::SEEK_HEAD, oldpos );
+ *pNumFastSpritesToAllocate = nFast;
+ *pNumOldStyleObjects = nOld;
+ nMaxFast = ( 3 + nMaxFast ) & ~3;
+ *nMaxNumOldSpritesInLeaf = nMaxOld;
+ *nMaxNumFastSpritesInLeaf = nMaxFast;
+
+}
+
+//-----------------------------------------------------------------------------
+// Unserialize all models
+//-----------------------------------------------------------------------------
+void CDetailObjectSystem::UnserializeModels( CUtlBuffer& buf )
+{
+ int firstDetailObject = 0;
+ int detailObjectCount = 0;
+ int detailObjectLeaf = -1;
+
+ int nNumOldStyleObjects;
+ int nNumFastSpritesToAllocate;
+ int nMaxOldInLeaf;
+ int nMaxFastInLeaf;
+ ScanForCounts( buf, &nNumOldStyleObjects, &nNumFastSpritesToAllocate, &nMaxOldInLeaf, &nMaxFastInLeaf );
+
+ FreeSortBuffers();
+
+ if ( nMaxOldInLeaf )
+ {
+ m_pSortInfo = reinterpret_cast<SortInfo_t *> (
+ MemAlloc_AllocAligned( (3 + nMaxOldInLeaf ) * sizeof( SortInfo_t ), sizeof( fltx4 ) ) );
+ }
+ if ( nMaxFastInLeaf )
+ {
+ m_pFastSortInfo = reinterpret_cast<SortInfo_t *> (
+ MemAlloc_AllocAligned( (3 + nMaxFastInLeaf ) * sizeof( SortInfo_t ), sizeof( fltx4 ) ) );
+
+ m_pBuildoutBuffer = reinterpret_cast<FastSpriteQuadBuildoutBufferX4_t *> (
+ MemAlloc_AllocAligned(
+ ( 1 + nMaxFastInLeaf / 4 ) * sizeof( FastSpriteQuadBuildoutBufferX4_t ),
+ sizeof( fltx4 ) ) );
+ }
+
+ if ( nNumFastSpritesToAllocate )
+ {
+ Assert( ( nNumFastSpritesToAllocate & 3 ) == 0 );
+ Assert( ! m_pFastSpriteData ); // wtf? didn't free?
+ m_pFastSpriteData = reinterpret_cast<FastSpriteX4_t *> (
+ MemAlloc_AllocAligned(
+ ( nNumFastSpritesToAllocate >> 2 ) * sizeof( FastSpriteX4_t ),
+ sizeof( fltx4 ) ) );
+ }
+
+ m_DetailObjects.EnsureCapacity( nNumOldStyleObjects );
+
+ int count = buf.GetInt();
+
+ int nCurFastObject = 0;
+ int nNumFastObjectsInCurLeaf = 0;
+ FastSpriteX4_t *pCurFastSpriteOut = m_pFastSpriteData;
+
+ bool bFlipped = true;
+ while ( --count >= 0 )
+ {
+ bFlipped = !bFlipped;
+ DetailObjectLump_t lump;
+ buf.Get( &lump, sizeof(DetailObjectLump_t) );
+
+ // We rely on the fact that details objects are sorted by leaf in the
+ // bsp file for this
+ if ( detailObjectLeaf != lump.m_Leaf )
+ {
+ if (detailObjectLeaf != -1)
+ {
+ if ( nNumFastObjectsInCurLeaf )
+ {
+ CFastDetailLeafSpriteList *pNew = new CFastDetailLeafSpriteList;
+ pNew->m_nNumSprites = nNumFastObjectsInCurLeaf;
+ pNew->m_nNumSIMDSprites = ( 3 + nNumFastObjectsInCurLeaf ) >> 2;
+ pNew->m_pSprites = pCurFastSpriteOut;
+ pCurFastSpriteOut += pNew->m_nNumSIMDSprites;
+ ClientLeafSystem()->SetSubSystemDataInLeaf(
+ detailObjectLeaf, CLSUBSYSTEM_DETAILOBJECTS, pNew );
+ // round to see boundary
+ nCurFastObject += ( 0 - nCurFastObject ) & 3;
+ nNumFastObjectsInCurLeaf = 0;
+ }
+ ClientLeafSystem()->SetDetailObjectsInLeaf( detailObjectLeaf,
+ firstDetailObject, detailObjectCount );
+ }
+
+ detailObjectLeaf = lump.m_Leaf;
+ firstDetailObject = m_DetailObjects.Count();
+ detailObjectCount = 0;
+ }
+
+ if ( DetailObjectIsFastSprite( lump ) )
+ {
+ for( int i =0 ; i < SPRITE_MULTIPLIER ; i++)
+ {
+ FastSpriteX4_t *pSpritex4 = m_pFastSpriteData + (nCurFastObject >> 2 );
+ int nSubField = ( nCurFastObject & 3 );
+ Vector pos(0,0,0);
+ if ( i )
+ {
+ pos += RandomVector( -50, 50 );
+ pos.z = 0;
+ }
+ UnserializeFastSprite( pSpritex4, nSubField, lump, bFlipped, pos );
+ if ( nSubField == 0 )
+ pSpritex4->ReplicateFirstEntryToOthers(); // keep bad numbers out to prevent denormals, etc
+ nCurFastObject++;
+ nNumFastObjectsInCurLeaf++;
+ }
+ }
+ else
+ {
+ switch( lump.m_Type )
+ {
+ case DETAIL_PROP_TYPE_MODEL:
+ {
+ int newObj = m_DetailObjects.AddToTail();
+ m_DetailObjects[newObj].Init(
+ newObj, lump.m_Origin, lump.m_Angles,
+ m_DetailObjectDict[lump.m_DetailModel].m_pModel, lump.m_Lighting,
+ lump.m_LightStyles, lump.m_LightStyleCount, lump.m_Orientation );
+ ++detailObjectCount;
+ }
+ break;
+
+ case DETAIL_PROP_TYPE_SPRITE:
+ case DETAIL_PROP_TYPE_SHAPE_CROSS:
+ case DETAIL_PROP_TYPE_SHAPE_TRI:
+ {
+ for( int i=0;i<SPRITE_MULTIPLIER;i++)
+ {
+ Vector pos = lump.m_Origin;
+ if ( i != 0)
+ {
+ pos += RandomVector( -50, 50 );
+ pos. z = lump.m_Origin.z;
+ }
+ int newObj = m_DetailObjects.AddToTail();
+ m_DetailObjects[newObj].InitSprite(
+ newObj, bFlipped, pos, lump.m_Angles,
+ lump.m_DetailModel, lump.m_Lighting,
+ lump.m_LightStyles, lump.m_LightStyleCount, lump.m_Orientation, lump.m_flScale,
+ lump.m_Type, lump.m_ShapeAngle, lump.m_ShapeSize, lump.m_SwayAmount );
+ ++detailObjectCount;
+ }
+ }
+ break;
+ }
+ }
+ }
+
+
+ if (detailObjectLeaf != -1)
+ {
+ if ( nNumFastObjectsInCurLeaf )
+ {
+ CFastDetailLeafSpriteList *pNew = new CFastDetailLeafSpriteList;
+ pNew->m_nNumSprites = nNumFastObjectsInCurLeaf;
+ pNew->m_nNumSIMDSprites = ( 3 + nNumFastObjectsInCurLeaf ) >> 2;
+ pNew->m_pSprites = pCurFastSpriteOut;
+ pCurFastSpriteOut += pNew->m_nNumSIMDSprites;
+ ClientLeafSystem()->SetSubSystemDataInLeaf(
+ detailObjectLeaf, CLSUBSYSTEM_DETAILOBJECTS, pNew );
+ }
+ ClientLeafSystem()->SetDetailObjectsInLeaf( detailObjectLeaf,
+ firstDetailObject, detailObjectCount );
+ }
+}
+
+
+Vector CDetailObjectSystem::GetSpriteMiddleBottomPosition( DetailObjectLump_t const &lump ) const
+{
+ DetailPropSpriteDict_t &dict = s_DetailObjectSystem.DetailSpriteDict( lump.m_DetailModel );
+
+ Vector vecDir;
+ QAngle Angles;
+
+ VectorSubtract( lump.m_Origin + Vector(0,-100,0), lump.m_Origin, vecDir );
+ vecDir.z = 0.0f;
+ VectorAngles( vecDir, Angles );
+
+ Vector vecOrigin, dx, dy;
+ AngleVectors( Angles, NULL, &dx, &dy );
+
+ Vector2D ul, lr;
+ float scale = lump.m_flScale;
+ Vector2DMultiply( dict.m_UL, scale, ul );
+ Vector2DMultiply( dict.m_LR, scale, lr );
+
+ VectorMA( lump.m_Origin, ul.x, dx, vecOrigin );
+ VectorMA( vecOrigin, ul.y, dy, vecOrigin );
+ dx *= (lr.x - ul.x);
+ dy *= (lr.y - ul.y);
+
+ Vector2D texul, texlr;
+ texul = dict.m_TexUL;
+ texlr = dict.m_TexLR;
+
+ return vecOrigin + dy + 0.5 * dx;
+}
+
+
+void CDetailObjectSystem::UnserializeFastSprite( FastSpriteX4_t *pSpritex4, int nSubField, DetailObjectLump_t const &lump, bool bFlipped, Vector const &posOffset )
+{
+ Vector pos = lump.m_Origin + posOffset;
+ pos = GetSpriteMiddleBottomPosition( lump ) + posOffset;
+
+ pSpritex4->m_Pos.X( nSubField ) = pos.x;
+ pSpritex4->m_Pos.Y( nSubField ) = pos.y;
+ pSpritex4->m_Pos.Z( nSubField ) = pos.z;
+ DetailPropSpriteDict_t *pSDef = &m_DetailSpriteDict[lump.m_DetailModel];
+
+ SubFloat( pSpritex4->m_HalfWidth, nSubField ) = 0.5 * lump.m_flScale * ( pSDef->m_LR.x - pSDef->m_UL.x );
+ SubFloat( pSpritex4->m_Height, nSubField ) = lump.m_flScale * ( pSDef->m_LR.y - pSDef->m_UL.y );
+ if ( !bFlipped )
+ {
+ pSDef = &m_DetailSpriteDictFlipped[lump.m_DetailModel];
+ }
+ // do packed color
+ ColorRGBExp32 rgbcolor = lump.m_Lighting;
+ float color[4];
+ color[0] = TexLightToLinear( rgbcolor.r, rgbcolor.exponent );
+ color[1] = TexLightToLinear( rgbcolor.g, rgbcolor.exponent );
+ color[2] = TexLightToLinear( rgbcolor.b, rgbcolor.exponent );
+ color[3] = 255;
+ engine->LinearToGamma( color, color );
+ pSpritex4->m_RGBColor[nSubField][0] = 255.0 * color[0];
+ pSpritex4->m_RGBColor[nSubField][1] = 255.0 * color[1];
+ pSpritex4->m_RGBColor[nSubField][2] = 255.0 * color[2];
+ pSpritex4->m_RGBColor[nSubField][3] = 255;
+
+ pSpritex4->m_pSpriteDefs[nSubField] = pSDef;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Renders all opaque detail objects in a particular set of leaves
+//-----------------------------------------------------------------------------
+void CDetailObjectSystem::RenderOpaqueDetailObjects( int nLeafCount, LeafIndex_t *pLeafList )
+{
+ // FIXME: Implement!
+}
+
+
+//-----------------------------------------------------------------------------
+// Count the number of detail sprites in the leaf list
+//-----------------------------------------------------------------------------
+int CDetailObjectSystem::CountSpritesInLeafList( int nLeafCount, LeafIndex_t *pLeafList ) const
+{
+ VPROF_BUDGET( "CDetailObjectSystem::CountSpritesInLeafList", VPROF_BUDGETGROUP_DETAILPROP_RENDERING );
+ int nPropCount = 0;
+ int nFirstDetailObject, nDetailObjectCount;
+ for ( int i = 0; i < nLeafCount; ++i )
+ {
+ // FIXME: This actually counts *everything* in the leaf, which is ok for now
+ // given how we're using it
+ ClientLeafSystem()->GetDetailObjectsInLeaf( pLeafList[i], nFirstDetailObject, nDetailObjectCount );
+ nPropCount += nDetailObjectCount;
+ }
+
+ return nPropCount;
+}
+
+//-----------------------------------------------------------------------------
+// Count the number of fast sprites in the leaf list
+//-----------------------------------------------------------------------------
+int CDetailObjectSystem::CountFastSpritesInLeafList( int nLeafCount, LeafIndex_t const *pLeafList,
+ int *nMaxFoundInLeaf ) const
+{
+ VPROF_BUDGET( "CDetailObjectSystem::CountSpritesInLeafList", VPROF_BUDGETGROUP_DETAILPROP_RENDERING );
+ int nCount = 0;
+ int nMax = 0;
+ for ( int i = 0; i < nLeafCount; ++i )
+ {
+ CFastDetailLeafSpriteList *pData = reinterpret_cast< CFastDetailLeafSpriteList *> (
+ ClientLeafSystem()->GetSubSystemDataInLeaf( pLeafList[i], CLSUBSYSTEM_DETAILOBJECTS ) );
+ if ( pData )
+ {
+ nCount += pData->m_nNumSprites;
+ nMax = MAX( nMax, pData->m_nNumSprites );
+ }
+ }
+ *nMaxFoundInLeaf = ( nMax + 3 ) & ~3; // round up
+ return nCount;
+}
+
+
+//-----------------------------------------------------------------------------
+// Count the number of detail sprite quads in the leaf list
+//-----------------------------------------------------------------------------
+int CDetailObjectSystem::CountSpriteQuadsInLeafList( int nLeafCount, LeafIndex_t *pLeafList ) const
+{
+#ifdef USE_DETAIL_SHAPES
+ VPROF_BUDGET( "CDetailObjectSystem::CountSpritesInLeafList", VPROF_BUDGETGROUP_DETAILPROP_RENDERING );
+ int nQuadCount = 0;
+ int nFirstDetailObject, nDetailObjectCount;
+ for ( int i = 0; i < nLeafCount; ++i )
+ {
+ // FIXME: This actually counts *everything* in the leaf, which is ok for now
+ // given how we're using it
+ ClientLeafSystem()->GetDetailObjectsInLeaf( pLeafList[i], nFirstDetailObject, nDetailObjectCount );
+ for ( int j = 0; j < nDetailObjectCount; ++j )
+ {
+ nQuadCount += m_DetailObjects[j + nFirstDetailObject].QuadsToDraw();
+ }
+ }
+
+ return nQuadCount;
+#else
+ return CountSpritesInLeafList( nLeafCount, pLeafList );
+#endif
+}
+
+
+#define TREATASINT(x) ( *( ( (int32 const *)( &(x) ) ) ) )
+
+//-----------------------------------------------------------------------------
+// Sorts sprites in back-to-front order
+//-----------------------------------------------------------------------------
+inline bool CDetailObjectSystem::SortLessFunc( const CDetailObjectSystem::SortInfo_t &left, const CDetailObjectSystem::SortInfo_t &right )
+{
+ return TREATASINT( left.m_flDistance ) > TREATASINT( right.m_flDistance );
+// return left.m_flDistance > right.m_flDistance;
+}
+
+
+int CDetailObjectSystem::SortSpritesBackToFront( int nLeaf, const Vector &viewOrigin, const Vector &viewForward, SortInfo_t *pSortInfo )
+{
+ VPROF_BUDGET( "CDetailObjectSystem::SortSpritesBackToFront", VPROF_BUDGETGROUP_DETAILPROP_RENDERING );
+ int nFirstDetailObject, nDetailObjectCount;
+ ClientLeafSystem()->GetDetailObjectsInLeaf( nLeaf, nFirstDetailObject, nDetailObjectCount );
+
+ float flFactor = 1.0f;
+ C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
+ if ( pLocalPlayer )
+ {
+ flFactor = 1.0 / pLocalPlayer->GetFOVDistanceAdjustFactor();
+ }
+
+ float flMaxSqDist;
+ float flFadeSqDist;
+ float flDetailDist = cl_detaildist.GetFloat();
+
+ flMaxSqDist = flDetailDist * flDetailDist;
+ flFadeSqDist = flDetailDist - cl_detailfade.GetFloat();
+ flMaxSqDist *= flFactor;
+ flFadeSqDist *= flFactor;
+ if (flFadeSqDist > 0)
+ {
+ flFadeSqDist *= flFadeSqDist;
+ }
+ else
+ {
+ flFadeSqDist = 0;
+ }
+ float flFalloffFactor = 255.0f / (flMaxSqDist - flFadeSqDist);
+
+ Vector vecDelta;
+ int nCount = 0;
+ nDetailObjectCount += nFirstDetailObject;
+ for ( int j = nFirstDetailObject; j < nDetailObjectCount; ++j )
+ {
+ CDetailModel &model = m_DetailObjects[j];
+
+ Vector v;
+ VectorSubtract( model.GetRenderOrigin(), viewOrigin, vecDelta );
+ float flSqDist = vecDelta.LengthSqr();
+ if ( flSqDist >= flMaxSqDist )
+ continue;
+
+ if ((flFadeSqDist > 0) && (flSqDist > flFadeSqDist))
+ {
+ model.SetAlpha( flFalloffFactor * ( flMaxSqDist - flSqDist ) );
+ }
+ else
+ {
+ model.SetAlpha( 255 );
+ }
+
+ if ( (model.GetType() == DETAIL_PROP_TYPE_MODEL) || (model.GetAlpha() == 0) )
+ continue;
+
+ // Perform screen alignment if necessary.
+ model.ComputeAngles();
+ SortInfo_t *pSortInfoCurrent = &pSortInfo[nCount];
+
+ pSortInfoCurrent->m_nIndex = j;
+
+ // Compute distance from the camera to each object
+ pSortInfoCurrent->m_flDistance = flSqDist;
+ ++nCount;
+ }
+
+ if ( nCount )
+ {
+ VPROF( "CDetailObjectSystem::SortSpritesBackToFront -- Sort" );
+ std::make_heap( pSortInfo, pSortInfo + nCount, SortLessFunc );
+ std::sort_heap( pSortInfo, pSortInfo + nCount, SortLessFunc );
+ }
+
+ return nCount;
+}
+
+
+#define MAGIC_NUMBER (1<<23)
+#ifdef VALVE_BIG_ENDIAN
+#define MANTISSA_LSB_OFFSET 3
+#else
+#define MANTISSA_LSB_OFFSET 0
+#endif
+static fltx4 Four_MagicNumbers={ MAGIC_NUMBER, MAGIC_NUMBER, MAGIC_NUMBER, MAGIC_NUMBER };
+static fltx4 Four_255s={ 255.0, 255.0, 255.0, 255.0 };
+
+static ALIGN16 int32 And255Mask[4] ALIGN16_POST = {0xff,0xff,0xff,0xff};
+#define PIXMASK ( * ( reinterpret_cast< fltx4 *>( &And255Mask ) ) )
+
+int CDetailObjectSystem::BuildOutSortedSprites( CFastDetailLeafSpriteList *pData,
+ Vector const &viewOrigin,
+ Vector const &viewForward,
+ Vector const &viewRight,
+ Vector const &viewUp )
+{
+ // part 1 - do all vertex math, fading, etc into a buffer, using as much simd as we can
+ int nSIMDSprites = pData->m_nNumSIMDSprites;
+ FastSpriteX4_t const *pSprites = pData->m_pSprites;
+ SortInfo_t *pOut = m_pFastSortInfo;
+ FastSpriteQuadBuildoutBufferX4_t *pQuadBufferOut = m_pBuildoutBuffer;
+ int curidx = 0;
+ int nLastBfMask = 0;
+
+ FourVectors vecViewPos;
+ vecViewPos.DuplicateVector( viewOrigin );
+ fltx4 maxsqdist = ReplicateX4( m_flCurMaxSqDist );
+
+ fltx4 falloffFactor = ReplicateX4( 1.0/ ( m_flCurMaxSqDist - m_flCurFadeSqDist ) );
+ fltx4 startFade = ReplicateX4( m_flCurFadeSqDist );
+
+ FourVectors vecUp;
+ vecUp.DuplicateVector(Vector(0,0,1) );
+ FourVectors vecFwd;
+ vecFwd.DuplicateVector( viewForward );
+
+ do
+ {
+ // calculate alpha
+ FourVectors ofs = pSprites->m_Pos;
+ ofs -= vecViewPos;
+ fltx4 ofsDotFwd = ofs * vecFwd;
+ fltx4 distanceSquared = ofs * ofs;
+ nLastBfMask = TestSignSIMD( OrSIMD( ofsDotFwd, CmpGtSIMD( distanceSquared, maxsqdist ) ) ); // cull
+ if ( nLastBfMask != 0xf )
+ {
+ FourVectors dx1;
+ dx1.x = fnegate( ofs.y );
+ dx1.y = ( ofs.x );
+ dx1.z = Four_Zeros;
+ dx1.VectorNormalizeFast();
+
+ FourVectors vecDx = dx1;
+ FourVectors vecDy = vecUp;
+
+ FourVectors vecPos0 = pSprites->m_Pos;
+
+ vecDx *= pSprites->m_HalfWidth;
+ vecDy *= pSprites->m_Height;
+ fltx4 alpha = MulSIMD( falloffFactor, SubSIMD( distanceSquared, startFade ) );
+ alpha = SubSIMD( Four_Ones, MinSIMD( MaxSIMD( alpha, Four_Zeros), Four_Ones ) );
+
+ pQuadBufferOut->m_Alpha = AddSIMD( Four_MagicNumbers,
+ MulSIMD( Four_255s,alpha ) );
+
+ vecPos0 += vecDx;
+ pQuadBufferOut->m_Coords[0] = vecPos0;
+ vecPos0 -= vecDy;
+ pQuadBufferOut->m_Coords[1] = vecPos0;
+ vecPos0 -= vecDx;
+ vecPos0 -= vecDx;
+ pQuadBufferOut->m_Coords[2] = vecPos0;
+ vecPos0 += vecDy;
+ pQuadBufferOut->m_Coords[3] = vecPos0;
+
+ fltx4 fetch4 = *( ( fltx4 *) ( &pSprites->m_pSpriteDefs[0] ) );
+ *( (fltx4 *) ( & ( pQuadBufferOut->m_pSpriteDefs[0] ) ) ) = fetch4;
+
+ fetch4 = *( ( fltx4 *) ( &pSprites->m_RGBColor[0][0] ) );
+ *( (fltx4 *) ( & ( pQuadBufferOut->m_RGBColor[0][0] ) ) ) = fetch4;
+
+ //!! bug!! store distance
+ // !! speed!! simd?
+ pOut[0].m_nIndex = curidx;
+ pOut[0].m_flDistance = SubFloat( distanceSquared, 0 );
+ pOut[1].m_nIndex = curidx+1;
+ pOut[1].m_flDistance = SubFloat( distanceSquared, 1 );
+ pOut[2].m_nIndex = curidx+2;
+ pOut[2].m_flDistance = SubFloat( distanceSquared, 2 );
+ pOut[3].m_nIndex = curidx+3;
+ pOut[3].m_flDistance = SubFloat( distanceSquared, 3 );
+ curidx += 4;
+ pOut += 4;
+ pQuadBufferOut++;
+ }
+ pSprites++;
+ } while( --nSIMDSprites );
+
+ // adjust count for tail
+ int nCount = pOut - m_pFastSortInfo;
+ if ( nLastBfMask != 0xf ) // if last not skipped
+ nCount -= ( 0 - pData->m_nNumSprites ) & 3;
+
+ // part 2 - sort
+ if ( nCount )
+ {
+ VPROF( "CDetailObjectSystem::SortSpritesBackToFront -- Sort" );
+ std::make_heap( m_pFastSortInfo, m_pFastSortInfo + nCount, SortLessFunc );
+ std::sort_heap( m_pFastSortInfo, m_pFastSortInfo + nCount, SortLessFunc );
+ }
+ return nCount;
+}
+
+
+void CDetailObjectSystem::RenderFastSprites( const Vector &viewOrigin, const Vector &viewForward, const Vector &viewRight, const Vector &viewUp, int nLeafCount, LeafIndex_t const * pLeafList )
+{
+ // Here, we must draw all detail objects back-to-front
+ // FIXME: Cache off a sorted list so we don't have to re-sort every frame
+
+ // Count the total # of detail quads we possibly could render
+ int nMaxInLeaf;
+
+ int nQuadCount = CountFastSpritesInLeafList( nLeafCount, pLeafList, &nMaxInLeaf );
+ if ( nQuadCount == 0 )
+ return;
+ if ( r_DrawDetailProps.GetInt() == 0 )
+ return;
+
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ IMaterial *pMaterial = m_DetailSpriteMaterial;
+ if ( ShouldDrawInWireFrameMode() || r_DrawDetailProps.GetInt() == 2 )
+ {
+ pMaterial = m_DetailWireframeMaterial;
+ }
+
+ CMeshBuilder meshBuilder;
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
+
+ int nMaxVerts, nMaxIndices;
+ pRenderContext->GetMaxToRender( pMesh, false, &nMaxVerts, &nMaxIndices );
+ int nMaxQuadsToDraw = nMaxIndices / 6;
+ if ( nMaxQuadsToDraw > nMaxVerts / 4 )
+ {
+ nMaxQuadsToDraw = nMaxVerts / 4;
+ }
+
+ if ( nMaxQuadsToDraw == 0 )
+ return;
+
+ int nQuadsToDraw = MIN( nQuadCount, nMaxQuadsToDraw );
+ int nQuadsRemaining = nQuadsToDraw;
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nQuadsToDraw );
+
+
+
+ // Sort detail sprites in each leaf independently; then render them
+ for ( int i = 0; i < nLeafCount; ++i )
+ {
+ int nLeaf = pLeafList[i];
+
+ CFastDetailLeafSpriteList *pData = reinterpret_cast<CFastDetailLeafSpriteList *> (
+ ClientLeafSystem()->GetSubSystemDataInLeaf( nLeaf, CLSUBSYSTEM_DETAILOBJECTS ) );
+
+ if ( pData )
+ {
+ Assert( pData->m_nNumSprites ); // ptr with no sprites?
+
+ int nCount = BuildOutSortedSprites( pData, viewOrigin, viewForward, viewRight, viewUp );
+
+ // part 3 - stuff the sorted sprites into the vb
+ SortInfo_t const *pDraw = m_pFastSortInfo;
+ FastSpriteQuadBuildoutBufferNonSIMDView_t const *pQuadBuffer =
+ ( FastSpriteQuadBuildoutBufferNonSIMDView_t const *) m_pBuildoutBuffer;
+
+ COMPILE_TIME_ASSERT( sizeof( FastSpriteQuadBuildoutBufferNonSIMDView_t ) ==
+ sizeof( FastSpriteQuadBuildoutBufferX4_t ) );
+
+ while( nCount )
+ {
+ if ( ! nQuadsRemaining ) // no room left?
+ {
+ meshBuilder.End();
+ pMesh->Draw();
+ nQuadsRemaining = nQuadsToDraw;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nQuadsToDraw );
+ }
+ int nToDraw = MIN( nCount, nQuadsRemaining );
+ nCount -= nToDraw;
+ nQuadsRemaining -= nToDraw;
+ while( nToDraw-- )
+ {
+ // draw the sucker
+ int nSIMDIdx = pDraw->m_nIndex >> 2;
+ int nSubIdx = pDraw->m_nIndex & 3;
+
+ FastSpriteQuadBuildoutBufferNonSIMDView_t const *pquad = pQuadBuffer+nSIMDIdx;
+
+ // voodoo - since everything is in 4s, offset structure pointer by a couple of floats to handle sub-index
+ pquad = (FastSpriteQuadBuildoutBufferNonSIMDView_t const *) ( ( (int) ( pquad ) )+ ( nSubIdx << 2 ) );
+ uint8 const *pColorsCasted = reinterpret_cast<uint8 const *> ( pquad->m_Alpha );
+
+ uint8 color[4];
+ color[0] = pquad->m_RGBColor[0][0];
+ color[1] = pquad->m_RGBColor[0][1];
+ color[2] = pquad->m_RGBColor[0][2];
+ color[3] = pColorsCasted[MANTISSA_LSB_OFFSET];
+
+ DetailPropSpriteDict_t *pDict = pquad->m_pSpriteDefs[0];
+
+ meshBuilder.Position3f( pquad->m_flX0[0], pquad->m_flY0[0], pquad->m_flZ0[0] );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, pDict->m_TexLR.x, pDict->m_TexLR.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( pquad->m_flX1[0], pquad->m_flY1[0], pquad->m_flZ1[0] );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, pDict->m_TexLR.x, pDict->m_TexUL.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( pquad->m_flX2[0], pquad->m_flY2[0], pquad->m_flZ2[0] );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, pDict->m_TexUL.x, pDict->m_TexUL.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( pquad->m_flX3[0], pquad->m_flY3[0], pquad->m_flZ3[0] );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, pDict->m_TexUL.x, pDict->m_TexLR.y );
+ meshBuilder.AdvanceVertex();
+ pDraw++;
+ }
+ }
+ }
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ pRenderContext->PopMatrix();
+}
+
+
+//-----------------------------------------------------------------------------
+// Renders all translucent detail objects in a particular set of leaves
+//-----------------------------------------------------------------------------
+void CDetailObjectSystem::RenderTranslucentDetailObjects( const Vector &viewOrigin, const Vector &viewForward, const Vector &viewRight, const Vector &viewUp, int nLeafCount, LeafIndex_t *pLeafList )
+{
+ VPROF_BUDGET( "CDetailObjectSystem::RenderTranslucentDetailObjects", VPROF_BUDGETGROUP_DETAILPROP_RENDERING );
+ if (nLeafCount == 0)
+ return;
+
+ // We better not have any partially drawn leaf of detail sprites!
+ Assert( m_nSpriteCount == m_nFirstSprite );
+
+ // Here, we must draw all detail objects back-to-front
+ RenderFastSprites( viewOrigin, viewForward, viewRight, viewUp, nLeafCount, pLeafList );
+
+ // FIXME: Cache off a sorted list so we don't have to re-sort every frame
+
+ // Count the total # of detail quads we possibly could render
+ int nQuadCount = CountSpriteQuadsInLeafList( nLeafCount, pLeafList );
+ if ( nQuadCount == 0 )
+ return;
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ IMaterial *pMaterial = m_DetailSpriteMaterial;
+ if ( ShouldDrawInWireFrameMode() || r_DrawDetailProps.GetInt() == 2 )
+ {
+ pMaterial = m_DetailWireframeMaterial;
+ }
+
+ CMeshBuilder meshBuilder;
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
+
+ int nMaxVerts, nMaxIndices;
+ pRenderContext->GetMaxToRender( pMesh, false, &nMaxVerts, &nMaxIndices );
+ int nMaxQuadsToDraw = nMaxIndices / 6;
+ if ( nMaxQuadsToDraw > nMaxVerts / 4 )
+ {
+ nMaxQuadsToDraw = nMaxVerts / 4;
+ }
+
+ if ( nMaxQuadsToDraw == 0 )
+ return;
+
+ int nQuadsToDraw = nQuadCount;
+ if ( nQuadsToDraw > nMaxQuadsToDraw )
+ {
+ nQuadsToDraw = nMaxQuadsToDraw;
+ }
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nQuadsToDraw );
+
+ int nQuadsDrawn = 0;
+ for ( int i = 0; i < nLeafCount; ++i )
+ {
+ int nLeaf = pLeafList[i];
+
+ int nFirstDetailObject, nDetailObjectCount;
+ ClientLeafSystem()->GetDetailObjectsInLeaf( nLeaf, nFirstDetailObject, nDetailObjectCount );
+
+ // Sort detail sprites in each leaf independently; then render them
+ SortInfo_t *pSortInfo = m_pSortInfo;
+ int nCount = SortSpritesBackToFront( nLeaf, viewOrigin, viewForward, pSortInfo );
+
+ for ( int j = 0; j < nCount; ++j )
+ {
+ CDetailModel &model = m_DetailObjects[ pSortInfo[j].m_nIndex ];
+ int nQuadsInModel = model.QuadsToDraw();
+
+ // Prevent the batches from getting too large
+ if ( nQuadsDrawn + nQuadsInModel > nQuadsToDraw )
+ {
+ meshBuilder.End();
+ pMesh->Draw();
+
+ nQuadCount -= nQuadsDrawn;
+ nQuadsToDraw = nQuadCount;
+ if (nQuadsToDraw > nMaxQuadsToDraw)
+ {
+ nQuadsToDraw = nMaxQuadsToDraw;
+ }
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nQuadsToDraw );
+ nQuadsDrawn = 0;
+ }
+
+ model.DrawSprite( meshBuilder );
+
+ nQuadsDrawn += nQuadsInModel;
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRenderContext->PopMatrix();
+}
+
+
+void CDetailObjectSystem::RenderFastTranslucentDetailObjectsInLeaf( const Vector &viewOrigin, const Vector &viewForward, const Vector &viewRight, const Vector &viewUp, int nLeaf, const Vector *pVecClosestPoint )
+{
+ CFastDetailLeafSpriteList *pData = reinterpret_cast< CFastDetailLeafSpriteList *> (
+ ClientLeafSystem()->GetSubSystemDataInLeaf( nLeaf, CLSUBSYSTEM_DETAILOBJECTS ) );
+ if ( ! pData )
+ return;
+
+ if ( m_nSortedFastLeaf != nLeaf )
+ {
+ m_nSortedFastLeaf = nLeaf;
+ pData->m_nNumPendingSprites = BuildOutSortedSprites( pData, viewOrigin, viewForward, viewRight, viewUp );
+ pData->m_nStartSpriteIndex = 0;
+ }
+ if ( pData->m_nNumPendingSprites == 0 )
+ return;
+
+ float flMinDistance = 0.0f;
+ if ( pVecClosestPoint )
+ {
+ Vector vecDelta;
+ VectorSubtract( *pVecClosestPoint, viewOrigin, vecDelta );
+ flMinDistance = vecDelta.LengthSqr();
+ }
+
+ if ( m_pFastSortInfo[pData->m_nStartSpriteIndex].m_flDistance < flMinDistance )
+ return;
+
+ int nCount = pData->m_nNumPendingSprites;
+
+ if ( r_DrawDetailProps.GetInt() == 0 )
+ return;
+
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ IMaterial *pMaterial = m_DetailSpriteMaterial;
+ if ( ShouldDrawInWireFrameMode() || r_DrawDetailProps.GetInt() == 2 )
+ {
+ pMaterial = m_DetailWireframeMaterial;
+ }
+
+ CMeshBuilder meshBuilder;
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
+
+ int nMaxVerts, nMaxIndices;
+ pRenderContext->GetMaxToRender( pMesh, false, &nMaxVerts, &nMaxIndices );
+ int nMaxQuadsToDraw = nMaxIndices / 6;
+ if ( nMaxQuadsToDraw > nMaxVerts / 4 )
+ {
+ nMaxQuadsToDraw = nMaxVerts / 4;
+ }
+
+ if ( nMaxQuadsToDraw == 0 )
+ return;
+
+ int nQuadsToDraw = MIN( nCount, nMaxQuadsToDraw );
+ int nQuadsRemaining = nQuadsToDraw;
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nQuadsToDraw );
+
+ SortInfo_t const *pDraw = m_pFastSortInfo + pData->m_nStartSpriteIndex;
+
+ FastSpriteQuadBuildoutBufferNonSIMDView_t const *pQuadBuffer =
+ ( FastSpriteQuadBuildoutBufferNonSIMDView_t const *) m_pBuildoutBuffer;
+
+ while( nCount && ( pDraw->m_flDistance >= flMinDistance ) )
+ {
+ if ( ! nQuadsRemaining ) // no room left?
+ {
+ meshBuilder.End();
+ pMesh->Draw();
+ nQuadsRemaining = nQuadsToDraw;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nQuadsToDraw );
+ }
+ int nToDraw = MIN( nCount, nQuadsRemaining );
+ nCount -= nToDraw;
+ nQuadsRemaining -= nToDraw;
+ while( nToDraw-- )
+ {
+ // draw the sucker
+ int nSIMDIdx = pDraw->m_nIndex >> 2;
+ int nSubIdx = pDraw->m_nIndex & 3;
+
+ FastSpriteQuadBuildoutBufferNonSIMDView_t const *pquad = pQuadBuffer+nSIMDIdx;
+
+ // voodoo - since everything is in 4s, offset structure pointer by a couple of floats to handle sub-index
+ pquad = (FastSpriteQuadBuildoutBufferNonSIMDView_t const *) ( ( (int) ( pquad ) )+ ( nSubIdx << 2 ) );
+ uint8 const *pColorsCasted = reinterpret_cast<uint8 const *> ( pquad->m_Alpha );
+
+ uint8 color[4];
+ color[0] = pquad->m_RGBColor[0][0];
+ color[1] = pquad->m_RGBColor[0][1];
+ color[2] = pquad->m_RGBColor[0][2];
+ color[3] = pColorsCasted[MANTISSA_LSB_OFFSET];
+
+ DetailPropSpriteDict_t *pDict = pquad->m_pSpriteDefs[0];
+
+ meshBuilder.Position3f( pquad->m_flX0[0], pquad->m_flY0[0], pquad->m_flZ0[0] );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, pDict->m_TexLR.x, pDict->m_TexLR.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( pquad->m_flX1[0], pquad->m_flY1[0], pquad->m_flZ1[0] );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, pDict->m_TexLR.x, pDict->m_TexUL.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( pquad->m_flX2[0], pquad->m_flY2[0], pquad->m_flZ2[0] );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, pDict->m_TexUL.x, pDict->m_TexUL.y );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( pquad->m_flX3[0], pquad->m_flY3[0], pquad->m_flZ3[0] );
+ meshBuilder.Color4ubv( color );
+ meshBuilder.TexCoord2f( 0, pDict->m_TexUL.x, pDict->m_TexLR.y );
+ meshBuilder.AdvanceVertex();
+ pDraw++;
+ }
+ }
+ pData->m_nNumPendingSprites = nCount;
+ pData->m_nStartSpriteIndex = pDraw - m_pFastSortInfo;
+
+ meshBuilder.End();
+ pMesh->Draw();
+ pRenderContext->PopMatrix();
+}
+
+//-----------------------------------------------------------------------------
+// Renders a subset of the detail objects in a particular leaf (for interleaving with other translucent entities)
+//-----------------------------------------------------------------------------
+void CDetailObjectSystem::RenderTranslucentDetailObjectsInLeaf( const Vector &viewOrigin, const Vector &viewForward, const Vector &viewRight, const Vector &viewUp, int nLeaf, const Vector *pVecClosestPoint )
+{
+ VPROF_BUDGET( "CDetailObjectSystem::RenderTranslucentDetailObjectsInLeaf", VPROF_BUDGETGROUP_DETAILPROP_RENDERING );
+
+ RenderFastTranslucentDetailObjectsInLeaf( viewOrigin, viewForward, viewRight, viewUp, nLeaf, pVecClosestPoint );
+ // We may have already sorted this leaf. If not, sort the leaf.
+ if ( m_nSortedLeaf != nLeaf )
+ {
+ m_nSortedLeaf = nLeaf;
+ m_nSpriteCount = 0;
+ m_nFirstSprite = 0;
+
+ // Count the total # of detail sprites we possibly could render
+ LeafIndex_t nLeafIndex = nLeaf;
+ int nSpriteCount = CountSpritesInLeafList( 1, &nLeafIndex );
+ if (nSpriteCount == 0)
+ return;
+
+ // Sort detail sprites in each leaf independently; then render them
+ m_nSpriteCount = SortSpritesBackToFront( nLeaf, viewOrigin, viewForward, m_pSortInfo );
+ Assert( m_nSpriteCount <= nSpriteCount );
+ }
+
+ // No more to draw? Bye!
+ if ( m_nSpriteCount == m_nFirstSprite )
+ return;
+
+ float flMinDistance = 0.0f;
+ if ( pVecClosestPoint )
+ {
+ Vector vecDelta;
+ VectorSubtract( *pVecClosestPoint, viewOrigin, vecDelta );
+ flMinDistance = vecDelta.LengthSqr();
+ }
+
+ if ( m_pSortInfo[m_nFirstSprite].m_flDistance < flMinDistance )
+ return;
+
+ CMatRenderContextPtr pRenderContext( materials );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ IMaterial *pMaterial = m_DetailSpriteMaterial;
+ if ( ShouldDrawInWireFrameMode() || r_DrawDetailProps.GetInt() == 2 )
+ {
+ pMaterial = m_DetailWireframeMaterial;
+ }
+
+ CMeshBuilder meshBuilder;
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, pMaterial );
+
+ int nMaxVerts, nMaxIndices;
+ pRenderContext->GetMaxToRender( pMesh, false, &nMaxVerts, &nMaxIndices );
+
+ // needs to be * 4 since there are a max of 4 quads per detail object
+ int nQuadCount = ( m_nSpriteCount - m_nFirstSprite ) * 4;
+ int nMaxQuadsToDraw = nMaxIndices/6;
+ if ( nMaxQuadsToDraw > nMaxVerts / 4 )
+ {
+ nMaxQuadsToDraw = nMaxVerts / 4;
+ }
+
+ if ( nMaxQuadsToDraw == 0 )
+ return;
+
+ int nQuadsToDraw = nQuadCount;
+ if ( nQuadsToDraw > nMaxQuadsToDraw )
+ {
+ nQuadsToDraw = nMaxQuadsToDraw;
+ }
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nQuadsToDraw );
+
+ int nQuadsDrawn = 0;
+ while ( m_nFirstSprite < m_nSpriteCount && m_pSortInfo[m_nFirstSprite].m_flDistance >= flMinDistance )
+ {
+ CDetailModel &model = m_DetailObjects[m_pSortInfo[m_nFirstSprite].m_nIndex];
+ int nQuadsInModel = model.QuadsToDraw();
+ if ( nQuadsDrawn + nQuadsInModel > nQuadsToDraw )
+ {
+ meshBuilder.End();
+ pMesh->Draw();
+
+ nQuadCount = ( m_nSpriteCount - m_nFirstSprite ) * 4;
+ nQuadsToDraw = nQuadCount;
+ if (nQuadsToDraw > nMaxQuadsToDraw)
+ {
+ nQuadsToDraw = nMaxQuadsToDraw;
+ }
+
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, nQuadsToDraw );
+ nQuadsDrawn = 0;
+ }
+
+ model.DrawSprite( meshBuilder );
+ ++m_nFirstSprite;
+ nQuadsDrawn += nQuadsInModel;
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRenderContext->PopMatrix();
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets called each view
+//-----------------------------------------------------------------------------
+bool CDetailObjectSystem::EnumerateLeaf( int leaf, int context )
+{
+ VPROF_BUDGET( "CDetailObjectSystem::EnumerateLeaf", VPROF_BUDGETGROUP_DETAILPROP_RENDERING );
+ Vector v;
+ int firstDetailObject, detailObjectCount;
+
+ EnumContext_t* pCtx = (EnumContext_t*)context;
+ ClientLeafSystem()->DrawDetailObjectsInLeaf( leaf, pCtx->m_BuildWorldListNumber,
+ firstDetailObject, detailObjectCount );
+
+ // Compute the translucency. Need to do it now cause we need to
+ // know that when we're rendering (opaque stuff is rendered first)
+ for ( int i = 0; i < detailObjectCount; ++i)
+ {
+ // Calculate distance (badly)
+ CDetailModel& model = m_DetailObjects[firstDetailObject+i];
+ VectorSubtract( model.GetRenderOrigin(), pCtx->m_vViewOrigin, v );
+
+ float sqDist = v.LengthSqr();
+
+ model.SetAlpha( 255 );
+ if ( sqDist < m_flCurMaxSqDist )
+ {
+ if ( sqDist > m_flCurFadeSqDist )
+ {
+ model.SetAlpha( m_flCurFalloffFactor * ( m_flCurMaxSqDist - sqDist ) );
+ }
+ else
+ {
+ model.SetAlpha( 255 );
+ }
+
+ // Perform screen alignment if necessary.
+ model.ComputeAngles();
+ }
+ else
+ {
+ model.SetAlpha( 0 );
+ }
+ }
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets called each view
+//-----------------------------------------------------------------------------
+void CDetailObjectSystem::BuildDetailObjectRenderLists( const Vector &vViewOrigin )
+{
+ VPROF_BUDGET( "CDetailObjectSystem::BuildDetailObjectRenderLists", VPROF_BUDGETGROUP_DETAILPROP_RENDERING );
+
+ if (!g_pClientMode->ShouldDrawDetailObjects() || (r_DrawDetailProps.GetInt() == 0))
+ return;
+
+ // Don't bother doing any of this if the level doesn't have detail props.
+ if ( ( ! m_pFastSpriteData ) && ( m_DetailObjects.Count() == 0 ) )
+ return;
+
+ EnumContext_t ctx;
+ ctx.m_vViewOrigin = vViewOrigin;
+ ctx.m_BuildWorldListNumber = view->BuildWorldListsNumber();
+
+ // We need to recompute translucency information for all detail props
+ for (int i = m_DetailObjectDict.Size(); --i >= 0; )
+ {
+ if (modelinfo->ModelHasMaterialProxy( m_DetailObjectDict[i].m_pModel ))
+ {
+ modelinfo->RecomputeTranslucency( m_DetailObjectDict[i].m_pModel, 0, 0, NULL );
+ }
+ }
+
+ float factor = 1.0f;
+ C_BasePlayer *local = C_BasePlayer::GetLocalPlayer();
+ if ( local )
+ {
+ factor = local->GetFOVDistanceAdjustFactor();
+ }
+
+ // Compute factors to optimize rendering of the detail models
+ m_flCurMaxSqDist = cl_detaildist.GetFloat() * cl_detaildist.GetFloat();
+ m_flCurFadeSqDist = cl_detaildist.GetFloat() - cl_detailfade.GetFloat();
+
+ m_flCurMaxSqDist /= factor;
+ m_flCurFadeSqDist /= factor;
+
+ if ( m_flCurFadeSqDist > 0)
+ {
+ m_flCurFadeSqDist *= m_flCurFadeSqDist;
+ }
+ else
+ {
+ m_flCurFadeSqDist = 0;
+ }
+ m_flCurFadeSqDist = MIN( m_flCurFadeSqDist, m_flCurMaxSqDist -1 );
+ m_flCurFalloffFactor = 255.0f / ( m_flCurMaxSqDist - m_flCurFadeSqDist );
+
+
+ ISpatialQuery* pQuery = engine->GetBSPTreeQuery();
+ pQuery->EnumerateLeavesInSphere( CurrentViewOrigin(),
+ cl_detaildist.GetFloat(), this, (int)&ctx );
+}
+
|