summaryrefslogtreecommitdiff
path: root/engine/ModelInfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engine/ModelInfo.cpp')
-rw-r--r--engine/ModelInfo.cpp1308
1 files changed, 1308 insertions, 0 deletions
diff --git a/engine/ModelInfo.cpp b/engine/ModelInfo.cpp
new file mode 100644
index 0000000..e5ea9a0
--- /dev/null
+++ b/engine/ModelInfo.cpp
@@ -0,0 +1,1308 @@
+//========= 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 Mix32HashFunctor()( (uint32)( p->fnHandle ) ); }
+ uint operator()( FileNameHandle_t fn ) const { return Mix32HashFunctor()( (uint32) 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( (MDLHandle_t)(int)cache&0xffff );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return virtualmodel_t block associated with model_t
+//-----------------------------------------------------------------------------
+virtualmodel_t *CModelInfo::GetVirtualModel( const studiohdr_t *pStudioHdr ) const
+{
+ MDLHandle_t handle = (MDLHandle_t)(int)pStudioHdr->virtualModel&0xffff;
+ return g_pMDLCache->GetVirtualModelFast( pStudioHdr, handle );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+byte *CModelInfo::GetAnimBlock( const studiohdr_t *pStudioHdr, int nBlock ) const
+{
+ MDLHandle_t handle = (MDLHandle_t)(int)pStudioHdr->virtualModel&0xffff;
+ return g_pMDLCache->GetAnimBlock( handle, nBlock );
+}
+
+int CModelInfo::GetAutoplayList( const studiohdr_t *pStudioHdr, unsigned short **pAutoplayList ) const
+{
+ MDLHandle_t handle = (MDLHandle_t)(int)pStudioHdr->virtualModel&0xffff;
+ 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, (MDLHandle_t)(int)virtualModel&0xffff );
+}
+
+byte *studiohdr_t::GetAnimBlock( int i ) const
+{
+ return g_pMDLCache->GetAnimBlock( (MDLHandle_t)(int)virtualModel&0xffff, i );
+}
+
+int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const
+{
+ return g_pMDLCache->GetAutoplayList( (MDLHandle_t)(int)virtualModel&0xffff, pOut );
+}
+
+const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const
+{
+ return g_pMDLCache->GetStudioHdr( (MDLHandle_t)(int)cache&0xffff );
+}
+
+
+//-----------------------------------------------------------------------------
+// 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