source-engine/game/client/detailobjectsystem.cpp
2022-04-16 12:54:00 +03:00

2812 lines
84 KiB
C++

//========= 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 = (float)( 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->GetHDREnabled() )
{
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 );
}