You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1308 lines
41 KiB
1308 lines
41 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $Workfile: $ |
|
// $NoKeywords: $ |
|
//===========================================================================// |
|
#include "engine/ivmodelinfo.h" |
|
#include "filesystem.h" |
|
#include "gl_model_private.h" |
|
#include "modelloader.h" |
|
#include "l_studio.h" |
|
#include "cmodel_engine.h" |
|
#include "server.h" |
|
#include "r_local.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "materialsystem/imaterial.h" |
|
#include "lightcache.h" |
|
#include "istudiorender.h" |
|
#include "utldict.h" |
|
#include "filesystem_engine.h" |
|
#include "client.h" |
|
#include "sys_dll.h" |
|
#include "gl_rsurf.h" |
|
#include "utlvector.h" |
|
#include "utlhashtable.h" |
|
#include "utlsymbol.h" |
|
#include "ModelInfo.h" |
|
#include "networkstringtable.h" // for Lock() |
|
|
|
#ifndef SWDS |
|
#include "demo.h" |
|
#endif |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Gets the lighting center |
|
//----------------------------------------------------------------------------- |
|
static void R_StudioGetLightingCenter( IClientRenderable *pRenderable, studiohdr_t* pStudioHdr, const Vector& origin, |
|
const QAngle &angles, Vector* pLightingOrigin ) |
|
{ |
|
Assert( pLightingOrigin ); |
|
matrix3x4_t matrix; |
|
AngleMatrix( angles, origin, matrix ); |
|
R_ComputeLightingOrigin( pRenderable, pStudioHdr, matrix, *pLightingOrigin ); |
|
} |
|
|
|
static int R_StudioBodyVariations( studiohdr_t *pstudiohdr ) |
|
{ |
|
mstudiobodyparts_t *pbodypart; |
|
int i, count; |
|
|
|
if ( !pstudiohdr ) |
|
return 0; |
|
|
|
count = 1; |
|
pbodypart = pstudiohdr->pBodypart( 0 ); |
|
|
|
// Each body part has nummodels variations so there are as many total variations as there |
|
// are in a matrix of each part by each other part |
|
for ( i = 0; i < pstudiohdr->numbodyparts; i++ ) |
|
{ |
|
count = count * pbodypart[i].nummodels; |
|
} |
|
return count; |
|
} |
|
|
|
static int ModelFrameCount( model_t *model ) |
|
{ |
|
int count = 1; |
|
|
|
if ( !model ) |
|
return count; |
|
|
|
if ( model->type == mod_sprite ) |
|
{ |
|
return model->sprite.numframes; |
|
} |
|
else if ( model->type == mod_studio ) |
|
{ |
|
count = R_StudioBodyVariations( ( studiohdr_t * )modelloader->GetExtraData( model ) ); |
|
} |
|
|
|
if ( count < 1 ) |
|
count = 1; |
|
|
|
return count; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// private extension of CNetworkStringTable to correct lack of Lock retval |
|
//----------------------------------------------------------------------------- |
|
class CNetworkStringTable_LockOverride : public CNetworkStringTable |
|
{ |
|
private: |
|
// CNetworkStringTable_LockOverride(); // no impl |
|
// ~CNetworkStringTable_LockOverride(); // no impl |
|
CNetworkStringTable_LockOverride(const CNetworkStringTable_LockOverride &); // no impl |
|
CNetworkStringTable_LockOverride& operator=(const CNetworkStringTable_LockOverride &); // no impl |
|
public: |
|
bool LockWithRetVal( bool bLock ) { bool bWasLocked = m_bLocked; Lock(bLock); return bWasLocked; } |
|
}; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// shared implementation of IVModelInfo |
|
//----------------------------------------------------------------------------- |
|
abstract_class CModelInfo : public IVModelInfoClient |
|
{ |
|
public: |
|
// GetModel, RegisterDynamicModel(name) are in CModelInfoClient/CModelInfoServer |
|
virtual int GetModelIndex( const char *name ) const; |
|
virtual int GetModelClientSideIndex( const char *name ) const; |
|
|
|
virtual bool RegisterModelLoadCallback( int modelindex, IModelLoadCallback* pCallback, bool bCallImmediatelyIfLoaded ); |
|
virtual void UnregisterModelLoadCallback( int modelindex, IModelLoadCallback* pCallback ); |
|
virtual bool IsDynamicModelLoading( int modelIndex ); |
|
virtual void AddRefDynamicModel( int modelIndex ); |
|
virtual void ReleaseDynamicModel( int modelIndex ); |
|
|
|
virtual void OnLevelChange(); |
|
|
|
virtual const char *GetModelName( const model_t *model ) const; |
|
virtual void GetModelBounds( const model_t *model, Vector& mins, Vector& maxs ) const; |
|
virtual void GetModelRenderBounds( const model_t *model, Vector& mins, Vector& maxs ) const; |
|
virtual int GetModelFrameCount( const model_t *model ) const; |
|
virtual int GetModelType( const model_t *model ) const; |
|
virtual void *GetModelExtraData( const model_t *model ); |
|
virtual bool ModelHasMaterialProxy( const model_t *model ) const; |
|
virtual bool IsTranslucent( const model_t *model ) const; |
|
virtual bool IsModelVertexLit( const model_t *model ) const; |
|
virtual bool IsTranslucentTwoPass( const model_t *model ) const; |
|
virtual void RecomputeTranslucency( const model_t *model, int nSkin, int nBody, void /*IClientRenderable*/ *pClientRenderable, float fInstanceAlphaModulate); |
|
virtual int GetModelMaterialCount( const model_t *model ) const; |
|
virtual void GetModelMaterials( const model_t *model, int count, IMaterial** ppMaterials ); |
|
virtual void GetIlluminationPoint( const model_t *model, IClientRenderable *pRenderable, const Vector& origin, |
|
const QAngle& angles, Vector* pLightingOrigin ); |
|
virtual int GetModelContents( int modelIndex ); |
|
vcollide_t *GetVCollide( const model_t *model ); |
|
vcollide_t *GetVCollide( int modelIndex ); |
|
virtual const char *GetModelKeyValueText( const model_t *model ); |
|
virtual bool GetModelKeyValue( const model_t *model, CUtlBuffer &buf ); |
|
virtual float GetModelRadius( const model_t *model ); |
|
virtual studiohdr_t *GetStudiomodel( const model_t *mod ); |
|
virtual int GetModelSpriteWidth( const model_t *model ) const; |
|
virtual int GetModelSpriteHeight( const model_t *model ) const; |
|
|
|
virtual const studiohdr_t *FindModel( const studiohdr_t *pStudioHdr, void **cache, char const *modelname ) const; |
|
virtual const studiohdr_t *FindModel( void *cache ) const; |
|
virtual virtualmodel_t *GetVirtualModel( const studiohdr_t *pStudioHdr ) const; |
|
virtual byte *GetAnimBlock( const studiohdr_t *pStudioHdr, int iBlock ) const; |
|
|
|
byte *LoadAnimBlock( model_t *model, const studiohdr_t *pStudioHdr, int iBlock, cache_user_t *cache ) const; |
|
|
|
// NOTE: These aren't in the server version, but putting them here makes this code easier to write |
|
// Sets/gets a map-specified fade range |
|
virtual void SetLevelScreenFadeRange( float flMinSize, float flMaxSize ) {} |
|
virtual void GetLevelScreenFadeRange( float *pMinArea, float *pMaxArea ) const { *pMinArea = 0; *pMaxArea = 0; } |
|
|
|
// Sets/gets a map-specified per-view fade range |
|
virtual void SetViewScreenFadeRange( float flMinSize, float flMaxSize ) {} |
|
|
|
// Computes fade alpha based on distance fade + screen fade |
|
virtual unsigned char ComputeLevelScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const { return 0; } |
|
virtual unsigned char ComputeViewScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const { return 0; } |
|
|
|
int GetAutoplayList( const studiohdr_t *pStudioHdr, unsigned short **pAutoplayList ) const; |
|
CPhysCollide *GetCollideForVirtualTerrain( int index ); |
|
virtual int GetSurfacepropsForVirtualTerrain( int index ) { return CM_SurfacepropsForDisp(index); } |
|
|
|
virtual bool IsUsingFBTexture( const model_t *model, int nSkin, int nBody, void /*IClientRenderable*/ *pClientRenderable ) const; |
|
|
|
virtual MDLHandle_t GetCacheHandle( const model_t *model ) const { return ( model->type == mod_studio ) ? model->studio : MDLHANDLE_INVALID; } |
|
|
|
// Returns planes of non-nodraw brush model surfaces |
|
virtual int GetBrushModelPlaneCount( const model_t *model ) const; |
|
virtual void GetBrushModelPlane( const model_t *model, int nIndex, cplane_t &plane, Vector *pOrigin ) const; |
|
|
|
protected: |
|
static int CLIENTSIDE_TO_MODEL( int i ) { return i >= 0 ? (-2 - (i*2 + 1)) : -1; } |
|
static int NETDYNAMIC_TO_MODEL( int i ) { return i >= 0 ? (-2 - (i*2)) : -1; } |
|
static int MODEL_TO_CLIENTSIDE( int i ) { return ( i <= -2 && (i & 1) ) ? (-2 - i) >> 1 : -1; } |
|
static int MODEL_TO_NETDYNAMIC( int i ) { return ( i <= -2 && !(i & 1) ) ? (-2 - i) >> 1 : -1; } |
|
|
|
model_t *LookupDynamicModel( int i ); |
|
|
|
virtual INetworkStringTable *GetDynamicModelStringTable() const = 0; |
|
virtual int LookupPrecachedModelIndex( const char *name ) const = 0; |
|
|
|
void GrowNetworkedDynamicModels( int netidx ) |
|
{ |
|
if ( m_NetworkedDynamicModels.Count() <= netidx ) |
|
{ |
|
int origCount = m_NetworkedDynamicModels.Count(); |
|
m_NetworkedDynamicModels.SetCountNonDestructively( netidx + 1 ); |
|
for ( int i = origCount; i <= netidx; ++i ) |
|
{ |
|
m_NetworkedDynamicModels[i] = NULL; |
|
} |
|
} |
|
} |
|
|
|
// Networked dynamic model indices are lookup indices for this vector |
|
CUtlVector< model_t* > m_NetworkedDynamicModels; |
|
|
|
public: |
|
struct ModelFileHandleHash |
|
{ |
|
uint operator()( model_t *p ) const { return PointerHashFunctor()( p->fnHandle ); } |
|
uint operator()( FileNameHandle_t fn ) const { return PointerHashFunctor()( fn ); } |
|
}; |
|
struct ModelFileHandleEq |
|
{ |
|
bool operator()( model_t *a, model_t *b ) const { return a == b; } |
|
bool operator()( model_t *a, FileNameHandle_t b ) const { return a->fnHandle == b; } |
|
}; |
|
protected: |
|
// Client-only dynamic model indices are iterators into this struct (only populated by CModelInfoClient subclass) |
|
CUtlStableHashtable< model_t*, empty_t, ModelFileHandleHash, ModelFileHandleEq, int16, FileNameHandle_t > m_ClientDynamicModels; |
|
}; |
|
|
|
int CModelInfo::GetModelIndex( const char *name ) const |
|
{ |
|
if ( !name ) |
|
return -1; |
|
|
|
// Order of preference: precached, networked, client-only. |
|
int nIndex = LookupPrecachedModelIndex( name ); |
|
if ( nIndex != -1 ) |
|
return nIndex; |
|
|
|
INetworkStringTable* pTable = GetDynamicModelStringTable(); |
|
if ( pTable ) |
|
{ |
|
int netdyn = pTable->FindStringIndex( name ); |
|
if ( netdyn != INVALID_STRING_INDEX ) |
|
{ |
|
Assert( !m_NetworkedDynamicModels.IsValidIndex( netdyn ) || V_strcmp( m_NetworkedDynamicModels[netdyn]->strName, name ) == 0 ); |
|
return NETDYNAMIC_TO_MODEL( netdyn ); |
|
} |
|
|
|
#if defined( DEMO_BACKWARDCOMPATABILITY ) && !defined( SWDS ) |
|
// dynamic model tables in old system did not have a full path with "models/" prefix |
|
if ( V_strnicmp( name, "models/", 7 ) == 0 && demoplayer && demoplayer->IsPlayingBack() && demoplayer->GetProtocolVersion() < PROTOCOL_VERSION_20 ) |
|
{ |
|
netdyn = pTable->FindStringIndex( name + 7 ); |
|
if ( netdyn != INVALID_STRING_INDEX ) |
|
{ |
|
Assert( !m_NetworkedDynamicModels.IsValidIndex( netdyn ) || V_strcmp( m_NetworkedDynamicModels[netdyn]->strName, name ) == 0 ); |
|
return NETDYNAMIC_TO_MODEL( netdyn ); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
return GetModelClientSideIndex( name ); |
|
} |
|
|
|
int CModelInfo::GetModelClientSideIndex( const char *name ) const |
|
{ |
|
if ( m_ClientDynamicModels.Count() != 0 ) |
|
{ |
|
FileNameHandle_t file = g_pFullFileSystem->FindFileName( name ); |
|
if ( file != FILENAMEHANDLE_INVALID ) |
|
{ |
|
UtlHashHandle_t h = m_ClientDynamicModels.Find( file ); |
|
if ( h != m_ClientDynamicModels.InvalidHandle() ) |
|
{ |
|
Assert( V_strcmp( m_ClientDynamicModels[h]->strName, name ) == 0 ); |
|
return CLIENTSIDE_TO_MODEL( h ); |
|
} |
|
} |
|
} |
|
|
|
return -1; |
|
} |
|
|
|
model_t *CModelInfo::LookupDynamicModel( int i ) |
|
{ |
|
Assert( IsDynamicModelIndex( i ) ); |
|
if ( IsClientOnlyModelIndex( i ) ) |
|
{ |
|
UtlHashHandle_t h = (UtlHashHandle_t) MODEL_TO_CLIENTSIDE( i ); |
|
return m_ClientDynamicModels.IsValidHandle( h ) ? m_ClientDynamicModels[ h ] : NULL; |
|
} |
|
else |
|
{ |
|
int netidx = MODEL_TO_NETDYNAMIC( i ); |
|
if ( m_NetworkedDynamicModels.IsValidIndex( netidx ) && m_NetworkedDynamicModels[ netidx ] ) |
|
return m_NetworkedDynamicModels[ netidx ]; |
|
|
|
INetworkStringTable *pTable = GetDynamicModelStringTable(); |
|
if ( pTable && (uint) netidx < (uint) pTable->GetNumStrings() ) |
|
{ |
|
GrowNetworkedDynamicModels( netidx ); |
|
const char *name = pTable->GetString( netidx ); |
|
|
|
#if defined( DEMO_BACKWARDCOMPATABILITY ) && !defined( SWDS ) |
|
// dynamic model tables in old system did not have a full path with "models/" prefix |
|
char fixupBuf[MAX_PATH]; |
|
if ( V_strnicmp( name, "models/", 7 ) != 0 && demoplayer && demoplayer->IsPlayingBack() && demoplayer->GetProtocolVersion() < PROTOCOL_VERSION_20 ) |
|
{ |
|
V_snprintf( fixupBuf, MAX_PATH, "models/%s", name ); |
|
name = fixupBuf; |
|
} |
|
#endif |
|
|
|
model_t *pModel = modelloader->GetDynamicModel( name, false ); |
|
m_NetworkedDynamicModels[ netidx ] = pModel; |
|
return pModel; |
|
} |
|
|
|
return NULL; |
|
} |
|
} |
|
|
|
|
|
bool CModelInfo::RegisterModelLoadCallback( int modelIndex, IModelLoadCallback* pCallback, bool bCallImmediatelyIfLoaded ) |
|
{ |
|
const model_t *pModel = GetModel( modelIndex ); |
|
Assert( pModel ); |
|
if ( pModel && IsDynamicModelIndex( modelIndex ) ) |
|
{ |
|
return modelloader->RegisterModelLoadCallback( const_cast< model_t *>( pModel ), IsClientOnlyModelIndex( modelIndex ), pCallback, bCallImmediatelyIfLoaded ); |
|
} |
|
else if ( pModel && bCallImmediatelyIfLoaded ) |
|
{ |
|
pCallback->OnModelLoadComplete( pModel ); |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
void CModelInfo::UnregisterModelLoadCallback( int modelIndex, IModelLoadCallback* pCallback ) |
|
{ |
|
if ( modelIndex == -1 ) |
|
{ |
|
modelloader->UnregisterModelLoadCallback( NULL, false, pCallback ); |
|
modelloader->UnregisterModelLoadCallback( NULL, true, pCallback ); |
|
} |
|
else if ( IsDynamicModelIndex( modelIndex ) ) |
|
{ |
|
const model_t *pModel = LookupDynamicModel( modelIndex ); |
|
Assert( pModel ); |
|
if ( pModel ) |
|
{ |
|
modelloader->UnregisterModelLoadCallback( const_cast< model_t *>( pModel ), IsClientOnlyModelIndex( modelIndex ), pCallback ); |
|
} |
|
} |
|
} |
|
|
|
|
|
bool CModelInfo::IsDynamicModelLoading( int modelIndex ) |
|
{ |
|
model_t *pModel = LookupDynamicModel( modelIndex ); |
|
return pModel && modelloader->IsDynamicModelLoading( pModel, IsClientOnlyModelIndex( modelIndex ) ); |
|
} |
|
|
|
|
|
void CModelInfo::AddRefDynamicModel( int modelIndex ) |
|
{ |
|
if ( IsDynamicModelIndex( modelIndex ) ) |
|
{ |
|
model_t *pModel = LookupDynamicModel( modelIndex ); |
|
Assert( pModel ); |
|
if ( pModel ) |
|
{ |
|
modelloader->AddRefDynamicModel( pModel, IsClientOnlyModelIndex( modelIndex ) ); |
|
} |
|
} |
|
} |
|
|
|
void CModelInfo::ReleaseDynamicModel( int modelIndex ) |
|
{ |
|
if ( IsDynamicModelIndex( modelIndex ) ) |
|
{ |
|
model_t *pModel = LookupDynamicModel( modelIndex ); |
|
Assert( pModel ); |
|
if ( pModel ) |
|
{ |
|
modelloader->ReleaseDynamicModel( pModel, IsClientOnlyModelIndex( modelIndex ) ); |
|
} |
|
} |
|
} |
|
|
|
void CModelInfo::OnLevelChange() |
|
{ |
|
// Network string table has reset |
|
m_NetworkedDynamicModels.Purge(); |
|
|
|
// Force-unload any server-side models |
|
modelloader->ForceUnloadNonClientDynamicModels(); |
|
} |
|
|
|
const char *CModelInfo::GetModelName( const model_t *pModel ) const |
|
{ |
|
if ( !pModel ) |
|
{ |
|
return "?"; |
|
} |
|
|
|
return modelloader->GetName( pModel ); |
|
} |
|
|
|
void CModelInfo::GetModelBounds( const model_t *model, Vector& mins, Vector& maxs ) const |
|
{ |
|
VectorCopy( model->mins, mins ); |
|
VectorCopy( model->maxs, maxs ); |
|
} |
|
|
|
void CModelInfo::GetModelRenderBounds( const model_t *model, Vector& mins, Vector& maxs ) const |
|
{ |
|
if (!model) |
|
{ |
|
mins.Init(0,0,0); |
|
maxs.Init(0,0,0); |
|
return; |
|
} |
|
|
|
switch( model->type ) |
|
{ |
|
case mod_studio: |
|
{ |
|
studiohdr_t *pStudioHdr = ( studiohdr_t * )modelloader->GetExtraData( (model_t*)model ); |
|
Assert( pStudioHdr ); |
|
|
|
// NOTE: We're not looking at the sequence box here, although we could |
|
if (!VectorCompare( vec3_origin, pStudioHdr->view_bbmin ) || !VectorCompare( vec3_origin, pStudioHdr->view_bbmax )) |
|
{ |
|
// clipping bounding box |
|
VectorCopy ( pStudioHdr->view_bbmin, mins); |
|
VectorCopy ( pStudioHdr->view_bbmax, maxs); |
|
} |
|
else |
|
{ |
|
// movement bounding box |
|
VectorCopy ( pStudioHdr->hull_min, mins); |
|
VectorCopy ( pStudioHdr->hull_max, maxs); |
|
} |
|
} |
|
break; |
|
|
|
case mod_brush: |
|
VectorCopy( model->mins, mins ); |
|
VectorCopy( model->maxs, maxs ); |
|
break; |
|
|
|
default: |
|
mins.Init( 0, 0, 0 ); |
|
maxs.Init( 0, 0, 0 ); |
|
break; |
|
} |
|
} |
|
|
|
int CModelInfo::GetModelSpriteWidth( const model_t *model ) const |
|
{ |
|
// We must be a sprite to make this query |
|
if ( model->type != mod_sprite ) |
|
return 0; |
|
|
|
return model->sprite.width; |
|
} |
|
|
|
int CModelInfo::GetModelSpriteHeight( const model_t *model ) const |
|
{ |
|
// We must be a sprite to make this query |
|
if ( model->type != mod_sprite ) |
|
return 0; |
|
|
|
return model->sprite.height; |
|
} |
|
|
|
int CModelInfo::GetModelFrameCount( const model_t *model ) const |
|
{ |
|
return ModelFrameCount( ( model_t *)model ); |
|
} |
|
|
|
int CModelInfo::GetModelType( const model_t *model ) const |
|
{ |
|
if ( !model ) |
|
return -1; |
|
|
|
if ( model->type == mod_bad ) |
|
{ |
|
if ( m_ClientDynamicModels.Find( (model_t*) model ) != m_ClientDynamicModels.InvalidHandle() ) |
|
return mod_studio; |
|
INetworkStringTable* pTable = GetDynamicModelStringTable(); |
|
if ( pTable && pTable->FindStringIndex( model->strName ) != INVALID_STRING_INDEX ) |
|
return mod_studio; |
|
|
|
#if defined( DEMO_BACKWARDCOMPATABILITY ) && !defined( SWDS ) |
|
// dynamic model tables in old system did not have a full path with "models/" prefix |
|
if ( pTable && demoplayer && demoplayer->IsPlayingBack() && demoplayer->GetProtocolVersion() < PROTOCOL_VERSION_20 && |
|
V_strnicmp( model->strName, "models/", 7 ) == 0 && pTable->FindStringIndex( model->strName + 7 ) != INVALID_STRING_INDEX ) |
|
{ |
|
return mod_studio; |
|
} |
|
#endif |
|
} |
|
|
|
return model->type; |
|
} |
|
|
|
void *CModelInfo::GetModelExtraData( const model_t *model ) |
|
{ |
|
return modelloader->GetExtraData( (model_t *)model ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Translate "cache" pointer into model_t, or lookup model by name |
|
//----------------------------------------------------------------------------- |
|
const studiohdr_t *CModelInfo::FindModel( const studiohdr_t *pStudioHdr, void **cache, char const *modelname ) const |
|
{ |
|
const model_t *model = (model_t *)*cache; |
|
|
|
if (!model) |
|
{ |
|
// FIXME: what do I pass in here? |
|
model = modelloader->GetModelForName( modelname, IModelLoader::FMODELLOADER_SERVER ); |
|
*cache = (void *)model; |
|
} |
|
|
|
return (const studiohdr_t *)modelloader->GetExtraData( (model_t *)model ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Translate "cache" pointer into model_t |
|
//----------------------------------------------------------------------------- |
|
const studiohdr_t *CModelInfo::FindModel( void *cache ) const |
|
{ |
|
return g_pMDLCache->GetStudioHdr( VoidPtrToMDLHandle( cache ) ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return virtualmodel_t block associated with model_t |
|
//----------------------------------------------------------------------------- |
|
virtualmodel_t *CModelInfo::GetVirtualModel( const studiohdr_t *pStudioHdr ) const |
|
{ |
|
MDLHandle_t handle = VoidPtrToMDLHandle( pStudioHdr->VirtualModel() ); |
|
return g_pMDLCache->GetVirtualModelFast( pStudioHdr, handle ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
byte *CModelInfo::GetAnimBlock( const studiohdr_t *pStudioHdr, int nBlock ) const |
|
{ |
|
MDLHandle_t handle = VoidPtrToMDLHandle( pStudioHdr->VirtualModel() ); |
|
return g_pMDLCache->GetAnimBlock( handle, nBlock ); |
|
} |
|
|
|
int CModelInfo::GetAutoplayList( const studiohdr_t *pStudioHdr, unsigned short **pAutoplayList ) const |
|
{ |
|
MDLHandle_t handle = VoidPtrToMDLHandle( pStudioHdr->VirtualModel() ); |
|
return g_pMDLCache->GetAutoplayList( handle, pAutoplayList ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: bind studiohdr_t support functions to engine |
|
// FIXME: This should be moved into studio.cpp? |
|
//----------------------------------------------------------------------------- |
|
const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *pModelName ) const |
|
{ |
|
MDLHandle_t handle = g_pMDLCache->FindMDL( pModelName ); |
|
*cache = (void*)(uintp)handle; |
|
return g_pMDLCache->GetStudioHdr( handle ); |
|
} |
|
|
|
virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const |
|
{ |
|
if ( numincludemodels == 0 ) |
|
return NULL; |
|
return g_pMDLCache->GetVirtualModelFast( this, VoidPtrToMDLHandle( VirtualModel() ) ); |
|
} |
|
|
|
byte *studiohdr_t::GetAnimBlock( int i ) const |
|
{ |
|
return g_pMDLCache->GetAnimBlock( VoidPtrToMDLHandle( VirtualModel() ), i ); |
|
} |
|
|
|
int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const |
|
{ |
|
return g_pMDLCache->GetAutoplayList( VoidPtrToMDLHandle( VirtualModel() ), pOut ); |
|
} |
|
|
|
const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const |
|
{ |
|
return g_pMDLCache->GetStudioHdr( VoidPtrToMDLHandle( cache ) ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CModelInfo::ModelHasMaterialProxy( const model_t *model ) const |
|
{ |
|
// Should we add skin & model to this function like IsUsingFBTexture()? |
|
return (model && (model->flags & MODELFLAG_MATERIALPROXY)); |
|
} |
|
|
|
bool CModelInfo::IsTranslucent( const model_t *model ) const |
|
{ |
|
return (model && (model->flags & MODELFLAG_TRANSLUCENT)); |
|
} |
|
|
|
bool CModelInfo::IsModelVertexLit( const model_t *model ) const |
|
{ |
|
// Should we add skin & model to this function like IsUsingFBTexture()? |
|
return (model && (model->flags & MODELFLAG_VERTEXLIT)); |
|
} |
|
|
|
bool CModelInfo::IsTranslucentTwoPass( const model_t *model ) const |
|
{ |
|
return (model && (model->flags & MODELFLAG_TRANSLUCENT_TWOPASS)); |
|
} |
|
|
|
bool CModelInfo::IsUsingFBTexture( const model_t *model, int nSkin, int nBody, void /*IClientRenderable*/ *pClientRenderable ) const |
|
{ |
|
bool bMightUseFbTextureThisFrame = (model && (model->flags & MODELFLAG_STUDIOHDR_USES_FB_TEXTURE)); |
|
|
|
if ( bMightUseFbTextureThisFrame ) |
|
{ |
|
// Check each material's NeedsPowerOfTwoFrameBufferTexture() virtual func |
|
switch( model->type ) |
|
{ |
|
case mod_brush: |
|
{ |
|
for (int i = 0; i < model->brush.nummodelsurfaces; ++i) |
|
{ |
|
SurfaceHandle_t surfID = SurfaceHandleFromIndex( model->brush.firstmodelsurface+i, model->brush.pShared ); |
|
IMaterial* material = MSurf_TexInfo( surfID, model->brush.pShared )->material; |
|
if ( material != NULL ) |
|
{ |
|
if ( material->NeedsPowerOfTwoFrameBufferTexture() ) |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
|
|
case mod_studio: |
|
{ |
|
IMaterial *pMaterials[ 128 ]; |
|
int materialCount = g_pStudioRender->GetMaterialListFromBodyAndSkin( model->studio, nSkin, nBody, ARRAYSIZE( pMaterials ), pMaterials ); |
|
for ( int i = 0; i < materialCount; i++ ) |
|
{ |
|
if ( pMaterials[i] != NULL ) |
|
{ |
|
// Bind material first so all material proxies execute |
|
CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); |
|
pRenderContext->Bind( pMaterials[i], pClientRenderable ); |
|
|
|
if ( pMaterials[i]->NeedsPowerOfTwoFrameBufferTexture() ) |
|
{ |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void CModelInfo::RecomputeTranslucency( const model_t *model, int nSkin, int nBody, void /*IClientRenderable*/ *pClientRenderable, float fInstanceAlphaModulate ) |
|
{ |
|
if ( model != NULL ) |
|
{ |
|
Mod_RecomputeTranslucency( (model_t *)model, nSkin, nBody, pClientRenderable, fInstanceAlphaModulate ); |
|
} |
|
} |
|
|
|
int CModelInfo::GetModelMaterialCount( const model_t *model ) const |
|
{ |
|
if (!model) |
|
return 0; |
|
return Mod_GetMaterialCount( (model_t *)model ); |
|
} |
|
|
|
void CModelInfo::GetModelMaterials( const model_t *model, int count, IMaterial** ppMaterials ) |
|
{ |
|
if (model) |
|
Mod_GetModelMaterials( (model_t *)model, count, ppMaterials ); |
|
} |
|
|
|
void CModelInfo::GetIlluminationPoint( const model_t *model, IClientRenderable *pRenderable, const Vector& origin, |
|
const QAngle& angles, Vector* pLightingOrigin ) |
|
{ |
|
Assert( model->type == mod_studio ); |
|
studiohdr_t* pStudioHdr = (studiohdr_t*)GetModelExtraData(model); |
|
if (pStudioHdr) |
|
{ |
|
R_StudioGetLightingCenter( pRenderable, pStudioHdr, origin, angles, pLightingOrigin ); |
|
} |
|
else |
|
{ |
|
*pLightingOrigin = origin; |
|
} |
|
} |
|
|
|
int CModelInfo::GetModelContents( int modelIndex ) |
|
{ |
|
const model_t *pModel = GetModel( modelIndex ); |
|
if ( pModel ) |
|
{ |
|
switch( pModel->type ) |
|
{ |
|
case mod_brush: |
|
return CM_InlineModelContents( modelIndex-1 ); |
|
|
|
// BUGBUG: Studio contents? |
|
case mod_studio: |
|
return CONTENTS_SOLID; |
|
} |
|
} |
|
return 0; |
|
} |
|
|
|
#if !defined( _RETAIL ) |
|
extern double g_flAccumulatedModelLoadTimeVCollideSync; |
|
#endif |
|
|
|
vcollide_t *CModelInfo::GetVCollide( const model_t *pModel ) |
|
{ |
|
if ( !pModel ) |
|
return NULL; |
|
|
|
if ( pModel->type == mod_studio ) |
|
{ |
|
#if !defined( _RETAIL ) |
|
double t1 = Plat_FloatTime(); |
|
#endif |
|
vcollide_t *col = g_pMDLCache->GetVCollide( pModel->studio ); |
|
#if !defined( _RETAIL ) |
|
double t2 = Plat_FloatTime(); |
|
g_flAccumulatedModelLoadTimeVCollideSync += ( t2 - t1 ); |
|
#endif |
|
return col; |
|
} |
|
|
|
int i = GetModelIndex( GetModelName( pModel ) ); |
|
if ( i >= 0 ) |
|
{ |
|
return GetVCollide( i ); |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
vcollide_t *CModelInfo::GetVCollide( int modelIndex ) |
|
{ |
|
// First model (index 0 )is is empty |
|
// Second model( index 1 ) is the world, then brushes/submodels, then players, etc. |
|
// So, we must subtract 1 from the model index to map modelindex to CM_ index |
|
// in cmodels, 0 is the world, then brushes, etc. |
|
if ( modelIndex < MAX_MODELS ) |
|
{ |
|
const model_t *pModel = GetModel( modelIndex ); |
|
if ( pModel ) |
|
{ |
|
switch( pModel->type ) |
|
{ |
|
case mod_brush: |
|
return CM_GetVCollide( modelIndex-1 ); |
|
case mod_studio: |
|
{ |
|
#if !defined( _RETAIL ) |
|
double t1 = Plat_FloatTime(); |
|
#endif |
|
vcollide_t *col = g_pMDLCache->GetVCollide( pModel->studio ); |
|
#if !defined( _RETAIL ) |
|
double t2 = Plat_FloatTime(); |
|
g_flAccumulatedModelLoadTimeVCollideSync += ( t2 - t1 ); |
|
#endif |
|
return col; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
// we may have the cmodels loaded and not know the model/mod->type yet |
|
return CM_GetVCollide( modelIndex-1 ); |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
// Client must instantiate a KeyValues, which will be filled by this method |
|
const char *CModelInfo::GetModelKeyValueText( const model_t *model ) |
|
{ |
|
if (!model || model->type != mod_studio) |
|
return NULL; |
|
|
|
studiohdr_t* pStudioHdr = g_pMDLCache->GetStudioHdr( model->studio ); |
|
if (!pStudioHdr) |
|
return NULL; |
|
|
|
return pStudioHdr->KeyValueText(); |
|
} |
|
|
|
|
|
|
|
bool CModelInfo::GetModelKeyValue( const model_t *model, CUtlBuffer &buf ) |
|
{ |
|
if (!model || model->type != mod_studio) |
|
return false; |
|
|
|
studiohdr_t* pStudioHdr = g_pMDLCache->GetStudioHdr( model->studio ); |
|
if (!pStudioHdr) |
|
return false; |
|
|
|
if ( pStudioHdr->numincludemodels == 0) |
|
{ |
|
buf.PutString( pStudioHdr->KeyValueText() ); |
|
return true; |
|
} |
|
|
|
virtualmodel_t *pVM = GetVirtualModel( pStudioHdr ); |
|
|
|
if (pVM) |
|
{ |
|
for (int i = 0; i < pVM->m_group.Count(); i++) |
|
{ |
|
const studiohdr_t* pSubStudioHdr = pVM->m_group[i].GetStudioHdr(); |
|
if (pSubStudioHdr && pSubStudioHdr->KeyValueText()) |
|
{ |
|
buf.PutString( pSubStudioHdr->KeyValueText() ); |
|
} |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *model - |
|
// Output : float |
|
//----------------------------------------------------------------------------- |
|
float CModelInfo::GetModelRadius( const model_t *model ) |
|
{ |
|
if ( !model ) |
|
return 0.0f; |
|
return model->radius; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Lovely studiohdrs |
|
//----------------------------------------------------------------------------- |
|
studiohdr_t *CModelInfo::GetStudiomodel( const model_t *model ) |
|
{ |
|
if ( model->type == mod_studio ) |
|
return g_pMDLCache->GetStudioHdr( model->studio ); |
|
|
|
return NULL; |
|
} |
|
|
|
CPhysCollide *CModelInfo::GetCollideForVirtualTerrain( int index ) |
|
{ |
|
return CM_PhysCollideForDisp( index ); |
|
} |
|
|
|
// Returns planes of non-nodraw brush model surfaces |
|
int CModelInfo::GetBrushModelPlaneCount( const model_t *model ) const |
|
{ |
|
if ( !model || model->type != mod_brush ) |
|
return 0; |
|
|
|
return R_GetBrushModelPlaneCount( model ); |
|
} |
|
|
|
void CModelInfo::GetBrushModelPlane( const model_t *model, int nIndex, cplane_t &plane, Vector *pOrigin ) const |
|
{ |
|
if ( !model || model->type != mod_brush ) |
|
return; |
|
|
|
plane = R_GetBrushModelPlane( model, nIndex, pOrigin ); |
|
} |
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// implementation of IVModelInfo for server |
|
//----------------------------------------------------------------------------- |
|
class CModelInfoServer : public CModelInfo |
|
{ |
|
public: |
|
virtual int RegisterDynamicModel( const char *name, bool bClientSideOnly ); |
|
virtual const model_t *GetModel( int modelindex ); |
|
virtual const model_t *FindOrLoadModel( const char *name ); |
|
virtual void OnDynamicModelsStringTableChange( int nStringIndex, const char *pString, const void *pData ); |
|
|
|
virtual void GetModelMaterialColorAndLighting( const model_t *model, const Vector& origin, |
|
const QAngle& angles, trace_t* pTrace, Vector& lighting, Vector& matColor ); |
|
|
|
protected: |
|
virtual INetworkStringTable *GetDynamicModelStringTable() const; |
|
virtual int LookupPrecachedModelIndex( const char *name ) const; |
|
}; |
|
|
|
INetworkStringTable *CModelInfoServer::GetDynamicModelStringTable() const |
|
{ |
|
return sv.GetDynamicModelsTable(); |
|
} |
|
|
|
int CModelInfoServer::LookupPrecachedModelIndex( const char *name ) const |
|
{ |
|
return sv.LookupModelIndex( name ); |
|
} |
|
|
|
int CModelInfoServer::RegisterDynamicModel( const char *name, bool bClientSide ) |
|
{ |
|
// Server should not know about client-side dynamic models! |
|
Assert( !bClientSide ); |
|
if ( bClientSide ) |
|
return -1; |
|
|
|
char buf[256]; |
|
V_strncpy( buf, name, ARRAYSIZE(buf) ); |
|
V_RemoveDotSlashes( buf, '/', true ); |
|
name = buf; |
|
|
|
Assert( V_strnicmp( name, "models/", 7 ) == 0 && V_strstr( name, ".mdl" ) != NULL ); |
|
|
|
// Already known? bClientSide should always be false and is asserted above. |
|
int index = GetModelIndex( name ); |
|
if ( index != -1 ) |
|
return index; |
|
|
|
INetworkStringTable *pTable = GetDynamicModelStringTable(); |
|
Assert( pTable ); |
|
if ( !pTable ) |
|
return -1; |
|
|
|
// Register this model with the dynamic model string table |
|
Assert( pTable->FindStringIndex( name ) == INVALID_STRING_INDEX ); |
|
bool bWasLocked = static_cast<CNetworkStringTable_LockOverride*>( pTable )->LockWithRetVal( false ); |
|
char nIsLoaded = 0; |
|
int netidx = pTable->AddString( true, name, 1, &nIsLoaded ); |
|
static_cast<CNetworkStringTable*>( pTable )->Lock( bWasLocked ); |
|
|
|
// And also cache the model_t* pointer at this time |
|
GrowNetworkedDynamicModels( netidx ); |
|
m_NetworkedDynamicModels[ netidx ] = modelloader->GetDynamicModel( name, bClientSide ); |
|
|
|
Assert( MODEL_TO_NETDYNAMIC( ( short ) NETDYNAMIC_TO_MODEL( netidx ) ) == netidx ); |
|
return NETDYNAMIC_TO_MODEL( netidx ); |
|
} |
|
|
|
const model_t *CModelInfoServer::GetModel( int modelindex ) |
|
{ |
|
if ( IsDynamicModelIndex( modelindex ) ) |
|
return LookupDynamicModel( modelindex ); |
|
|
|
return sv.GetModel( modelindex ); |
|
} |
|
|
|
void CModelInfoServer::OnDynamicModelsStringTableChange( int nStringIndex, const char *pString, const void *pData ) |
|
{ |
|
AssertMsg( false, "CModelInfoServer::OnDynamicModelsStringTableChange should never be called" ); |
|
} |
|
|
|
void CModelInfoServer::GetModelMaterialColorAndLighting( const model_t *model, const Vector& origin, |
|
const QAngle& angles, trace_t* pTrace, Vector& lighting, Vector& matColor ) |
|
{ |
|
Msg( "GetModelMaterialColorAndLighting: Available on client only!\n" ); |
|
} |
|
|
|
const model_t *CModelInfoServer::FindOrLoadModel( const char *name ) |
|
{ |
|
AssertMsg( false, "CModelInfoServer::FindOrLoadModel should never be called" ); |
|
return NULL; |
|
} |
|
|
|
|
|
static CModelInfoServer g_ModelInfoServer; |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CModelInfoServer, IVModelInfo003, VMODELINFO_SERVER_INTERFACE_VERSION_3, g_ModelInfoServer ); |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CModelInfoServer, IVModelInfo, VMODELINFO_SERVER_INTERFACE_VERSION, g_ModelInfoServer ); |
|
|
|
// Expose IVModelInfo to the engine |
|
IVModelInfo *modelinfo = &g_ModelInfoServer; |
|
|
|
|
|
#ifndef SWDS |
|
//----------------------------------------------------------------------------- |
|
// implementation of IVModelInfo for client |
|
//----------------------------------------------------------------------------- |
|
class CModelInfoClient : public CModelInfo |
|
{ |
|
public: |
|
virtual int RegisterDynamicModel( const char *name, bool bClientSideOnly ); |
|
virtual const model_t *GetModel( int modelindex ); |
|
virtual const model_t *FindOrLoadModel( const char *name ); |
|
virtual void OnDynamicModelsStringTableChange( int nStringIndex, const char *pString, const void *pData ); |
|
|
|
// Sets/gets a map-specified fade range |
|
virtual void SetLevelScreenFadeRange( float flMinSize, float flMaxSize ); |
|
virtual void GetLevelScreenFadeRange( float *pMinArea, float *pMaxArea ) const; |
|
|
|
// Sets/gets a map-specified per-view fade range |
|
virtual void SetViewScreenFadeRange( float flMinSize, float flMaxSize ); |
|
|
|
// Computes fade alpha based on distance fade + screen fade |
|
virtual unsigned char ComputeLevelScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const; |
|
virtual unsigned char ComputeViewScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const; |
|
|
|
virtual void GetModelMaterialColorAndLighting( const model_t *model, const Vector& origin, |
|
const QAngle& angles, trace_t* pTrace, Vector& lighting, Vector& matColor ); |
|
|
|
protected: |
|
virtual INetworkStringTable *GetDynamicModelStringTable() const; |
|
virtual int LookupPrecachedModelIndex( const char *name ) const; |
|
|
|
private: |
|
struct ScreenFadeInfo_t |
|
{ |
|
float m_flMinScreenWidth; |
|
float m_flMaxScreenWidth; |
|
float m_flFalloffFactor; |
|
}; |
|
|
|
// Sets/gets a map-specified fade range |
|
void SetScreenFadeRange( float flMinSize, float flMaxSize, ScreenFadeInfo_t *pFade ); |
|
unsigned char ComputeScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale, const ScreenFadeInfo_t &fade ) const; |
|
|
|
ScreenFadeInfo_t m_LevelFade; |
|
ScreenFadeInfo_t m_ViewFade; |
|
}; |
|
|
|
INetworkStringTable *CModelInfoClient::GetDynamicModelStringTable() const |
|
{ |
|
return cl.m_pDynamicModelsTable; |
|
} |
|
|
|
int CModelInfoClient::LookupPrecachedModelIndex( const char *name ) const |
|
{ |
|
return cl.LookupModelIndex( name ); |
|
} |
|
|
|
int CModelInfoClient::RegisterDynamicModel( const char *name, bool bClientSide ) |
|
{ |
|
// Clients cannot register non-client-side dynamic models! |
|
Assert( bClientSide ); |
|
if ( !bClientSide ) |
|
return -1; |
|
|
|
char buf[256]; |
|
V_strncpy( buf, name, ARRAYSIZE(buf) ); |
|
V_RemoveDotSlashes( buf, '/', true ); |
|
name = buf; |
|
|
|
Assert( V_strstr( name, ".mdl" ) != NULL ); |
|
|
|
// Already known? bClientSide should always be true and is asserted above. |
|
int index = GetModelClientSideIndex( name ); |
|
if ( index != -1 ) |
|
return index; |
|
|
|
// Lookup (or create) model_t* and register it to get a stable iterator index |
|
model_t* pModel = modelloader->GetDynamicModel( name, true ); |
|
Assert( pModel ); |
|
UtlHashHandle_t localidx = m_ClientDynamicModels.Insert( pModel ); |
|
Assert( m_ClientDynamicModels.Count() < ((32767 >> 1) - 2) ); |
|
Assert( MODEL_TO_CLIENTSIDE( (short) CLIENTSIDE_TO_MODEL( localidx ) ) == (int) localidx ); |
|
return CLIENTSIDE_TO_MODEL( localidx ); |
|
} |
|
|
|
const model_t *CModelInfoClient::GetModel( int modelindex ) |
|
{ |
|
if ( IsDynamicModelIndex( modelindex ) ) |
|
return LookupDynamicModel( modelindex ); |
|
|
|
return cl.GetModel( modelindex ); |
|
} |
|
|
|
void CModelInfoClient::OnDynamicModelsStringTableChange( int nStringIndex, const char *pString, const void *pData ) |
|
{ |
|
// Do a lookup to force an immediate insertion into our local lookup tables |
|
model_t* pModel = LookupDynamicModel( NETDYNAMIC_TO_MODEL( nStringIndex ) ); |
|
|
|
// Notify model loader that the server-side state may have changed |
|
bool bServerLoaded = pData ? ( *(char*)pData != 0 ) : ( g_ClientGlobalVariables.network_protocol <= PROTOCOL_VERSION_20 ); |
|
modelloader->Client_OnServerModelStateChanged( pModel, bServerLoaded ); |
|
} |
|
|
|
const model_t *CModelInfoClient::FindOrLoadModel( const char *name ) |
|
{ |
|
// find the cached model from the server or client |
|
const model_t *pModel = GetModel( GetModelIndex( name ) ); |
|
if ( pModel ) |
|
return pModel; |
|
|
|
// load the model |
|
return modelloader->GetModelForName( name, IModelLoader::FMODELLOADER_CLIENTDLL ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets/gets a map-specified fade range |
|
//----------------------------------------------------------------------------- |
|
void CModelInfoClient::SetScreenFadeRange( float flMinSize, float flMaxSize, ScreenFadeInfo_t *pFade ) |
|
{ |
|
pFade->m_flMinScreenWidth = flMinSize; |
|
pFade->m_flMaxScreenWidth = flMaxSize; |
|
if ( pFade->m_flMaxScreenWidth <= pFade->m_flMinScreenWidth ) |
|
{ |
|
pFade->m_flMaxScreenWidth = pFade->m_flMinScreenWidth; |
|
} |
|
|
|
if (pFade->m_flMaxScreenWidth != pFade->m_flMinScreenWidth) |
|
{ |
|
pFade->m_flFalloffFactor = 255.0f / (pFade->m_flMaxScreenWidth - pFade->m_flMinScreenWidth); |
|
} |
|
else |
|
{ |
|
pFade->m_flFalloffFactor = 255.0f; |
|
} |
|
} |
|
|
|
void CModelInfoClient::SetLevelScreenFadeRange( float flMinSize, float flMaxSize ) |
|
{ |
|
SetScreenFadeRange( flMinSize, flMaxSize, &m_LevelFade ); |
|
} |
|
|
|
void CModelInfoClient::GetLevelScreenFadeRange( float *pMinArea, float *pMaxArea ) const |
|
{ |
|
*pMinArea = m_LevelFade.m_flMinScreenWidth; |
|
*pMaxArea = m_LevelFade.m_flMaxScreenWidth; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets/gets a map-specified per-view fade range |
|
//----------------------------------------------------------------------------- |
|
void CModelInfoClient::SetViewScreenFadeRange( float flMinSize, float flMaxSize ) |
|
{ |
|
SetScreenFadeRange( flMinSize, flMaxSize, &m_ViewFade ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Computes fade alpha based on distance fade + screen fade |
|
//----------------------------------------------------------------------------- |
|
inline unsigned char CModelInfoClient::ComputeScreenFade( const Vector &vecAbsOrigin, |
|
float flRadius, float flFadeScale, const ScreenFadeInfo_t &fade ) const |
|
{ |
|
if ( ( fade.m_flMinScreenWidth <= 0 ) || (flFadeScale <= 0.0f) ) |
|
return 255; |
|
|
|
CMatRenderContextPtr pRenderContext( materials ); |
|
|
|
float flPixelWidth = pRenderContext->ComputePixelWidthOfSphere( vecAbsOrigin, flRadius ) / flFadeScale; |
|
|
|
unsigned char alpha = 0; |
|
if ( flPixelWidth > fade.m_flMinScreenWidth ) |
|
{ |
|
if ( (fade.m_flMaxScreenWidth >= 0) && (flPixelWidth < fade.m_flMaxScreenWidth) ) |
|
{ |
|
int nAlpha = fade.m_flFalloffFactor * (flPixelWidth - fade.m_flMinScreenWidth); |
|
alpha = clamp( nAlpha, 0, 255 ); |
|
} |
|
else |
|
{ |
|
alpha = 255; |
|
} |
|
} |
|
|
|
return alpha; |
|
} |
|
|
|
unsigned char CModelInfoClient::ComputeLevelScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const |
|
{ |
|
if ( IsXbox() ) |
|
{ |
|
return 255; |
|
} |
|
else |
|
{ |
|
return ComputeScreenFade( vecAbsOrigin, flRadius, flFadeScale, m_LevelFade ); |
|
} |
|
} |
|
|
|
unsigned char CModelInfoClient::ComputeViewScreenFade( const Vector &vecAbsOrigin, float flRadius, float flFadeScale ) const |
|
{ |
|
return ComputeScreenFade( vecAbsOrigin, flRadius, flFadeScale, m_ViewFade ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// A method to get the material color + texture coordinate |
|
//----------------------------------------------------------------------------- |
|
IMaterial* BrushModel_GetLightingAndMaterial( const Vector &start, |
|
const Vector &end, Vector &diffuseLightColor, Vector &baseColor) |
|
{ |
|
float textureS, textureT; |
|
IMaterial *material; |
|
|
|
// TEMP initialize these values until we can find why R_LightVec is not assigning values to them |
|
textureS = 0; |
|
textureT = 0; |
|
|
|
SurfaceHandle_t surfID = R_LightVec( start, end, true, diffuseLightColor, &textureS, &textureT ); |
|
if( !IS_SURF_VALID( surfID ) || !MSurf_TexInfo( surfID ) ) |
|
{ |
|
// ConMsg( "didn't hit anything\n" ); |
|
return 0; |
|
} |
|
else |
|
{ |
|
material = MSurf_TexInfo( surfID )->material; |
|
if ( material ) |
|
{ |
|
material->GetLowResColorSample( textureS, textureT, baseColor.Base() ); |
|
// ConMsg( "%s: diff: %f %f %f base: %f %f %f\n", material->GetName(), diffuseLightColor[0], diffuseLightColor[1], diffuseLightColor[2], baseColor[0], baseColor[1], baseColor[2] ); |
|
} |
|
else |
|
{ |
|
baseColor.Init(); |
|
} |
|
return material; |
|
} |
|
} |
|
|
|
void CModelInfoClient::GetModelMaterialColorAndLighting( const model_t *model, const Vector & origin, |
|
const QAngle & angles, trace_t* pTrace, Vector& lighting, Vector& matColor ) |
|
{ |
|
switch( model->type ) |
|
{ |
|
case mod_brush: |
|
{ |
|
Vector origin_l, delta, delta_l; |
|
VectorSubtract( pTrace->endpos, pTrace->startpos, delta ); |
|
|
|
// subtract origin offset |
|
VectorSubtract (pTrace->startpos, origin, origin_l); |
|
|
|
// rotate start and end into the models frame of reference |
|
if (angles[0] || angles[1] || angles[2]) |
|
{ |
|
Vector forward, right, up; |
|
AngleVectors (angles, &forward, &right, &up); |
|
|
|
// transform the direction into the local space of this entity |
|
delta_l[0] = DotProduct (delta, forward); |
|
delta_l[1] = -DotProduct (delta, right); |
|
delta_l[2] = DotProduct (delta, up); |
|
} |
|
else |
|
{ |
|
VectorCopy( delta, delta_l ); |
|
} |
|
|
|
Vector end_l; |
|
VectorMA( origin_l, 1.1f, delta_l, end_l ); |
|
|
|
R_LightVecUseModel( ( model_t * )model ); |
|
BrushModel_GetLightingAndMaterial( origin_l, end_l, lighting, matColor ); |
|
R_LightVecUseModel(); |
|
return; |
|
} |
|
|
|
case mod_studio: |
|
{ |
|
// FIXME: Need some way of getting the material! |
|
matColor.Init( 0.5f, 0.5f, 0.5f ); |
|
|
|
// Get the lighting at the point |
|
LightingState_t lightingState; |
|
LightcacheGetDynamic_Stats stats; |
|
LightcacheGetDynamic( pTrace->endpos, lightingState, stats, LIGHTCACHEFLAGS_STATIC|LIGHTCACHEFLAGS_DYNAMIC|LIGHTCACHEFLAGS_LIGHTSTYLE|LIGHTCACHEFLAGS_ALLOWFAST ); |
|
// Convert the light parameters into something studiorender can digest |
|
LightDesc_t desc[MAXLOCALLIGHTS]; |
|
int count = 0; |
|
for (int i = 0; i < lightingState.numlights; ++i) |
|
{ |
|
if (WorldLightToMaterialLight( lightingState.locallight[i], desc[count] )) |
|
{ |
|
++count; |
|
} |
|
} |
|
|
|
// Ask studiorender to figure out the lighting |
|
g_pStudioRender->ComputeLighting( lightingState.r_boxcolor, |
|
count, desc, pTrace->endpos, pTrace->plane.normal, lighting ); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
static CModelInfoClient g_ModelInfoClient; |
|
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CModelInfoClient, IVModelInfoClient, VMODELINFO_CLIENT_INTERFACE_VERSION, g_ModelInfoClient ); |
|
|
|
// Expose IVModelInfo to the engine |
|
IVModelInfoClient *modelinfoclient = &g_ModelInfoClient; |
|
|
|
#endif
|
|
|