//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//===========================================================================//
# include <stdlib.h>
# include "tier0/platform.h"
# include "studiorendercontext.h"
# include "optimize.h"
# include "materialsystem/imaterialvar.h"
# include "materialsystem/imesh.h"
# include "materialsystem/imorph.h"
# include "materialsystem/ivballoctracker.h"
# include "vstdlib/random.h"
# include "tier0/tslist.h"
# include "tier0/platform.h"
# include "tier1/refcount.h"
# include "tier1/callqueue.h"
# include "cmodel.h"
# include "tier0/vprof.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
// garymcthack - this should go elsewhere
# define MAX_NUM_BONE_INDICES 4
//-----------------------------------------------------------------------------
// Toggles studio queued mode
//-----------------------------------------------------------------------------
void StudioChangeCallback ( IConVar * var , const char * pOldValue , float flOldValue )
{
// NOTE: This is necessary to flush the queued thread when this value changes
MaterialLock_t hLock = g_pMaterialSystem - > Lock ( ) ;
g_pMaterialSystem - > Unlock ( hLock ) ;
}
static ConVar studio_queue_mode ( " studio_queue_mode " , " 1 " , 0 , " " , StudioChangeCallback ) ;
//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
static float s_pZeroFlexWeights [ MAXSTUDIOFLEXDESC ] ;
//-----------------------------------------------------------------------------
// Singleton instance
//-----------------------------------------------------------------------------
IStudioDataCache * g_pStudioDataCache = NULL ;
static CStudioRenderContext s_StudioRenderContext ;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR ( CStudioRenderContext , IStudioRender ,
STUDIO_RENDER_INTERFACE_VERSION , s_StudioRenderContext ) ;
//-----------------------------------------------------------------------------
// Constructor, destructor
//-----------------------------------------------------------------------------
CStudioRenderContext : : CStudioRenderContext ( )
{
// Initialize render context
m_RC . m_pForcedMaterial = NULL ;
m_RC . m_nForcedMaterialType = OVERRIDE_NORMAL ;
m_RC . m_ColorMod [ 0 ] = m_RC . m_ColorMod [ 1 ] = m_RC . m_ColorMod [ 2 ] = 1.0f ;
m_RC . m_AlphaMod = 1.0f ;
m_RC . m_ViewOrigin . Init ( ) ;
m_RC . m_ViewRight . Init ( ) ;
m_RC . m_ViewUp . Init ( ) ;
m_RC . m_ViewPlaneNormal . Init ( ) ;
m_RC . m_Config . m_bEnableHWMorph = true ;
m_RC . m_Config . m_bStatsMode = false ;
m_RC . m_NumLocalLights = 0 ;
for ( int i = 0 ; i < 6 ; + + i )
{
m_RC . m_LightBoxColors [ i ] . Init ( 0 , 0 , 0 ) ;
}
}
CStudioRenderContext : : ~ CStudioRenderContext ( )
{
}
//-----------------------------------------------------------------------------
// Connect, disconnect
//-----------------------------------------------------------------------------
bool CStudioRenderContext : : Connect ( CreateInterfaceFn factory )
{
if ( ! BaseClass : : Connect ( factory ) )
return false ;
g_pStudioDataCache = ( IStudioDataCache * ) factory ( STUDIO_DATA_CACHE_INTERFACE_VERSION , NULL ) ;
if ( ! g_pMaterialSystem | | ! g_pMaterialSystemHardwareConfig | | ! g_pStudioDataCache )
{
Msg ( " StudioRender failed to connect to a required system \n " ) ;
}
return ( g_pMaterialSystem & & g_pMaterialSystemHardwareConfig & & g_pStudioDataCache ) ;
}
void CStudioRenderContext : : Disconnect ( )
{
g_pStudioDataCache = NULL ;
BaseClass : : Disconnect ( ) ;
}
//-----------------------------------------------------------------------------
// Here's where systems can access other interfaces implemented by this object
// Returns NULL if it doesn't implement the requested interface
//-----------------------------------------------------------------------------
void * CStudioRenderContext : : QueryInterface ( const char * pInterfaceName )
{
// Loading the studiorender DLL mounts *all* interfaces
CreateInterfaceFn factory = Sys_GetFactoryThis ( ) ; // This silly construction is necessary
return factory ( pInterfaceName , NULL ) ; // to prevent the LTCG compiler from crashing.
}
//-----------------------------------------------------------------------------
// Init, shutdown
//-----------------------------------------------------------------------------
InitReturnVal_t CStudioRenderContext : : Init ( )
{
MathLib_Init ( 2.2f , 2.2f , 0.0f , 2.0f ) ;
InitReturnVal_t nRetVal = BaseClass : : Init ( ) ;
if ( nRetVal ! = INIT_OK )
return nRetVal ;
if ( ! g_pMaterialSystem | | ! g_pMaterialSystemHardwareConfig )
return INIT_FAILED ;
return g_pStudioRenderImp - > Init ( ) ;
}
void CStudioRenderContext : : Shutdown ( void )
{
g_pStudioRenderImp - > Shutdown ( ) ;
BaseClass : : Shutdown ( ) ;
}
//-----------------------------------------------------------------------------
// Used to activate the stub material system.
//-----------------------------------------------------------------------------
void CStudioRenderContext : : Mat_Stub ( IMaterialSystem * pMatSys )
{
g_pMaterialSystem = pMatSys ;
}
//-----------------------------------------------------------------------------
// Determines material flags
//-----------------------------------------------------------------------------
void CStudioRenderContext : : ComputeMaterialFlags ( studiohdr_t * phdr , studioloddata_t & lodData , IMaterial * pMaterial )
{
// requesting info forces the initial material precache (and its build out)
if ( pMaterial - > UsesEnvCubemap ( ) )
{
phdr - > flags | = STUDIOHDR_FLAGS_USES_ENV_CUBEMAP ;
}
if ( pMaterial - > NeedsPowerOfTwoFrameBufferTexture ( false ) ) // The false checks if it will ever need the frame buffer, not just this frame
{
phdr - > flags | = STUDIOHDR_FLAGS_USES_FB_TEXTURE ;
}
// FIXME: I'd rather know that the material is definitely using the bumpmap.
// It could be in the file without actually being used.
static unsigned int bumpvarCache = 0 ;
IMaterialVar * pBumpMatVar = pMaterial - > FindVarFast ( " $bumpmap " , & bumpvarCache ) ;
if ( pBumpMatVar & & pBumpMatVar - > IsDefined ( ) & & pMaterial - > NeedsTangentSpace ( ) )
{
phdr - > flags | = STUDIOHDR_FLAGS_USES_BUMPMAPPING ;
}
// Make sure material is treated as bump mapped if phong is set
static unsigned int phongVarCache = 0 ;
IMaterialVar * pPhongMatVar = pMaterial - > FindVarFast ( " $phong " , & phongVarCache ) ;
if ( pPhongMatVar & & pPhongMatVar - > IsDefined ( ) & & ( pPhongMatVar - > GetIntValue ( ) ! = 0 ) )
{
phdr - > flags | = STUDIOHDR_FLAGS_USES_BUMPMAPPING ;
}
}
//-----------------------------------------------------------------------------
// Does this material use a mouth shader?
//-----------------------------------------------------------------------------
static bool UsesMouthShader ( IMaterial * pMaterial )
{
// FIXME: hack, needs proper client side material system interface
static unsigned int clientShaderCache = 0 ;
IMaterialVar * clientShaderVar = pMaterial - > FindVarFast ( " $clientShader " , & clientShaderCache ) ;
if ( clientShaderVar )
return ( Q_stricmp ( clientShaderVar - > GetStringValue ( ) , " MouthShader " ) = = 0 ) ;
return false ;
}
//-----------------------------------------------------------------------------
// Returns the actual texture name to use on the model
//-----------------------------------------------------------------------------
static const char * GetTextureName ( studiohdr_t * phdr , OptimizedModel : : FileHeader_t * pVtxHeader ,
int lodID , int inMaterialID )
{
OptimizedModel : : MaterialReplacementListHeader_t * materialReplacementList =
pVtxHeader - > pMaterialReplacementList ( lodID ) ;
int i ;
for ( i = 0 ; i < materialReplacementList - > numReplacements ; i + + )
{
OptimizedModel : : MaterialReplacementHeader_t * materialReplacement =
materialReplacementList - > pMaterialReplacement ( i ) ;
if ( materialReplacement - > materialID = = inMaterialID )
{
const char * str = materialReplacement - > pMaterialReplacementName ( ) ;
return str ;
}
}
return phdr - > pTexture ( inMaterialID ) - > pszName ( ) ;
}
//-----------------------------------------------------------------------------
// Loads materials associated with a particular LOD of a model
//-----------------------------------------------------------------------------
void CStudioRenderContext : : LoadMaterials ( studiohdr_t * phdr ,
OptimizedModel : : FileHeader_t * pVtxHeader , studioloddata_t & lodData , int lodID )
{
typedef IMaterial * IMaterialPtr ;
Assert ( phdr ) ;
lodData . numMaterials = phdr - > numtextures ;
if ( lodData . numMaterials = = 0 )
{
lodData . ppMaterials = NULL ;
return ;
}
lodData . ppMaterials = new IMaterialPtr [ lodData . numMaterials ] ;
Assert ( lodData . ppMaterials ) ;
lodData . pMaterialFlags = new int [ lodData . numMaterials ] ;
Assert ( lodData . pMaterialFlags ) ;
int i , j ;
// get index of each material
// set the runtime studiohdr flags that are material derived
if ( phdr - > textureindex = = 0 )
return ;
for ( i = 0 ; i < phdr - > numtextures ; i + + )
{
char szPath [ MAX_PATH ] ;
IMaterial * pMaterial = NULL ;
// search through all specified directories until a valid material is found
for ( j = 0 ; j < phdr - > numcdtextures & & IsErrorMaterial ( pMaterial ) ; j + + )
{
// If we don't do this, we get filenames like "materials\\blah.vmt".
const char * textureName = GetTextureName ( phdr , pVtxHeader , lodID , i ) ;
if ( textureName [ 0 ] = = CORRECT_PATH_SEPARATOR | | textureName [ 0 ] = = INCORRECT_PATH_SEPARATOR )
+ + textureName ;
// This prevents filenames like /models/blah.vmt.
const char * pCdTexture = phdr - > pCdtexture ( j ) ;
if ( pCdTexture [ 0 ] = = CORRECT_PATH_SEPARATOR | | pCdTexture [ 0 ] = = INCORRECT_PATH_SEPARATOR )
+ + pCdTexture ;
V_ComposeFileName ( pCdTexture , textureName , szPath , sizeof ( szPath ) ) ;
if ( phdr - > flags & STUDIOHDR_FLAGS_OBSOLETE )
{
pMaterial = g_pMaterialSystem - > FindMaterial ( " models/obsolete/obsolete " , TEXTURE_GROUP_MODEL , false ) ;
if ( IsErrorMaterial ( pMaterial ) )
{
Warning ( " StudioRender: OBSOLETE material missing: \" models/obsolete/obsolete \" \n " ) ;
}
}
else
{
pMaterial = g_pMaterialSystem - > FindMaterial ( szPath , TEXTURE_GROUP_MODEL , false ) ;
}
}
if ( IsErrorMaterial ( pMaterial ) )
{
// hack - if it isn't found, go through the motions of looking for it again
// so that the materialsystem will give an error.
char szPrefix [ 256 ] ;
Q_strncpy ( szPrefix , phdr - > pszName ( ) , sizeof ( szPrefix ) ) ;
Q_strncat ( szPrefix , " : " , sizeof ( szPrefix ) , COPY_ALL_CHARACTERS ) ;
for ( j = 0 ; j < phdr - > numcdtextures ; j + + )
{
Q_strncpy ( szPath , phdr - > pCdtexture ( j ) , sizeof ( szPath ) ) ;
const char * textureName = GetTextureName ( phdr , pVtxHeader , lodID , i ) ;
Q_strncat ( szPath , textureName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
g_pMaterialSystem - > FindMaterial ( szPath , TEXTURE_GROUP_MODEL , true , szPrefix ) ;
}
}
lodData . ppMaterials [ i ] = pMaterial ;
if ( pMaterial )
{
// Increment the reference count for the material.
pMaterial - > IncrementReferenceCount ( ) ;
ComputeMaterialFlags ( phdr , lodData , pMaterial ) ;
lodData . pMaterialFlags [ i ] = UsesMouthShader ( pMaterial ) ? 1 : 0 ;
}
}
}
//-----------------------------------------------------------------------------
// Suppresses all hw morphs on a model
//-----------------------------------------------------------------------------
static void SuppressAllHWMorphs ( mstudiomodel_t * pModel , OptimizedModel : : ModelLODHeader_t * pVtxLOD )
{
for ( int k = 0 ; k < pModel - > nummeshes ; + + k )
{
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( k ) ;
for ( int i = 0 ; i < pVtxMesh - > numStripGroups ; + + i )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( i ) ;
if ( ( pStripGroup - > flags & OptimizedModel : : STRIPGROUP_IS_DELTA_FLEXED ) )
{
pStripGroup - > flags | = OptimizedModel : : STRIPGROUP_SUPPRESS_HW_MORPH ;
}
}
}
}
//-----------------------------------------------------------------------------
// Computes the total flexes on a model
//-----------------------------------------------------------------------------
static int ComputeTotalFlexCount ( mstudiomodel_t * pModel )
{
int nFlexCount = 0 ;
for ( int k = 0 ; k < pModel - > nummeshes ; + + k )
{
mstudiomesh_t * pMesh = pModel - > pMesh ( k ) ;
nFlexCount + = pMesh - > numflexes ;
}
return nFlexCount ;
}
//-----------------------------------------------------------------------------
// Count deltas affecting a particular stripgroup
//-----------------------------------------------------------------------------
int CStudioRenderContext : : CountDeltaFlexedStripGroups ( mstudiomodel_t * pModel , OptimizedModel : : ModelLODHeader_t * pVtxLOD )
{
int nFlexedStripGroupCount = 0 ;
for ( int k = 0 ; k < pModel - > nummeshes ; + + k )
{
Assert ( pModel - > nummeshes = = pVtxLOD - > numMeshes ) ;
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( k ) ;
for ( int i = 0 ; i < pVtxMesh - > numStripGroups ; + + i )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( i ) ;
if ( ( pStripGroup - > flags & OptimizedModel : : STRIPGROUP_IS_DELTA_FLEXED ) = = 0 )
continue ;
+ + nFlexedStripGroupCount ;
}
}
return nFlexedStripGroupCount ;
}
//-----------------------------------------------------------------------------
// Count vertices affected by deltas in a particular strip group
//-----------------------------------------------------------------------------
int CStudioRenderContext : : CountFlexedVertices ( mstudiomesh_t * pMesh , OptimizedModel : : StripGroupHeader_t * pStripGroup )
{
if ( ! pMesh - > numflexes )
return 0 ;
// an inverse mapping from mesh index to strip group index
unsigned short * pMeshIndexToGroupIndex = ( unsigned short * ) _alloca ( pMesh - > pModel ( ) - > numvertices * sizeof ( unsigned short ) ) ;
memset ( pMeshIndexToGroupIndex , 0xFF , pMesh - > pModel ( ) - > numvertices * sizeof ( unsigned short ) ) ;
for ( int i = 0 ; i < pStripGroup - > numVerts ; + + i )
{
int nMeshVert = pStripGroup - > pVertex ( i ) - > origMeshVertID ;
pMeshIndexToGroupIndex [ nMeshVert ] = ( unsigned short ) i ;
}
int nFlexVertCount = 0 ;
for ( int i = 0 ; i < pMesh - > numflexes ; + + i )
{
mstudioflex_t * pFlex = pMesh - > pFlex ( i ) ;
byte * pVAnim = pFlex - > pBaseVertanim ( ) ;
int nVAnimSizeBytes = pFlex - > VertAnimSizeBytes ( ) ;
for ( int j = 0 ; j < pFlex - > numverts ; + + j )
{
mstudiovertanim_t * pAnim = ( mstudiovertanim_t * ) ( pVAnim + j * nVAnimSizeBytes ) ;
int nMeshVert = pAnim - > index ;
unsigned short nGroupVert = pMeshIndexToGroupIndex [ nMeshVert ] ;
// In this case, this vertex is not part of this meshgroup. Ignore it.
if ( nGroupVert ! = 0xFFFF )
{
// Only count it once
pMeshIndexToGroupIndex [ nMeshVert ] = 0xFFFF ;
+ + nFlexVertCount ;
}
}
}
return nFlexVertCount ;
}
//-----------------------------------------------------------------------------
// Determine if any strip groups shouldn't be morphed
//-----------------------------------------------------------------------------
static int * s_pVertexCount ;
static int SortVertCount ( const void * arg1 , const void * arg2 )
{
/* Compare all of both strings: */
return s_pVertexCount [ * ( const int * ) arg2 ] - s_pVertexCount [ * ( const int * ) arg1 ] ;
}
# define MIN_HWMORPH_FLEX_COUNT 200
void CStudioRenderContext : : DetermineHWMorphing ( mstudiomodel_t * pModel , OptimizedModel : : ModelLODHeader_t * pVtxLOD )
{
if ( ! g_pMaterialSystemHardwareConfig - > HasFastVertexTextures ( ) )
return ;
// There is fixed cost to using HW morphing in the form of setting rendertargets.
// Therefore if there is a low chance of there being enough work, then do it in software.
int nTotalFlexCount = ComputeTotalFlexCount ( pModel ) ;
if ( nTotalFlexCount = = 0 )
return ;
if ( nTotalFlexCount < MIN_HWMORPH_FLEX_COUNT )
{
SuppressAllHWMorphs ( pModel , pVtxLOD ) ;
return ;
}
// If we have less meshes than the most morphs we can do in a batch, we're done.
int nMaxHWMorphBatchCount = g_pMaterialSystemHardwareConfig - > MaxHWMorphBatchCount ( ) ;
bool bHWMorph = ( pModel - > nummeshes < = nMaxHWMorphBatchCount ) ;
if ( bHWMorph )
return ;
// If we have less flexed strip groups than the most we can do in a batch, we're done.
int nFlexedStripGroup = CountDeltaFlexedStripGroups ( pModel , pVtxLOD ) ;
if ( nFlexedStripGroup < = nMaxHWMorphBatchCount )
return ;
// Finally, the expensive method. Do HW morphing on the N most expensive strip groups
// FIXME: We should do this at studiomdl time?
// Certainly counting the # of flexed vertices can be done at studiomdl time.
int * pVertexCount = ( int * ) _alloca ( nFlexedStripGroup * sizeof ( int ) ) ;
int nCount = 0 ;
for ( int k = 0 ; k < pModel - > nummeshes ; + + k )
{
Assert ( pModel - > nummeshes = = pVtxLOD - > numMeshes ) ;
mstudiomesh_t * pMesh = pModel - > pMesh ( k ) ;
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( k ) ;
for ( int i = 0 ; i < pVtxMesh - > numStripGroups ; + + i )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( i ) ;
if ( ( pStripGroup - > flags & OptimizedModel : : STRIPGROUP_IS_DELTA_FLEXED ) = = 0 )
continue ;
pVertexCount [ nCount + + ] = CountFlexedVertices ( pMesh , pStripGroup ) ;
}
}
int * pSortedVertexIndices = ( int * ) _alloca ( nFlexedStripGroup * sizeof ( int ) ) ;
for ( int i = 0 ; i < nFlexedStripGroup ; + + i )
{
pSortedVertexIndices [ i ] = i ;
}
s_pVertexCount = pVertexCount ;
qsort ( pSortedVertexIndices , nCount , sizeof ( int ) , SortVertCount ) ;
bool * pSuppressHWMorph = ( bool * ) _alloca ( nFlexedStripGroup * sizeof ( bool ) ) ;
memset ( pSuppressHWMorph , 1 , nFlexedStripGroup * sizeof ( bool ) ) ;
for ( int i = 0 ; i < nMaxHWMorphBatchCount ; + + i )
{
pSuppressHWMorph [ pSortedVertexIndices [ i ] ] = false ;
}
// Bleah. Pretty lame. We should change StripGroupHeader_t to store the flex vertex count
int nIndex = 0 ;
for ( int k = 0 ; k < pModel - > nummeshes ; + + k )
{
Assert ( pModel - > nummeshes = = pVtxLOD - > numMeshes ) ;
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( k ) ;
for ( int i = 0 ; i < pVtxMesh - > numStripGroups ; + + i )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( i ) ;
if ( ( pStripGroup - > flags & OptimizedModel : : STRIPGROUP_IS_DELTA_FLEXED ) = = 0 )
continue ;
if ( pSuppressHWMorph [ nIndex ] )
{
pStripGroup - > flags | = OptimizedModel : : STRIPGROUP_SUPPRESS_HW_MORPH ;
}
+ + nIndex ;
}
}
}
//-----------------------------------------------------------------------------
// Adds a vertex to the meshbuilder. Returns false if boneweights did not sum to 1.0
//-----------------------------------------------------------------------------
template < VertexCompressionType_t T > bool CStudioRenderContext : : R_AddVertexToMesh ( const char * pModelName , bool bNeedsTangentSpace , CMeshBuilder & meshBuilder ,
OptimizedModel : : Vertex_t * pVertex , mstudiomesh_t * pMesh , const mstudio_meshvertexdata_t * vertData , bool hwSkin )
{
bool bOK = true ;
int idx = pVertex - > origMeshVertID ;
mstudiovertex_t & vert = * vertData - > Vertex ( idx ) ;
// FIXME: if this ever becomes perf-critical... these writes are not in memory-ascending order,
// which hurts since VBs are in write-combined memory (See WriteCombineOrdering_t)
meshBuilder . Position3fv ( vert . m_vecPosition . Base ( ) ) ;
meshBuilder . CompressedNormal3fv < T > ( vert . m_vecNormal . Base ( ) ) ;
/*
if ( vert . m_vecNormal . Length ( ) < .9f | | vert . m_vecNormal . Length ( ) > 1.1f )
{
static CUtlStringMap < bool > errorMessages ;
if ( ! errorMessages . Defined ( pModelName ) )
{
errorMessages [ pModelName ] = true ;
Warning ( " MODELBUG %s: bad normal \n " , pModelName ) ;
Warning ( " \t normal %0.1f %0.1f %0.1f pos: %0.1f %0.1f %0.1f \n " ,
vert . m_vecNormal . x , vert . m_vecNormal . y , vert . m_vecNormal . z ,
vert . m_vecPosition . x , vert . m_vecPosition . y , vert . m_vecPosition . z ) ;
}
}
*/
meshBuilder . TexCoord2fv ( 0 , vert . m_vecTexCoord . Base ( ) ) ;
if ( vertData - > HasTangentData ( ) )
{
/*
if ( bNeedsTangentSpace & & pModelName & & vertData - > TangentS ( idx ) )
{
const Vector4D & tangentS = * vertData - > TangentS ( idx ) ;
float w = tangentS . w ;
if ( ! ( w = = 1.0f | | w = = - 1.0f ) )
{
static CUtlStringMap < bool > errorMessages ;
if ( ! errorMessages . Defined ( pModelName ) )
{
errorMessages [ pModelName ] = true ;
Warning ( " MODELBUG %s: bad tangent sign \n " , pModelName ) ;
Warning ( " \t sign %0.1f at position %0.1f %0.1f %0.1f \n " ,
w , vert . m_vecPosition . x , vert . m_vecPosition . y , vert . m_vecPosition . z ) ;
}
}
float len = tangentS . AsVector3D ( ) . Length ( ) ;
if ( len < .9f | | len > 1.1f )
{
static CUtlStringMap < bool > errorMessages ;
if ( ! errorMessages . Defined ( pModelName ) )
{
errorMessages [ pModelName ] = true ;
Warning ( " MODELBUG %s: bad tangent vector \n " , pModelName ) ;
Warning ( " \t tangent: %0.1f %0.1f %0.1f with length %0.1f at position %0.1f %0.1f %0.1f \n " ,
tangentS . x , tangentS . y , tangentS . z ,
len ,
vert . m_vecPosition . x , vert . m_vecPosition . y , vert . m_vecPosition . z ) ;
}
}
#if 0
float dot = DotProduct ( vert . m_vecNormal , tangentS . AsVector3D ( ) ) ;
if ( dot > .95 | | dot < - .95 )
{
static CUtlStringMap < bool > errorMessages ;
if ( ! errorMessages . Defined ( pModelName ) )
{
errorMessages [ pModelName ] = true ;
// this is crashing for some reason. .need to investigate.
Warning ( " MODELBUG %s: nearly colinear tangentS (%f %f %f) and normal (%f %f %f) at position %f %f %f Probably have 2 or more texcoords that are the same on a triangle. \n " ,
pModelName , tangentS . x , tangentS . y , tangentS . y , vert . m_vecNormal . x , vert . m_vecNormal . y , vert . m_vecNormal . z , vert . m_vecPosition . x , vert . m_vecPosition . y , vert . m_vecPosition . z ) ;
}
}
# endif
}
*/
// send down tangent S as a 4D userdata vect.
meshBuilder . CompressedUserData < T > ( ( * vertData - > TangentS ( idx ) ) . Base ( ) ) ;
}
// Just in case we get hooked to a material that wants per-vertex color
meshBuilder . Color4ub ( 255 , 255 , 255 , 255 ) ;
float boneWeights [ MAX_NUM_BONE_INDICES ] ;
if ( hwSkin )
{
// sum up weights..
int i ;
// We have to do this because since we're potentially dropping bones
// to get them to fit in hardware, we'll need to renormalize based on
// the actual total.
mstudioboneweight_t * pBoneWeight = vertData - > BoneWeights ( idx ) ;
// NOTE: We use pVertex->numbones because that's the number of bones actually influencing this
// vertex. Note that pVertex->numBones is not necessary the *desired* # of bones influencing this
// vertex; we could have collapsed some of those bones out. pBoneWeight->numbones stures the desired #
float totalWeight = 0 ;
for ( i = 0 ; i < pVertex - > numBones ; + + i )
{
totalWeight + = pBoneWeight - > weight [ pVertex - > boneWeightIndex [ i ] ] ;
}
// The only way we should not add up to 1 is if there's more than 3 *desired* bones
// and more than 1 *actual* bone (we can have 0 vertex bones in the case of static props
if ( ( pVertex - > numBones > 0 ) & & ( pBoneWeight - > numbones < = 3 ) & & fabs ( totalWeight - 1.0f ) > 1e-3 )
{
// force them to re-normalize
bOK = false ;
totalWeight = 1.0f ;
}
// Fix up the static prop case
if ( totalWeight = = 0.0f )
{
totalWeight = 1.0f ;
}
float invTotalWeight = 1.0f / totalWeight ;
// It is essential to iterate over all actual bones so that the bone indices
// are set correctly, even though the last bone weight is computed in a shader program
for ( i = 0 ; i < pVertex - > numBones ; + + i )
{
if ( pVertex - > boneID [ i ] = = - 1 )
{
boneWeights [ i ] = 0.0f ;
meshBuilder . BoneMatrix ( i , BONE_MATRIX_INDEX_INVALID ) ;
}
else
{
float weight = pBoneWeight - > weight [ pVertex - > boneWeightIndex [ i ] ] ;
boneWeights [ i ] = weight * invTotalWeight ;
meshBuilder . BoneMatrix ( i , pVertex - > boneID [ i ] ) ;
}
}
for ( ; i < MAX_NUM_BONE_INDICES ; i + + )
{
boneWeights [ i ] = 0.0f ;
meshBuilder . BoneMatrix ( i , BONE_MATRIX_INDEX_INVALID ) ;
}
}
else
{
for ( int i = 0 ; i < MAX_NUM_BONE_INDICES ; + + i )
{
boneWeights [ i ] = ( i = = 0 ) ? 1.0f : 0.0f ;
meshBuilder . BoneMatrix ( i , BONE_MATRIX_INDEX_INVALID ) ;
}
}
// Set all the weights at once (the meshbuilder performs additional, post-compression, normalization):
Assert ( pVertex - > numBones < = 3 ) ;
if ( pVertex - > numBones > 0 )
{
meshBuilder . CompressedBoneWeight3fv < T > ( & ( boneWeights [ 0 ] ) ) ;
}
meshBuilder . AdvanceVertex ( ) ;
return bOK ;
}
// Get (uncompressed) vertex data from a mesh, if available
inline const mstudio_meshvertexdata_t * GetFatVertexData ( mstudiomesh_t * pMesh , studiohdr_t * pStudioHdr )
{
if ( ! pMesh - > pModel ( ) - > CacheVertexData ( pStudioHdr ) )
{
// not available yet
return NULL ;
}
const mstudio_meshvertexdata_t * pVertData = pMesh - > GetVertexData ( pStudioHdr ) ;
Assert ( pVertData ) ;
if ( ! pVertData )
{
static unsigned int warnCount = 0 ;
if ( warnCount + + < 20 )
Warning ( " ERROR: model verts have been compressed, cannot render! (use \" -no_compressed_vvds \" ) " ) ;
}
return pVertData ;
}
//-----------------------------------------------------------------------------
// Builds the group
//-----------------------------------------------------------------------------
void CStudioRenderContext : : R_StudioBuildMeshGroup ( const char * pModelName , bool bNeedsTangentSpace , studiomeshgroup_t * pMeshGroup ,
OptimizedModel : : StripGroupHeader_t * pStripGroup , mstudiomesh_t * pMesh ,
studiohdr_t * pStudioHdr , VertexFormat_t vertexFormat )
{
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
// We have to do this here because of skinning; there may be any number of
// materials that are applied to this mesh.
// Copy over all the vertices + indices in this strip group
pMeshGroup - > m_pMesh = pRenderContext - > CreateStaticMesh ( vertexFormat , TEXTURE_GROUP_STATIC_VERTEX_BUFFER_MODELS ) ;
VertexCompressionType_t compressionType = CompressionType ( vertexFormat ) ;
pMeshGroup - > m_ColorMeshID = - 1 ;
bool hwSkin = ( pMeshGroup - > m_Flags & MESHGROUP_IS_HWSKINNED ) ! = 0 ;
// This mesh could have tristrips or trilists in it
CMeshBuilder meshBuilder ;
meshBuilder . SetCompressionType ( compressionType ) ;
meshBuilder . Begin ( pMeshGroup - > m_pMesh , MATERIAL_HETEROGENOUS ,
hwSkin ? pStripGroup - > numVerts : 0 , pStripGroup - > numIndices ) ;
int i ;
bool bBadBoneWeights = false ;
if ( hwSkin )
{
const mstudio_meshvertexdata_t * vertData = GetFatVertexData ( pMesh , pStudioHdr ) ;
Assert ( vertData ) ;
for ( i = 0 ; i < pStripGroup - > numVerts ; + + i )
{
bool success ;
switch ( compressionType )
{
case VERTEX_COMPRESSION_ON :
success = R_AddVertexToMesh < VERTEX_COMPRESSION_ON > ( pModelName , bNeedsTangentSpace , meshBuilder , pStripGroup - > pVertex ( i ) , pMesh , vertData , hwSkin ) ;
break ;
case VERTEX_COMPRESSION_NONE :
default :
success = R_AddVertexToMesh < VERTEX_COMPRESSION_NONE > ( pModelName , bNeedsTangentSpace , meshBuilder , pStripGroup - > pVertex ( i ) , pMesh , vertData , hwSkin ) ;
break ;
}
if ( ! success )
{
bBadBoneWeights = true ;
}
}
}
if ( bBadBoneWeights )
{
mstudiomodel_t * pModel = pMesh - > pModel ( ) ;
ConMsg ( " Bad data found in model \" %s \" (bad bone weights) \n " , pModel - > pszName ( ) ) ;
}
for ( i = 0 ; i < pStripGroup - > numIndices ; + + i )
{
unsigned short index ;
memcpy ( & index , pStripGroup - > pIndex ( i ) , sizeof ( index ) ) ;
meshBuilder . Index ( index ) ;
meshBuilder . AdvanceIndex ( ) ;
}
meshBuilder . End ( ) ;
// Copy over the strip indices. We need access to the indices for decals
pMeshGroup - > m_pIndices = new unsigned short [ pStripGroup - > numIndices ] ;
memcpy ( pMeshGroup - > m_pIndices , pStripGroup - > pIndex ( 0 ) ,
pStripGroup - > numIndices * sizeof ( unsigned short ) ) ;
// Compute the number of non-degenerate trianges in each strip group
// for statistics gathering
pMeshGroup - > m_pUniqueTris = new int [ pStripGroup - > numStrips ] ;
for ( i = 0 ; i < pStripGroup - > numStrips ; + + i )
{
int numUnique = 0 ;
if ( pStripGroup - > pStrip ( i ) - > flags & OptimizedModel : : STRIP_IS_TRISTRIP )
{
int last [ 2 ] = { - 1 , - 1 } ;
int curr = pStripGroup - > pStrip ( i ) - > indexOffset ;
int end = curr + pStripGroup - > pStrip ( i ) - > numIndices ;
while ( curr ! = end )
{
int idx = * pStripGroup - > pIndex ( curr ) ;
if ( idx ! = last [ 0 ] & & idx ! = last [ 1 ] & & last [ 0 ] ! = last [ 1 ] & & last [ 0 ] ! = - 1 )
+ + numUnique ;
last [ 0 ] = last [ 1 ] ;
last [ 1 ] = idx ;
+ + curr ;
}
}
else
{
numUnique = pStripGroup - > pStrip ( i ) - > numIndices / 3 ;
}
pMeshGroup - > m_pUniqueTris [ i ] = numUnique ;
}
}
//-----------------------------------------------------------------------------
// Builds the group
//-----------------------------------------------------------------------------
void CStudioRenderContext : : R_StudioBuildMorph ( studiohdr_t * pStudioHdr ,
studiomeshgroup_t * pMeshGroup , mstudiomesh_t * pMesh ,
OptimizedModel : : StripGroupHeader_t * pStripGroup )
{
if ( ! g_pMaterialSystemHardwareConfig - > HasFastVertexTextures ( ) | |
( ( pMeshGroup - > m_Flags & MESHGROUP_IS_DELTA_FLEXED ) = = 0 ) | |
( ( pStripGroup - > flags & OptimizedModel : : STRIPGROUP_SUPPRESS_HW_MORPH ) ! = 0 ) )
{
pMeshGroup - > m_pMorph = NULL ;
return ;
}
// Build an inverse mapping from mesh index to strip group index
unsigned short * pMeshIndexToGroupIndex = ( unsigned short * ) _alloca ( pMesh - > pModel ( ) - > numvertices * sizeof ( unsigned short ) ) ;
memset ( pMeshIndexToGroupIndex , 0xFF , pMesh - > pModel ( ) - > numvertices * sizeof ( unsigned short ) ) ;
for ( int i = 0 ; i < pStripGroup - > numVerts ; + + i )
{
int nMeshVert = pStripGroup - > pVertex ( i ) - > origMeshVertID ;
pMeshIndexToGroupIndex [ nMeshVert ] = ( unsigned short ) i ;
}
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
MorphFormat_t morphType = MORPH_POSITION | MORPH_NORMAL | MORPH_SPEED | MORPH_SIDE ;
for ( int i = 0 ; i < pMesh - > numflexes ; + + i )
{
if ( pMesh - > pFlex ( i ) - > vertanimtype = = STUDIO_VERT_ANIM_WRINKLE )
{
morphType | = MORPH_WRINKLE ;
break ;
}
}
char pTemp [ 256 ] ;
Q_snprintf ( pTemp , sizeof ( pTemp ) , " %s [%p] " , pStudioHdr - > pszName ( ) , pMeshGroup ) ;
pMeshGroup - > m_pMorph = pRenderContext - > CreateMorph ( morphType , pTemp ) ;
const float flVertAnimFixedPointScale = pStudioHdr - > VertAnimFixedPointScale ( ) ;
CMorphBuilder morphBuilder ;
morphBuilder . Begin ( pMeshGroup - > m_pMorph , 1.0f / flVertAnimFixedPointScale ) ;
for ( int i = 0 ; i < pMesh - > numflexes ; + + i )
{
mstudioflex_t * pFlex = pMesh - > pFlex ( i ) ;
byte * pVAnim = pFlex - > pBaseVertanim ( ) ;
int nVAnimSizeBytes = pFlex - > VertAnimSizeBytes ( ) ;
for ( int j = 0 ; j < pFlex - > numverts ; + + j )
{
mstudiovertanim_t * pAnim = ( mstudiovertanim_t * ) ( pVAnim + j * nVAnimSizeBytes ) ;
int nMeshVert = pAnim - > index ;
unsigned short nGroupVert = pMeshIndexToGroupIndex [ nMeshVert ] ;
// In this case, this vertex is not part of this meshgroup. Ignore it.
if ( nGroupVert = = 0xFFFF )
continue ;
morphBuilder . PositionDelta3 ( pAnim - > GetDeltaFixed ( flVertAnimFixedPointScale ) ) ;
morphBuilder . NormalDelta3 ( pAnim - > GetNDeltaFixed ( flVertAnimFixedPointScale ) ) ;
morphBuilder . Speed1f ( pAnim - > speed / 255.0f ) ;
morphBuilder . Side1f ( pAnim - > side / 255.0f ) ;
if ( pFlex - > vertanimtype = = STUDIO_VERT_ANIM_WRINKLE )
{
mstudiovertanim_wrinkle_t * pWrinkleAnim = static_cast < mstudiovertanim_wrinkle_t * > ( pAnim ) ;
morphBuilder . WrinkleDelta1f ( pWrinkleAnim - > GetWrinkleDeltaFixed ( flVertAnimFixedPointScale ) ) ;
}
else
{
morphBuilder . WrinkleDelta1f ( 0.0f ) ;
}
morphBuilder . AdvanceMorph ( nGroupVert , i ) ;
}
}
morphBuilder . End ( ) ;
}
//-----------------------------------------------------------------------------
// Builds the strip data
//-----------------------------------------------------------------------------
void CStudioRenderContext : : R_StudioBuildMeshStrips ( studiomeshgroup_t * pMeshGroup ,
OptimizedModel : : StripGroupHeader_t * pStripGroup )
{
// FIXME: This is bogus
// Compute the amount of memory we need to store the strip data
int i ;
int stripDataSize = 0 ;
for ( i = 0 ; i < pStripGroup - > numStrips ; + + i )
{
stripDataSize + = sizeof ( OptimizedModel : : StripHeader_t ) ;
stripDataSize + = pStripGroup - > pStrip ( i ) - > numBoneStateChanges *
sizeof ( OptimizedModel : : BoneStateChangeHeader_t ) ;
}
pMeshGroup - > m_pStripData = ( OptimizedModel : : StripHeader_t * ) malloc ( stripDataSize ) ;
// Copy over the strip info
int boneStateChangeOffset = pStripGroup - > numStrips * sizeof ( OptimizedModel : : StripHeader_t ) ;
for ( i = 0 ; i < pStripGroup - > numStrips ; + + i )
{
memcpy ( & pMeshGroup - > m_pStripData [ i ] , pStripGroup - > pStrip ( i ) ,
sizeof ( OptimizedModel : : StripHeader_t ) ) ;
// Fixup the bone state change offset, since we have it right after the strip data
pMeshGroup - > m_pStripData [ i ] . boneStateChangeOffset = boneStateChangeOffset -
i * sizeof ( OptimizedModel : : StripHeader_t ) ;
// copy over bone state changes
int boneWeightSize = pMeshGroup - > m_pStripData [ i ] . numBoneStateChanges *
sizeof ( OptimizedModel : : BoneStateChangeHeader_t ) ;
if ( boneWeightSize ! = 0 )
{
unsigned char * pBoneStateChange = ( unsigned char * ) pMeshGroup - > m_pStripData + boneStateChangeOffset ;
memcpy ( pBoneStateChange , pStripGroup - > pStrip ( i ) - > pBoneStateChange ( 0 ) , boneWeightSize ) ;
boneStateChangeOffset + = boneWeightSize ;
}
}
pMeshGroup - > m_NumStrips = pStripGroup - > numStrips ;
}
//-----------------------------------------------------------------------------
// Determine the max. number of bone weights used by a stripgroup
//-----------------------------------------------------------------------------
int CStudioRenderContext : : GetNumBoneWeights ( const OptimizedModel : : StripGroupHeader_t * pGroup )
{
int nBoneWeightsMax = 0 ;
for ( int i = 0 ; i < pGroup - > numStrips ; i + + )
{
OptimizedModel : : StripHeader_t * pStrip = pGroup - > pStrip ( i ) ;
nBoneWeightsMax = max ( nBoneWeightsMax , ( int ) pStrip - > numBones ) ;
}
return nBoneWeightsMax ;
}
//-----------------------------------------------------------------------------
// Determine an actual model vertex format for a mesh based on its material usage.
// Bypasses the homegenous model vertex format in favor of the actual format.
// Ideally matches 1:1 the shader's data requirements without any bloat.
//-----------------------------------------------------------------------------
VertexFormat_t CStudioRenderContext : : CalculateVertexFormat ( const studiohdr_t * pStudioHdr , const studioloddata_t * pStudioLodData ,
const mstudiomesh_t * pMesh , OptimizedModel : : StripGroupHeader_t * pGroup , bool bIsHwSkinned )
{
bool bSkinnedMesh = ( pStudioHdr - > numbones > 1 ) ;
int nBoneWeights = GetNumBoneWeights ( pGroup ) ;
bool bIsDX7 = ! g_pMaterialSystemHardwareConfig - > SupportsVertexAndPixelShaders ( ) ;
bool bIsDX8 = ( g_pMaterialSystemHardwareConfig - > GetDXSupportLevel ( ) < 90 ) ;
if ( bIsDX7 )
{
// FIXME: this is untested (as of June '07, the engine currently doesn't work with "-dxlevel 70")
if ( bSkinnedMesh )
return MATERIAL_VERTEX_FORMAT_MODEL_SKINNED_DX7 ;
else
return MATERIAL_VERTEX_FORMAT_MODEL_DX7 ;
}
else if ( bIsDX8 )
{
if ( bSkinnedMesh )
return MATERIAL_VERTEX_FORMAT_MODEL_SKINNED ;
else
return MATERIAL_VERTEX_FORMAT_MODEL ;
}
else
{
// DX9+ path (supports vertex compression)
// iterate each skin table
// determine aggregate vertex format for specified mesh's material
VertexFormat_t newVertexFormat = 0 ;
//bool bBumpmapping = false;
short * pSkinref = pStudioHdr - > pSkinref ( 0 ) ;
for ( int i = 0 ; i < pStudioHdr - > numskinfamilies ; i + + )
{
// FIXME: ### MATERIAL VERTEX FORMATS ARE UNRELIABLE! ###
//
// IMaterial* pMaterial = pStudioLodData->ppMaterials[ pSkinref[ pMesh->material ] ];
// Assert( pMaterial );
// VertexFormat_t vertexFormat = pMaterial->GetVertexFormat();
// newVertexFormat &= ~VERTEX_FORMAT_COMPRESSED; // Decide whether to compress below
//
// FIXME: ### MATERIAL VERTEX FORMATS ARE UNRELIABLE! ###
// we need to go through all the shader CPP code and make sure that the correct vertex format
// is being specified for every single shader combo! We don't have time to fix that before
// shipping Ep2, but should fix it ASAP afterwards. To make catching such errors easier, we
// should Assert in draw calls that the vertexdecl matches vertex shader inputs (note that D3D
// debug DLLs will do that on PC, though it's not as informative as if we do it ourselves).
// So, in the absence of reliable material vertex formats, use the old 'standard' elements
// (we can still omit skinning data - and COLOR for DX8+, where it should come from the
// second static lighting stream):
VertexFormat_t vertexFormat = bIsDX7 ? MATERIAL_VERTEX_FORMAT_MODEL_DX7 : ( MATERIAL_VERTEX_FORMAT_MODEL & ~ VERTEX_COLOR ) ;
// aggregate single bit settings
newVertexFormat | = vertexFormat & ( ( 1 < < VERTEX_LAST_BIT ) - 1 ) ;
int nUserDataSize = UserDataSize ( vertexFormat ) ;
if ( nUserDataSize > UserDataSize ( newVertexFormat ) )
{
newVertexFormat & = ~ USER_DATA_SIZE_MASK ;
newVertexFormat | = VERTEX_USERDATA_SIZE ( nUserDataSize ) ;
}
for ( int j = 0 ; j < VERTEX_MAX_TEXTURE_COORDINATES ; + + j )
{
int nSize = TexCoordSize ( j , vertexFormat ) ;
if ( nSize > TexCoordSize ( j , newVertexFormat ) )
{
newVertexFormat & = ~ VERTEX_TEXCOORD_SIZE ( j , 0x7 ) ;
newVertexFormat | = VERTEX_TEXCOORD_SIZE ( j , nSize ) ;
}
}
// FIXME: re-enable this test, fix it to work and see how much memory we save (Q: why is this different to CStudioRenderContext::MeshNeedsTangentSpace ?)
/*if ( !bBumpmapping && pMaterial->NeedsTangentSpace() )
{
bool bFound = false ;
IMaterialVar * pEnvmapMatVar = pMaterial - > FindVar ( " $envmap " , & bFound , false ) ;
if ( bFound & & pEnvmapMatVar - > IsDefined ( ) )
{
IMaterialVar * pBumpMatVar = pMaterial - > FindVar ( " $bumpmap " , & bFound , false ) ;
if ( bFound & & pBumpMatVar - > IsDefined ( ) )
{
bBumpmapping = true ;
}
}
} */
pSkinref + = pStudioHdr - > numskinref ;
}
// Add skinning elements for non-rigid models (with more than one bone weight)
if ( bSkinnedMesh )
{
if ( nBoneWeights > 0 )
{
// Always exactly zero or two weights
newVertexFormat | = VERTEX_BONEWEIGHT ( 2 ) ;
}
newVertexFormat | = VERTEX_BONE_INDEX ;
}
// FIXME: re-enable this (see above)
/*if ( !bBumpmapping )
{
// no bumpmapping, user data not needed
newVertexFormat & = ~ USER_DATA_SIZE_MASK ;
} */
// materials on models should never have tangent space as they use userdata
Assert ( ! ( newVertexFormat & VERTEX_TANGENT_SPACE ) ) ;
// Don't compress the mesh unless it is HW-skinned (we only want to compress static
// VBs, not dynamic ones - that would slow down the MeshBuilder in dynamic use cases).
// Also inspect the vertex data to see if it's appropriate for the vertex element
// compression techniques that we do (e.g. look at UV ranges).
if ( //IsX360() && // Disabled until the craziness is banished
bIsHwSkinned & &
( g_pMaterialSystemHardwareConfig - > SupportsCompressedVertices ( ) = = VERTEX_COMPRESSION_ON ) )
{
// this mesh is appropriate for vertex compression
newVertexFormat | = VERTEX_FORMAT_COMPRESSED ;
}
return newVertexFormat ;
}
}
bool CStudioRenderContext : : MeshNeedsTangentSpace ( studiohdr_t * pStudioHdr , studioloddata_t * pStudioLodData , mstudiomesh_t * pMesh )
{
// iterate each skin table
if ( ! pStudioHdr | | ! pStudioHdr - > pSkinref ( 0 ) | | ! pStudioHdr - > numskinfamilies )
{
return false ;
}
short * pSkinref = pStudioHdr - > pSkinref ( 0 ) ;
for ( int i = 0 ; i < pStudioHdr - > numskinfamilies ; i + + )
{
IMaterial * pMaterial = pStudioLodData - > ppMaterials [ pSkinref [ pMesh - > material ] ] ;
Assert ( pMaterial ) ;
if ( ! pMaterial )
{
continue ;
}
// Warning( "*****%s needstangentspace: %d\n", pMaterial->GetName(), pMaterial->NeedsTangentSpace() ? 1 : 0 );
if ( pMaterial - > NeedsTangentSpace ( ) )
{
return true ;
}
}
return false ;
}
//-----------------------------------------------------------------------------
// Creates a single mesh
//-----------------------------------------------------------------------------
void CStudioRenderContext : : R_StudioCreateSingleMesh ( studiohdr_t * pStudioHdr , studioloddata_t * pStudioLodData ,
mstudiomesh_t * pMesh , OptimizedModel : : MeshHeader_t * pVtxMesh , int numBones ,
studiomeshdata_t * pMeshData , int * pColorMeshID )
{
// Here are the cases where we don't use any meshes at all...
// In the case of eyes, we're just gonna use dynamic buffers
// because it's the fastest solution (prevents lots of locks)
bool bNeedsTangentSpace = MeshNeedsTangentSpace ( pStudioHdr , pStudioLodData , pMesh ) ;
// Each strip group represents a locking group, it's a set of vertices
// that are locked together, and, potentially, software light + skinned together
pMeshData - > m_NumGroup = pVtxMesh - > numStripGroups ;
pMeshData - > m_pMeshGroup = new studiomeshgroup_t [ pVtxMesh - > numStripGroups ] ;
for ( int i = 0 ; i < pVtxMesh - > numStripGroups ; + + i )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( i ) ;
studiomeshgroup_t * pMeshGroup = & pMeshData - > m_pMeshGroup [ i ] ;
pMeshGroup - > m_MeshNeedsRestore = false ;
// Set the flags...
pMeshGroup - > m_Flags = 0 ;
if ( pStripGroup - > flags & OptimizedModel : : STRIPGROUP_IS_FLEXED )
{
pMeshGroup - > m_Flags | = MESHGROUP_IS_FLEXED ;
}
if ( pStripGroup - > flags & OptimizedModel : : STRIPGROUP_IS_DELTA_FLEXED )
{
pMeshGroup - > m_Flags | = MESHGROUP_IS_DELTA_FLEXED ;
}
bool bIsHwSkinned = ! ! ( pStripGroup - > flags & OptimizedModel : : STRIPGROUP_IS_HWSKINNED ) ;
if ( bIsHwSkinned )
{
pMeshGroup - > m_Flags | = MESHGROUP_IS_HWSKINNED ;
}
// get the minimal vertex format for this mesh
VertexFormat_t vertexFormat = CalculateVertexFormat ( pStudioHdr , pStudioLodData , pMesh , pStripGroup , bIsHwSkinned ) ;
// Build the vertex + index buffers
R_StudioBuildMeshGroup ( pStudioHdr - > pszName ( ) , bNeedsTangentSpace , pMeshGroup , pStripGroup , pMesh , pStudioHdr , vertexFormat ) ;
// Copy over the tristrip and triangle list data
R_StudioBuildMeshStrips ( pMeshGroup , pStripGroup ) ;
// Builds morph targets
R_StudioBuildMorph ( pStudioHdr , pMeshGroup , pMesh , pStripGroup ) ;
// Build the mapping from strip group vertex idx to actual mesh idx
pMeshGroup - > m_pGroupIndexToMeshIndex = new unsigned short [ pStripGroup - > numVerts + PREFETCH_VERT_COUNT ] ;
pMeshGroup - > m_NumVertices = pStripGroup - > numVerts ;
int j ;
for ( j = 0 ; j < pStripGroup - > numVerts ; + + j )
{
pMeshGroup - > m_pGroupIndexToMeshIndex [ j ] = pStripGroup - > pVertex ( j ) - > origMeshVertID ;
}
// Extra copies are for precaching...
for ( j = pStripGroup - > numVerts ; j < pStripGroup - > numVerts + PREFETCH_VERT_COUNT ; + + j )
{
pMeshGroup - > m_pGroupIndexToMeshIndex [ j ] = pMeshGroup - > m_pGroupIndexToMeshIndex [ pStripGroup - > numVerts - 1 ] ;
}
// assign the possibly used color mesh id now
pMeshGroup - > m_ColorMeshID = ( * pColorMeshID ) + + ;
}
}
//-----------------------------------------------------------------------------
// Creates static meshes
//-----------------------------------------------------------------------------
void CStudioRenderContext : : R_StudioCreateStaticMeshes ( studiohdr_t * pStudioHdr ,
OptimizedModel : : FileHeader_t * pVtxHdr , studiohwdata_t * pStudioHWData , int nLodID , int * pColorMeshID )
{
int i , j , k ;
Assert ( pStudioHdr & & pVtxHdr & & pStudioHWData ) ;
pStudioHWData - > m_pLODs [ nLodID ] . m_pMeshData = new studiomeshdata_t [ pStudioHWData - > m_NumStudioMeshes ] ;
// Iterate over every body part...
for ( i = 0 ; i < pStudioHdr - > numbodyparts ; i + + )
{
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( i ) ;
OptimizedModel : : BodyPartHeader_t * pVtxBodyPart = pVtxHdr - > pBodyPart ( i ) ;
// Iterate over every submodel...
for ( j = 0 ; j < pBodyPart - > nummodels ; + + j )
{
mstudiomodel_t * pModel = pBodyPart - > pModel ( j ) ;
OptimizedModel : : ModelHeader_t * pVtxModel = pVtxBodyPart - > pModel ( j ) ;
OptimizedModel : : ModelLODHeader_t * pVtxLOD = pVtxModel - > pLOD ( nLodID ) ;
// Determine which meshes should be hw morphed
DetermineHWMorphing ( pModel , pVtxLOD ) ;
// Support tracking of VB allocations
// FIXME: categorise studiomodel allocs more precisely
if ( g_VBAllocTracker )
{
if ( ( pStudioHdr - > numbones > 8 ) | | ( pStudioHdr - > numflexdesc > 0 ) )
{
g_VBAllocTracker - > TrackMeshAllocations ( " R_StudioCreateStaticMeshes (character) " ) ;
}
else
{
if ( pStudioHdr - > flags & STUDIOHDR_FLAGS_STATIC_PROP )
{
g_VBAllocTracker - > TrackMeshAllocations ( " R_StudioCreateStaticMeshes (prop_static) " ) ;
}
else
{
g_VBAllocTracker - > TrackMeshAllocations ( " R_StudioCreateStaticMeshes (prop_dynamic) " ) ;
}
}
}
// Iterate over all the meshes....
for ( k = 0 ; k < pModel - > nummeshes ; + + k )
{
Assert ( pModel - > nummeshes = = pVtxLOD - > numMeshes ) ;
mstudiomesh_t * pMesh = pModel - > pMesh ( k ) ;
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( k ) ;
Assert ( pMesh - > meshid < pStudioHWData - > m_NumStudioMeshes ) ;
R_StudioCreateSingleMesh ( pStudioHdr , & pStudioHWData - > m_pLODs [ nLodID ] ,
pMesh , pVtxMesh , pVtxHdr - > maxBonesPerVert ,
& pStudioHWData - > m_pLODs [ nLodID ] . m_pMeshData [ pMesh - > meshid ] , pColorMeshID ) ;
}
if ( g_VBAllocTracker )
{
g_VBAllocTracker - > TrackMeshAllocations ( NULL ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Destroys static meshes
//-----------------------------------------------------------------------------
void CStudioRenderContext : : R_StudioDestroyStaticMeshes ( int numStudioMeshes , studiomeshdata_t * * ppStudioMeshes )
{
if ( ! * ppStudioMeshes )
return ;
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
// Iterate over every body mesh...
for ( int i = 0 ; i < numStudioMeshes ; + + i )
{
studiomeshdata_t * pMesh = & ( ( * ppStudioMeshes ) [ i ] ) ;
for ( int j = 0 ; j < pMesh - > m_NumGroup ; + + j )
{
studiomeshgroup_t * pGroup = & pMesh - > m_pMeshGroup [ j ] ;
if ( pGroup - > m_pGroupIndexToMeshIndex )
{
delete [ ] pGroup - > m_pGroupIndexToMeshIndex ;
pGroup - > m_pGroupIndexToMeshIndex = 0 ;
}
if ( pGroup - > m_pUniqueTris )
{
delete [ ] pGroup - > m_pUniqueTris ;
pGroup - > m_pUniqueTris = 0 ;
}
if ( pGroup - > m_pIndices )
{
delete [ ] pGroup - > m_pIndices ;
pGroup - > m_pIndices = 0 ;
}
if ( pGroup - > m_pMesh )
{
pRenderContext - > DestroyStaticMesh ( pGroup - > m_pMesh ) ;
pGroup - > m_pMesh = 0 ;
}
if ( pGroup - > m_pMorph )
{
pRenderContext - > DestroyMorph ( pGroup - > m_pMorph ) ;
pGroup - > m_pMorph = 0 ;
}
if ( pGroup - > m_pStripData )
{
free ( pGroup - > m_pStripData ) ;
pGroup - > m_pStripData = 0 ;
}
}
if ( pMesh - > m_pMeshGroup )
{
delete [ ] pMesh - > m_pMeshGroup ;
pMesh - > m_pMeshGroup = 0 ;
}
}
if ( * ppStudioMeshes )
{
delete [ ] * ppStudioMeshes ;
* ppStudioMeshes = 0 ;
}
}
//-----------------------------------------------------------------------------
// Builds the decal bone remap for a particular mesh
//-----------------------------------------------------------------------------
void CStudioRenderContext : : BuildDecalBoneMap ( studiohdr_t * pStudioHdr , int * pUsedBones , int * pBoneRemap , int * pMaxBoneCount , mstudiomesh_t * pMesh , OptimizedModel : : StripGroupHeader_t * pStripGroup )
{
const mstudio_meshvertexdata_t * pVertData = GetFatVertexData ( pMesh , pStudioHdr ) ;
Assert ( pVertData ) ;
for ( int i = 0 ; i < pStripGroup - > numVerts ; + + i )
{
int nMeshVert = pStripGroup - > pVertex ( i ) - > origMeshVertID ;
mstudioboneweight_t & boneWeight = pVertData - > Vertex ( nMeshVert ) - > m_BoneWeights ;
int nBoneCount = boneWeight . numbones ;
for ( int j = 0 ; j < nBoneCount ; + + j )
{
if ( boneWeight . weight [ j ] = = 0.0f )
continue ;
if ( pBoneRemap [ ( unsigned ) boneWeight . bone [ j ] ] > = 0 )
continue ;
pBoneRemap [ ( unsigned ) boneWeight . bone [ j ] ] = * pUsedBones ;
* pUsedBones = * pUsedBones + 1 ;
}
}
for ( int i = 0 ; i < pStripGroup - > numStrips ; + + i )
{
if ( pStripGroup - > pStrip ( i ) - > numBones > * pMaxBoneCount )
{
* pMaxBoneCount = pStripGroup - > pStrip ( i ) - > numBones ;
}
}
}
//-----------------------------------------------------------------------------
// For decals on hardware morphing, we must actually do hardware skinning
// because the flex must occur before skinning.
// For this to work, we have to hope that the total # of bones used by
// hw flexed verts is < than the max possible for the dx level we're running under
//-----------------------------------------------------------------------------
void CStudioRenderContext : : ComputeHWMorphDecalBoneRemap ( studiohdr_t * pStudioHdr , OptimizedModel : : FileHeader_t * pVtxHdr , studiohwdata_t * pStudioHWData , int nLOD )
{
if ( pStudioHdr - > numbones = = 0 )
return ;
// Remaps sw bones to hw bones during decal rendering
// NOTE: Only bones affecting vertices which have hw flexes will be add to this map.
int nBufSize = pStudioHdr - > numbones * sizeof ( int ) ;
int * pBoneRemap = ( int * ) _alloca ( nBufSize ) ;
memset ( pBoneRemap , 0xFF , nBufSize ) ;
int nMaxBoneCount = 0 ;
// NOTE: HW bone index 0 is always the identity transform during decals.
pBoneRemap [ 0 ] = 0 ; // necessary for unused bones in a vertex
int nUsedBones = 1 ;
studioloddata_t * pStudioLOD = & pStudioHWData - > m_pLODs [ nLOD ] ;
for ( int i = 0 ; i < pStudioHdr - > numbodyparts ; + + i )
{
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( i ) ;
OptimizedModel : : BodyPartHeader_t * pVtxBodyPart = pVtxHdr - > pBodyPart ( i ) ;
// Iterate over every submodel...
for ( int j = 0 ; j < pBodyPart - > nummodels ; + + j )
{
mstudiomodel_t * pModel = pBodyPart - > pModel ( j ) ;
OptimizedModel : : ModelHeader_t * pVtxModel = pVtxBodyPart - > pModel ( j ) ;
OptimizedModel : : ModelLODHeader_t * pVtxLOD = pVtxModel - > pLOD ( nLOD ) ;
// Iterate over all the meshes....
for ( int k = 0 ; k < pModel - > nummeshes ; + + k )
{
Assert ( pModel - > nummeshes = = pVtxLOD - > numMeshes ) ;
mstudiomesh_t * pMesh = pModel - > pMesh ( k ) ;
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( k ) ;
studiomeshdata_t * pMeshData = & pStudioLOD - > m_pMeshData [ pMesh - > meshid ] ;
for ( int l = 0 ; l < pVtxMesh - > numStripGroups ; + + l )
{
studiomeshgroup_t * pMeshGroup = & pMeshData - > m_pMeshGroup [ l ] ;
if ( ! pMeshGroup - > m_pMorph )
continue ;
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( l ) ;
BuildDecalBoneMap ( pStudioHdr , & nUsedBones , pBoneRemap , & nMaxBoneCount , pMesh , pStripGroup ) ;
}
}
}
}
if ( nUsedBones > 1 )
{
if ( nUsedBones > g_pMaterialSystemHardwareConfig - > MaxVertexShaderBlendMatrices ( ) )
{
Warning ( " Hardware morphing of decals will be busted! Too many unique bones on flexed vertices! \n " ) ;
}
pStudioLOD - > m_pHWMorphDecalBoneRemap = new int [ pStudioHdr - > numbones ] ;
memcpy ( pStudioLOD - > m_pHWMorphDecalBoneRemap , pBoneRemap , nBufSize ) ;
pStudioLOD - > m_nDecalBoneCount = nMaxBoneCount ;
}
}
//-----------------------------------------------------------------------------
// Hook needed by mdlcache to load the vertex data
//-----------------------------------------------------------------------------
const vertexFileHeader_t * mstudiomodel_t : : CacheVertexData ( void * pModelData )
{
// make requested data resident
return g_pStudioDataCache - > CacheVertexData ( ( studiohdr_t * ) pModelData ) ;
}
//-----------------------------------------------------------------------------
// Loads, unloads models
//-----------------------------------------------------------------------------
bool CStudioRenderContext : : LoadModel ( studiohdr_t * pStudioHdr , void * pVtxBuffer , studiohwdata_t * pStudioHWData )
{
int i ;
int j ;
Assert ( pStudioHdr ) ;
Assert ( pVtxBuffer ) ;
Assert ( pStudioHWData ) ;
if ( ! pStudioHdr | | ! pVtxBuffer | | ! pStudioHWData )
return false ;
// NOTE: This must be called *after* Mod_LoadStudioModel
OptimizedModel : : FileHeader_t * pVertexHdr = ( OptimizedModel : : FileHeader_t * ) pVtxBuffer ;
if ( pVertexHdr - > checkSum ! = pStudioHdr - > checksum )
{
ConDMsg ( " Error! Model %s .vtx file out of synch with .mdl \n " , pStudioHdr - > pszName ( ) ) ;
return false ;
}
pStudioHWData - > m_NumStudioMeshes = 0 ;
for ( i = 0 ; i < pStudioHdr - > numbodyparts ; i + + )
{
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( i ) ;
for ( j = 0 ; j < pBodyPart - > nummodels ; j + + )
{
pStudioHWData - > m_NumStudioMeshes + = pBodyPart - > pModel ( j ) - > nummeshes ;
}
}
// Create static meshes
Assert ( pVertexHdr - > numLODs ) ;
pStudioHWData - > m_RootLOD = min ( ( int ) pStudioHdr - > rootLOD , pVertexHdr - > numLODs - 1 ) ;
pStudioHWData - > m_NumLODs = pVertexHdr - > numLODs ;
pStudioHWData - > m_pLODs = new studioloddata_t [ pVertexHdr - > numLODs ] ;
memset ( pStudioHWData - > m_pLODs , 0 , pVertexHdr - > numLODs * sizeof ( studioloddata_t ) ) ;
// reset the runtime flags
pStudioHdr - > flags & = ~ STUDIOHDR_FLAGS_USES_ENV_CUBEMAP ;
pStudioHdr - > flags & = ~ STUDIOHDR_FLAGS_USES_FB_TEXTURE ;
pStudioHdr - > flags & = ~ STUDIOHDR_FLAGS_USES_BUMPMAPPING ;
# ifdef _DEBUG
int totalNumMeshGroups = 0 ;
# endif
int nColorMeshID = 0 ;
int nLodID ;
for ( nLodID = pStudioHWData - > m_RootLOD ; nLodID < pStudioHWData - > m_NumLODs ; nLodID + + )
{
// Load materials and determine material dependent mesh requirements
LoadMaterials ( pStudioHdr , pVertexHdr , pStudioHWData - > m_pLODs [ nLodID ] , nLodID ) ;
// build the meshes
R_StudioCreateStaticMeshes ( pStudioHdr , pVertexHdr , pStudioHWData , nLodID , & nColorMeshID ) ;
// Build the hardware bone remap for decal rendering using HW morphing
ComputeHWMorphDecalBoneRemap ( pStudioHdr , pVertexHdr , pStudioHWData , nLodID ) ;
// garymcthack - need to check for NULL here.
// save off the lod switch point
pStudioHWData - > m_pLODs [ nLodID ] . m_SwitchPoint = pVertexHdr - > pBodyPart ( 0 ) - > pModel ( 0 ) - > pLOD ( nLodID ) - > switchPoint ;
# ifdef _DEBUG
studioloddata_t * pLOD = & pStudioHWData - > m_pLODs [ nLodID ] ;
for ( int meshID = 0 ; meshID < pStudioHWData - > m_NumStudioMeshes ; + + meshID )
{
totalNumMeshGroups + = pLOD - > m_pMeshData [ meshID ] . m_NumGroup ;
}
# endif
}
# ifdef _DEBUG
Assert ( nColorMeshID = = totalNumMeshGroups ) ;
# endif
return true ;
}
void CStudioRenderContext : : UnloadModel ( studiohwdata_t * pHardwareData )
{
int i ;
for ( i = pHardwareData - > m_RootLOD ; i < pHardwareData - > m_NumLODs ; i + + )
{
int j ;
for ( j = 0 ; j < pHardwareData - > m_pLODs [ i ] . numMaterials ; j + + )
{
if ( pHardwareData - > m_pLODs [ i ] . ppMaterials [ j ] )
{
pHardwareData - > m_pLODs [ i ] . ppMaterials [ j ] - > DecrementReferenceCount ( ) ;
}
}
delete [ ] pHardwareData - > m_pLODs [ i ] . ppMaterials ;
delete [ ] pHardwareData - > m_pLODs [ i ] . pMaterialFlags ;
pHardwareData - > m_pLODs [ i ] . ppMaterials = NULL ;
pHardwareData - > m_pLODs [ i ] . pMaterialFlags = NULL ;
}
for ( i = pHardwareData - > m_RootLOD ; i < pHardwareData - > m_NumLODs ; i + + )
{
R_StudioDestroyStaticMeshes ( pHardwareData - > m_NumStudioMeshes , & pHardwareData - > m_pLODs [ i ] . m_pMeshData ) ;
}
delete [ ] pHardwareData - > m_pLODs ;
pHardwareData - > m_pLODs = NULL ;
}
//-----------------------------------------------------------------------------
// Refresh the studiohdr since it was lost...
//-----------------------------------------------------------------------------
void CStudioRenderContext : : RefreshStudioHdr ( studiohdr_t * pStudioHdr , studiohwdata_t * pHardwareData )
{
}
//-----------------------------------------------------------------------------
// Set the eye view target
//-----------------------------------------------------------------------------
void CStudioRenderContext : : SetEyeViewTarget ( const studiohdr_t * pStudioHdr , int nBodyIndex , const Vector & viewtarget )
{
VectorCopy ( viewtarget , m_RC . m_ViewTarget ) ;
}
//-----------------------------------------------------------------------------
// Returns information about the ambient light samples
//-----------------------------------------------------------------------------
static TableVector s_pAmbientLightDir [ 6 ] =
{
{ 1 , 0 , 0 } ,
{ - 1 , 0 , 0 } ,
{ 0 , 1 , 0 } ,
{ 0 , - 1 , 0 } ,
{ 0 , 0 , 1 } ,
{ 0 , 0 , - 1 } ,
} ;
int CStudioRenderContext : : GetNumAmbientLightSamples ( )
{
return 6 ;
}
const Vector * CStudioRenderContext : : GetAmbientLightDirections ( )
{
return ( const Vector * ) s_pAmbientLightDir ;
}
//-----------------------------------------------------------------------------
// Methods related to LOD
//-----------------------------------------------------------------------------
int CStudioRenderContext : : GetNumLODs ( const studiohwdata_t & hardwareData ) const
{
return hardwareData . m_NumLODs ;
}
float CStudioRenderContext : : GetLODSwitchValue ( const studiohwdata_t & hardwareData , int nLOD ) const
{
return hardwareData . m_pLODs [ nLOD ] . m_SwitchPoint ;
}
void CStudioRenderContext : : SetLODSwitchValue ( studiohwdata_t & hardwareData , int nLOD , float flSwitchValue )
{
// NOTE: This must block the hardware thread since it reads this data.
// This method is only used in tools, though.
MaterialLock_t hLock = g_pMaterialSystem - > Lock ( ) ;
hardwareData . m_pLODs [ nLOD ] . m_SwitchPoint = flSwitchValue ;
g_pMaterialSystem - > Unlock ( hLock ) ;
}
//-----------------------------------------------------------------------------
// Returns the first n materials. The studiohdr material list is the superset
// for all lods.
//-----------------------------------------------------------------------------
int CStudioRenderContext : : GetMaterialList ( studiohdr_t * pStudioHdr , int count , IMaterial * * ppMaterials )
{
AssertMsg ( pStudioHdr , " Don't ignore this assert! CStudioRenderContext::GetMaterialList() has null pStudioHdr. " ) ;
if ( ! pStudioHdr )
return 0 ;
if ( pStudioHdr - > textureindex = = 0 )
return 0 ;
// iterate each texture
int i ;
int j ;
int found = 0 ;
for ( i = 0 ; i < pStudioHdr - > numtextures ; i + + )
{
char szPath [ MAX_PATH ] ;
IMaterial * pMaterial = NULL ;
// iterate quietly through all specified directories until a valid material is found
for ( j = 0 ; j < pStudioHdr - > numcdtextures & & IsErrorMaterial ( pMaterial ) ; j + + )
{
// If we don't do this, we get filenames like "materials\\blah.vmt".
const char * textureName = pStudioHdr - > pTexture ( i ) - > pszName ( ) ;
if ( textureName [ 0 ] = = CORRECT_PATH_SEPARATOR | | textureName [ 0 ] = = INCORRECT_PATH_SEPARATOR )
+ + textureName ;
// This prevents filenames like /models/blah.vmt.
const char * pCdTexture = pStudioHdr - > pCdtexture ( j ) ;
if ( pCdTexture [ 0 ] = = CORRECT_PATH_SEPARATOR | | pCdTexture [ 0 ] = = INCORRECT_PATH_SEPARATOR )
+ + pCdTexture ;
V_ComposeFileName ( pCdTexture , textureName , szPath , sizeof ( szPath ) ) ;
if ( pStudioHdr - > flags & STUDIOHDR_FLAGS_OBSOLETE )
{
pMaterial = g_pMaterialSystem - > FindMaterialEx ( " models/obsolete/obsolete " , TEXTURE_GROUP_MODEL , MATERIAL_FINDCONTEXT_ISONAMODEL , false ) ;
}
else
{
pMaterial = g_pMaterialSystem - > FindMaterialEx ( szPath , TEXTURE_GROUP_MODEL , MATERIAL_FINDCONTEXT_ISONAMODEL , false ) ;
}
}
if ( ! pMaterial )
continue ;
if ( found < count )
{
int k ;
for ( k = 0 ; k < found ; k + + )
{
if ( ppMaterials [ k ] = = pMaterial )
break ;
}
if ( k > = found )
{
// add uniquely
ppMaterials [ found + + ] = pMaterial ;
}
}
else
{
break ;
}
}
return found ;
}
int CStudioRenderContext : : GetMaterialListFromBodyAndSkin ( MDLHandle_t studio , int nSkin , int nBody , int nCountOutputMaterials , IMaterial * * ppOutputMaterials )
{
int found = 0 ;
studiohwdata_t * pStudioHWData = g_pMDLCache - > GetHardwareData ( studio ) ;
if ( pStudioHWData = = NULL )
return 0 ;
for ( int lodID = pStudioHWData - > m_RootLOD ; lodID < pStudioHWData - > m_NumLODs ; lodID + + )
{
studiohdr_t * pStudioHdr = g_pMDLCache - > GetStudioHdr ( studio ) ;
IMaterial * * ppInputMaterials = pStudioHWData - > m_pLODs [ lodID ] . ppMaterials ;
if ( nSkin > = pStudioHdr - > numskinfamilies )
{
nSkin = 0 ;
}
short * pSkinRef = pStudioHdr - > pSkinref ( nSkin * pStudioHdr - > numskinref ) ;
for ( int i = 0 ; i < pStudioHdr - > numbodyparts ; i + + )
{
mstudiomodel_t * pModel = NULL ;
R_StudioSetupModel ( i , nBody , & pModel , pStudioHdr ) ;
// Iterate over all the meshes.... each mesh is a new material
for ( int k = 0 ; k < pModel - > nummeshes ; + + k )
{
mstudiomesh_t * pMesh = pModel - > pMesh ( k ) ;
IMaterial * pMaterial = ppInputMaterials [ pSkinRef [ pMesh - > material ] ] ;
Assert ( pMaterial ) ;
int m ;
for ( m = 0 ; m < found ; m + + )
{
if ( ppOutputMaterials [ m ] = = pMaterial )
break ;
}
if ( m > = found )
{
// add uniquely
ppOutputMaterials [ found + + ] = pMaterial ;
// No more room to store additional materials!
if ( found > = nCountOutputMaterials )
return found ;
}
}
}
}
return found ;
}
//-----------------------------------------------------------------------------
// Returns perf stats about a particular model
//-----------------------------------------------------------------------------
void CStudioRenderContext : : GetPerfStats ( DrawModelResults_t * pResults , const DrawModelInfo_t & info , CUtlBuffer * pSpewBuf ) const
{
pResults - > m_ActualTriCount = pResults - > m_TextureMemoryBytes = 0 ;
pResults - > m_Materials . RemoveAll ( ) ;
Assert ( info . m_Lod > = 0 ) ;
if ( info . m_Lod < 0 | | ! info . m_pHardwareData - > m_pLODs )
return ;
studiomeshdata_t * pStudioMeshes = info . m_pHardwareData - > m_pLODs [ info . m_Lod ] . m_pMeshData ;
// Set up an array that keeps up with the number of used hardware bones in the models.
CUtlVector < bool > hardwareBonesUsed ;
hardwareBonesUsed . EnsureCount ( info . m_pStudioHdr - > numbones ) ;
int i ;
for ( i = 0 ; i < info . m_pStudioHdr - > numbones ; i + + )
{
hardwareBonesUsed [ i ] = false ;
}
// Warning( "\n\n\n" );
pResults - > m_NumMaterials = 0 ;
int numBoneStateChangeBatches = 0 ;
int numBoneStateChanges = 0 ;
// Iterate over every submodel...
IMaterial * * ppMaterials = info . m_pHardwareData - > m_pLODs [ info . m_Lod ] . ppMaterials ;
int nSkin = info . m_Skin ;
if ( nSkin > = info . m_pStudioHdr - > numskinfamilies )
{
nSkin = 0 ;
}
short * pSkinRef = info . m_pStudioHdr - > pSkinref ( nSkin * info . m_pStudioHdr - > numskinref ) ;
pResults - > m_NumBatches = 0 ;
for ( i = 0 ; i < info . m_pStudioHdr - > numbodyparts ; i + + )
{
mstudiomodel_t * pModel = NULL ;
R_StudioSetupModel ( i , info . m_Body , & pModel , info . m_pStudioHdr ) ;
// Iterate over all the meshes.... each mesh is a new material
int k ;
for ( k = 0 ; k < pModel - > nummeshes ; + + k )
{
mstudiomesh_t * pMesh = pModel - > pMesh ( k ) ;
IMaterial * pMaterial = ppMaterials [ pSkinRef [ pMesh - > material ] ] ;
Assert ( pMaterial ) ;
studiomeshdata_t * pMeshData = & pStudioMeshes [ pMesh - > meshid ] ;
if ( pMeshData - > m_NumGroup = = 0 )
continue ;
Assert ( pResults - > m_NumMaterials = = pResults - > m_Materials . Count ( ) ) ;
pResults - > m_NumMaterials + + ;
if ( pResults - > m_NumMaterials < MAX_DRAW_MODEL_INFO_MATERIALS )
{
pResults - > m_Materials . AddToTail ( pMaterial ) ;
}
else
{
Assert ( 0 ) ;
}
if ( pSpewBuf )
{
pSpewBuf - > Printf ( " material: %s \n " , pMaterial - > GetName ( ) ) ;
}
int numPasses = m_RC . m_pForcedMaterial ? m_RC . m_pForcedMaterial - > GetNumPasses ( ) : pMaterial - > GetNumPasses ( ) ;
if ( pSpewBuf )
{
pSpewBuf - > Printf ( " numPasses:%d \n " , numPasses ) ;
}
int bytes = pMaterial - > GetTextureMemoryBytes ( ) ;
pResults - > m_TextureMemoryBytes + = bytes ;
if ( pSpewBuf )
{
pSpewBuf - > Printf ( " texture memory: %d (Only valid in a rendering app) \n " , bytes ) ;
}
// Iterate over all stripgroups
int stripGroupID ;
for ( stripGroupID = 0 ; stripGroupID < pMeshData - > m_NumGroup ; stripGroupID + + )
{
studiomeshgroup_t * pMeshGroup = & pMeshData - > m_pMeshGroup [ stripGroupID ] ;
bool bIsFlexed = ( pMeshGroup - > m_Flags & MESHGROUP_IS_FLEXED ) ! = 0 ;
bool bIsHWSkinned = ( pMeshGroup - > m_Flags & MESHGROUP_IS_HWSKINNED ) ! = 0 ;
if ( pSpewBuf )
{
pSpewBuf - > Printf ( " %d batch(es): \n " , ( int ) pMeshGroup - > m_NumStrips ) ;
}
// Iterate over all strips. . . each strip potentially changes bones states.
int stripID ;
for ( stripID = 0 ; stripID < pMeshGroup - > m_NumStrips ; stripID + + )
{
pResults - > m_NumBatches + + ;
OptimizedModel : : StripHeader_t * pStripData = & pMeshGroup - > m_pStripData [ stripID ] ;
numBoneStateChangeBatches + + ;
numBoneStateChanges + = pStripData - > numBoneStateChanges ;
if ( bIsHWSkinned )
{
// Only count bones as hardware bones if we are using hardware skinning here.
int boneID ;
for ( boneID = 0 ; boneID < pStripData - > numBoneStateChanges ; boneID + + )
{
OptimizedModel : : BoneStateChangeHeader_t * pBoneStateChange = pStripData - > pBoneStateChange ( boneID ) ;
hardwareBonesUsed [ pBoneStateChange - > newBoneID ] = true ;
}
}
if ( pStripData - > flags & OptimizedModel : : STRIP_IS_TRILIST )
{
// TODO: need to factor in bIsFlexed and bIsHWSkinned
int numTris = pStripData - > numIndices / 3 ;
if ( pSpewBuf )
{
pSpewBuf - > Printf ( " %s%s " , bIsFlexed ? " flexed " : " nonflexed " ,
bIsHWSkinned ? " hwskinned " : " swskinned " ) ;
pSpewBuf - > Printf ( " tris: %d " , numTris ) ;
pSpewBuf - > Printf ( " bone changes: %d bones/strip: %d \n " , pStripData - > numBoneStateChanges ,
( int ) pStripData - > numBones ) ;
}
pResults - > m_ActualTriCount + = numTris * numPasses ;
}
else if ( pStripData - > flags & OptimizedModel : : STRIP_IS_TRISTRIP )
{
Assert ( 0 ) ; // FIXME: fill this in when we start using strips again.
}
else
{
Assert ( 0 ) ;
}
}
}
}
}
if ( pSpewBuf )
{
char nil = ' \0 ' ;
pSpewBuf - > Put ( & nil , 1 ) ; ;
}
pResults - > m_NumHardwareBones = 0 ;
for ( i = 0 ; i < info . m_pStudioHdr - > numbones ; i + + )
{
if ( hardwareBonesUsed [ i ] )
{
pResults - > m_NumHardwareBones + + ;
}
}
}
//-----------------------------------------------------------------------------
// Begin/end frame
//-----------------------------------------------------------------------------
static ConVar r_hwmorph ( " r_hwmorph " , " 1 " , FCVAR_CHEAT ) ;
void CStudioRenderContext : : BeginFrame ( void )
{
// Cache a few values here so I don't have to in software inner loops:
Assert ( g_pMaterialSystemHardwareConfig ) ;
m_RC . m_Config . m_bSupportsVertexAndPixelShaders = g_pMaterialSystemHardwareConfig - > SupportsVertexAndPixelShaders ( ) ;
m_RC . m_Config . m_bSupportsOverbright = g_pMaterialSystemHardwareConfig - > SupportsOverbright ( ) ;
m_RC . m_Config . m_bEnableHWMorph = r_hwmorph . GetInt ( ) ! = 0 ;
// Haven't implemented the hw morph with threading yet
if ( g_pMaterialSystem - > GetThreadMode ( ) ! = MATERIAL_SINGLE_THREADED )
{
m_RC . m_Config . m_bEnableHWMorph = false ;
}
m_RC . m_Config . m_bStatsMode = false ;
g_pStudioRenderImp - > PrecacheGlint ( ) ;
}
void CStudioRenderContext : : EndFrame ( void )
{
}
//-----------------------------------------------------------------------------
// Methods related to config
//-----------------------------------------------------------------------------
void CStudioRenderContext : : UpdateConfig ( const StudioRenderConfig_t & config )
{
memcpy ( & m_RC . m_Config , & config , sizeof ( StudioRenderConfig_t ) ) ;
}
void CStudioRenderContext : : GetCurrentConfig ( StudioRenderConfig_t & config )
{
memcpy ( & config , & m_RC . m_Config , sizeof ( StudioRenderConfig_t ) ) ;
}
//-----------------------------------------------------------------------------
// Material overrides
//-----------------------------------------------------------------------------
void CStudioRenderContext : : ForcedMaterialOverride ( IMaterial * newMaterial , OverrideType_t nOverrideType )
{
m_RC . m_pForcedMaterial = newMaterial ;
m_RC . m_nForcedMaterialType = nOverrideType ;
}
//-----------------------------------------------------------------------------
// Return the material overrides
//-----------------------------------------------------------------------------
void CStudioRenderContext : : GetMaterialOverride ( IMaterial * * ppOutForcedMaterial , OverrideType_t * pOutOverrideType )
{
Assert ( ppOutForcedMaterial ! = NULL & & pOutOverrideType ! = NULL ) ;
* ppOutForcedMaterial = m_RC . m_pForcedMaterial ;
* pOutOverrideType = m_RC . m_nForcedMaterialType ;
}
//-----------------------------------------------------------------------------
// Sets the view state
//-----------------------------------------------------------------------------
void CStudioRenderContext : : SetViewState ( const Vector & viewOrigin ,
const Vector & viewRight , const Vector & viewUp , const Vector & viewPlaneNormal )
{
VectorCopy ( viewOrigin , m_RC . m_ViewOrigin ) ;
VectorCopy ( viewRight , m_RC . m_ViewRight ) ;
VectorCopy ( viewUp , m_RC . m_ViewUp ) ;
VectorCopy ( viewPlaneNormal , m_RC . m_ViewPlaneNormal ) ;
}
//-----------------------------------------------------------------------------
// Sets lighting state
//-----------------------------------------------------------------------------
void CStudioRenderContext : : SetAmbientLightColors ( const Vector * pColors )
{
for ( int i = 0 ; i < 6 ; i + + )
{
VectorCopy ( pColors [ i ] , m_RC . m_LightBoxColors [ i ] . AsVector3D ( ) ) ;
m_RC . m_LightBoxColors [ i ] [ 3 ] = 1.0f ;
}
// FIXME: Would like to get this into the render thread, but there's systemic confusion
// about whether to set lighting state here or in the material system
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
pRenderContext - > SetAmbientLightCube ( m_RC . m_LightBoxColors ) ;
}
void CStudioRenderContext : : SetAmbientLightColors ( const Vector4D * pColors )
{
memcpy ( m_RC . m_LightBoxColors , pColors , 6 * sizeof ( Vector4D ) ) ;
// FIXME: Would like to get this into the render thread, but there's systemic confusion
// about whether to set lighting state here or in the material system
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
pRenderContext - > SetAmbientLightCube ( m_RC . m_LightBoxColors ) ;
}
void CStudioRenderContext : : SetLocalLights ( int nLightCount , const LightDesc_t * pLights )
{
m_RC . m_NumLocalLights = CopyLocalLightingState ( MAXLOCALLIGHTS , m_RC . m_LocalLights , nLightCount , pLights ) ;
// FIXME: Would like to get this into the render thread, but there's systemic confusion
// about whether to set lighting state here or in the material system
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
if ( m_RC . m_Config . bSoftwareLighting | | m_RC . m_NumLocalLights = = 0 )
{
pRenderContext - > DisableAllLocalLights ( ) ;
}
else
{
int i ;
int nMaxLightCount = g_pMaterialSystemHardwareConfig - > MaxNumLights ( ) ;
int nLightCount = min ( m_RC . m_NumLocalLights , nMaxLightCount ) ;
for ( i = 0 ; i < nLightCount ; i + + )
{
pRenderContext - > SetLight ( i , m_RC . m_LocalLights [ i ] ) ;
}
for ( ; i < nMaxLightCount ; i + + )
{
LightDesc_t desc ;
desc . m_Type = MATERIAL_LIGHT_DISABLE ;
pRenderContext - > SetLight ( i , desc ) ;
}
}
}
//-----------------------------------------------------------------------------
// Sets the color modulation
//-----------------------------------------------------------------------------
void CStudioRenderContext : : SetColorModulation ( const float * pColor )
{
VectorCopy ( pColor , m_RC . m_ColorMod ) ;
}
void CStudioRenderContext : : SetAlphaModulation ( float alpha )
{
m_RC . m_AlphaMod = alpha ;
}
//-----------------------------------------------------------------------------
// Used to set bone-to-world transforms.
// FIXME: Should this be a lock/unlock pattern so we can't read after unlock?
//-----------------------------------------------------------------------------
matrix3x4_t * CStudioRenderContext : : LockBoneMatrices ( int nCount )
{
MEM_ALLOC_CREDIT_ ( " CStudioRenderContext::m_BoneToWorldMatrices " ) ;
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
CMatRenderData < matrix3x4_t > rdMatrix ( pRenderContext ) ;
matrix3x4_t * pDest = rdMatrix . Lock ( nCount ) ;
return pDest ;
}
void CStudioRenderContext : : UnlockBoneMatrices ( )
{
}
//-----------------------------------------------------------------------------
// Allocates flex weights
//-----------------------------------------------------------------------------
void CStudioRenderContext : : LockFlexWeights ( int nWeightCount , float * * ppFlexWeights , float * * ppFlexDelayedWeights )
{
MEM_ALLOC_CREDIT_ ( " CStudioRenderContext::m_FlexWeights " ) ;
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
CMatRenderData < float > rdFlex ( pRenderContext ) ;
CMatRenderData < float > rdFlexDelayed ( pRenderContext ) ;
float * pFlexOut = rdFlex . Lock ( nWeightCount ) ;
for ( int i = 0 ; i < nWeightCount ; i + + )
{
pFlexOut [ i ] = 0.0f ;
}
* ppFlexWeights = pFlexOut ;
if ( ppFlexDelayedWeights )
{
pFlexOut = rdFlexDelayed . Lock ( nWeightCount ) ;
for ( int i = 0 ; i < nWeightCount ; i + + )
{
pFlexOut [ i ] = 0.0f ;
}
* ppFlexDelayedWeights = pFlexOut ;
}
}
void CStudioRenderContext : : UnlockFlexWeights ( )
{
}
//-----------------------------------------------------------------------------
// Methods related to flex weights
//-----------------------------------------------------------------------------
static ConVar r_randomflex ( " r_randomflex " , " 0 " , FCVAR_CHEAT ) ;
//-----------------------------------------------------------------------------
// This will generate random flex data that has a specified # of non-zero values
//-----------------------------------------------------------------------------
void CStudioRenderContext : : GenerateRandomFlexWeights ( int nWeightCount , float * pWeights , float * pDelayedWeights )
{
int nRandomFlex = r_randomflex . GetInt ( ) ;
if ( nRandomFlex < = 0 | | ! pWeights )
return ;
if ( nRandomFlex > nWeightCount )
{
nRandomFlex = nWeightCount ;
}
int * pIndices = ( int * ) _alloca ( nWeightCount * sizeof ( int ) ) ;
for ( int i = 0 ; i < nWeightCount ; + + i )
{
pIndices [ i ] = i ;
}
// Shuffle
for ( int i = 0 ; i < nWeightCount ; + + i )
{
int n = RandomInt ( 0 , nWeightCount - 1 ) ;
int nTemp = pIndices [ n ] ;
pIndices [ n ] = pIndices [ i ] ;
pIndices [ i ] = nTemp ;
}
memset ( pWeights , 0 , nWeightCount * sizeof ( float ) ) ;
for ( int i = 0 ; i < nRandomFlex ; + + i )
{
pWeights [ pIndices [ i ] ] = RandomFloat ( 0.0f , 1.0f ) ;
}
if ( pDelayedWeights )
{
memset ( pDelayedWeights , 0 , nWeightCount * sizeof ( float ) ) ;
for ( int i = 0 ; i < nRandomFlex ; + + i )
{
pDelayedWeights [ pIndices [ i ] ] = RandomFloat ( 0.0f , 1.0f ) ;
}
}
}
//-----------------------------------------------------------------------------
// Computes LOD
//-----------------------------------------------------------------------------
int CStudioRenderContext : : ComputeRenderLOD ( IMatRenderContext * pRenderContext ,
const DrawModelInfo_t & info , const Vector & origin , float * pMetric )
{
int lod = info . m_Lod ;
int lastlod = info . m_pHardwareData - > m_NumLODs - 1 ;
if ( pMetric )
{
* pMetric = 0.0f ;
}
if ( lod = = USESHADOWLOD )
return lastlod ;
if ( lod ! = - 1 )
return clamp ( lod , info . m_pHardwareData - > m_RootLOD , lastlod ) ;
float screenSize = pRenderContext - > ComputePixelWidthOfSphere ( origin , 0.5f ) ;
lod = ComputeModelLODAndMetric ( info . m_pHardwareData , screenSize , pMetric ) ;
// make sure we have a valid lod
if ( info . m_pStudioHdr - > flags & STUDIOHDR_FLAGS_HASSHADOWLOD )
{
lastlod - - ;
}
lod = clamp ( lod , info . m_pHardwareData - > m_RootLOD , lastlod ) ;
return lod ;
}
//-----------------------------------------------------------------------------
// This invokes proxies of all materials that are queued to be rendered
// It has the effect of ensuring the material vars are in the correct state
// since material var sets generated by the proxy bind are queued.
//-----------------------------------------------------------------------------
void CStudioRenderContext : : InvokeBindProxies ( const DrawModelInfo_t & info )
{
if ( m_RC . m_pForcedMaterial )
{
if ( m_RC . m_nForcedMaterialType = = OVERRIDE_NORMAL & & m_RC . m_pForcedMaterial - > HasProxy ( ) )
{
m_RC . m_pForcedMaterial - > CallBindProxy ( info . m_pClientEntity ) ;
}
return ;
}
// get skinref array
int nSkin = ( m_RC . m_Config . skin > 0 ) ? m_RC . m_Config . skin : info . m_Skin ;
short * pSkinRef = info . m_pStudioHdr - > pSkinref ( 0 ) ;
if ( nSkin > 0 & & nSkin < info . m_pStudioHdr - > numskinfamilies )
{
pSkinRef + = ( nSkin * info . m_pStudioHdr - > numskinref ) ;
}
// This is used to ensure proxies are only called once
int nBufSize = info . m_pStudioHdr - > numtextures * sizeof ( bool ) ;
bool * pProxyCalled = ( bool * ) stackalloc ( nBufSize ) ;
memset ( pProxyCalled , 0 , nBufSize ) ;
IMaterial * * ppMaterials = info . m_pHardwareData - > m_pLODs [ info . m_Lod ] . ppMaterials ;
mstudiomodel_t * pModel ;
for ( int i = 0 ; i < info . m_pStudioHdr - > numbodyparts ; + + i )
{
R_StudioSetupModel ( i , info . m_Body , & pModel , info . m_pStudioHdr ) ;
for ( int somethingOtherThanI = 0 ; somethingOtherThanI < pModel - > nummeshes ; + + somethingOtherThanI )
{
mstudiomesh_t * pMesh = pModel - > pMesh ( somethingOtherThanI ) ;
int nMaterialIndex = pSkinRef [ pMesh - > material ] ;
if ( pProxyCalled [ nMaterialIndex ] )
continue ;
pProxyCalled [ nMaterialIndex ] = true ;
IMaterial * pMaterial = ppMaterials [ nMaterialIndex ] ;
if ( pMaterial & & pMaterial - > HasProxy ( ) )
{
pMaterial - > CallBindProxy ( info . m_pClientEntity ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Draws a model
//-----------------------------------------------------------------------------
void CStudioRenderContext : : DrawModel ( DrawModelResults_t * pResults , const DrawModelInfo_t & info ,
matrix3x4_t * pBoneToWorld , float * pFlexWeights , float * pFlexDelayedWeights , const Vector & origin , int flags )
{
// Set to zero in case we don't render anything.
if ( pResults )
{
pResults - > m_ActualTriCount = pResults - > m_TextureMemoryBytes = 0 ;
}
if ( ! info . m_pStudioHdr | | ! info . m_pHardwareData | |
! info . m_pHardwareData - > m_NumLODs | | ! info . m_pHardwareData - > m_pLODs )
{
return ;
}
// Replace the flex weight data with random data for testing
GenerateRandomFlexWeights ( info . m_pStudioHdr - > numflexdesc , pFlexWeights , pFlexDelayedWeights ) ;
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
float flMetric ;
const_cast < DrawModelInfo_t * > ( & info ) - > m_Lod = ComputeRenderLOD ( pRenderContext , info , origin , & flMetric ) ;
if ( pResults )
{
pResults - > m_nLODUsed = info . m_Lod ;
pResults - > m_flLODMetric = flMetric ;
}
MaterialLock_t hLock = 0 ;
if ( flags & STUDIORENDER_DRAW_ACCURATETIME )
{
VPROF ( " STUDIORENDER_DRAW_ACCURATETIME " ) ;
// Flush the material system before timing this model:
hLock = g_pMaterialSystem - > Lock ( ) ;
g_pMaterialSystem - > Flush ( true ) ;
}
if ( pResults )
{
pResults - > m_RenderTime . Start ( ) ;
}
FlexWeights_t flex ;
flex . m_pFlexWeights = pFlexWeights ? pFlexWeights : s_pZeroFlexWeights ;
flex . m_pFlexDelayedWeights = pFlexDelayedWeights ? pFlexDelayedWeights : flex . m_pFlexWeights ;
ICallQueue * pCallQueue = pRenderContext - > GetCallQueue ( ) ;
if ( ! pCallQueue | | studio_queue_mode . GetInt ( ) = = 0 )
{
g_pStudioRenderImp - > DrawModel ( info , m_RC , pBoneToWorld , flex , flags ) ;
}
else
{
CMatRenderData < matrix3x4_t > rdMatrix ( pRenderContext , info . m_pStudioHdr - > numbones , pBoneToWorld ) ;
CMatRenderData < float > rdFlex ( pRenderContext ) ;
CMatRenderData < float > rdFlexDelayed ( pRenderContext ) ;
InvokeBindProxies ( info ) ;
pBoneToWorld = rdMatrix . Base ( ) ;
if ( info . m_pStudioHdr - > numflexdesc ! = 0 )
{
rdFlex . Lock ( info . m_pStudioHdr - > numflexdesc , flex . m_pFlexWeights ) ;
flex . m_pFlexWeights = rdFlex . Base ( ) ;
if ( ! pFlexDelayedWeights )
{
flex . m_pFlexDelayedWeights = flex . m_pFlexWeights ;
}
else
{
rdFlexDelayed . Lock ( info . m_pStudioHdr - > numflexdesc , flex . m_pFlexDelayedWeights ) ;
flex . m_pFlexDelayedWeights = rdFlexDelayed . Base ( ) ;
}
}
pCallQueue - > QueueCall ( g_pStudioRenderImp , & CStudioRender : : DrawModel , info , m_RC , pBoneToWorld , flex , flags ) ;
}
if ( flags & STUDIORENDER_DRAW_ACCURATETIME )
{
VPROF ( " STUDIORENDER_DRAW_ACCURATETIME " ) ;
// Make sure this model is completely drawn before ending the timer:
g_pMaterialSystem - > Flush ( true ) ;
g_pMaterialSystem - > Flush ( true ) ;
g_pMaterialSystem - > Unlock ( hLock ) ;
}
if ( pResults )
{
pResults - > m_RenderTime . End ( ) ;
if ( flags & STUDIORENDER_DRAW_GET_PERF_STATS )
{
GetPerfStats ( pResults , info , 0 ) ;
}
}
}
void CStudioRenderContext : : DrawModelArray ( const DrawModelInfo_t & drawInfo , int arrayCount , model_array_instance_t * pInstanceData , int instanceStride , int flags )
{
// UNDONE: Support queue mode?
g_pStudioRenderImp - > DrawModelArray ( drawInfo , m_RC , arrayCount , pInstanceData , instanceStride , flags ) ;
}
//-----------------------------------------------------------------------------
// Methods related to rendering static props
//-----------------------------------------------------------------------------
void CStudioRenderContext : : DrawModelStaticProp ( const DrawModelInfo_t & info , const matrix3x4_t & modelToWorld , int flags )
{
if ( info . m_Lod < info . m_pHardwareData - > m_RootLOD )
{
const_cast < DrawModelInfo_t * > ( & info ) - > m_Lod = info . m_pHardwareData - > m_RootLOD ;
}
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
ICallQueue * pCallQueue = pRenderContext - > GetCallQueue ( ) ;
if ( ! pCallQueue | | studio_queue_mode . GetInt ( ) = = 0 )
{
g_pStudioRenderImp - > DrawModelStaticProp ( info , m_RC , modelToWorld , flags ) ;
}
else
{
InvokeBindProxies ( info ) ;
pCallQueue - > QueueCall ( g_pStudioRenderImp , & CStudioRender : : DrawModelStaticProp , info , m_RC , modelToWorld , flags ) ;
}
}
void CStudioRenderContext : : DrawStaticPropDecals ( const DrawModelInfo_t & info , const matrix3x4_t & modelToWorld )
{
QUEUE_STUDIORENDER_CALL ( DrawStaticPropDecals , CStudioRender , g_pStudioRenderImp , info , m_RC , modelToWorld ) ;
}
void CStudioRenderContext : : DrawStaticPropShadows ( const DrawModelInfo_t & info , const matrix3x4_t & modelToWorld , int flags )
{
QUEUE_STUDIORENDER_CALL ( DrawStaticPropShadows , CStudioRender , g_pStudioRenderImp , info , m_RC , modelToWorld , flags ) ;
}
//-----------------------------------------------------------------------------
// Methods related to shadows
//-----------------------------------------------------------------------------
void CStudioRenderContext : : AddShadow ( IMaterial * pMaterial , void * pProxyData ,
FlashlightState_t * pFlashlightState , VMatrix * pWorldToTexture , ITexture * pFlashlightDepthTexture )
{
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
ICallQueue * pCallQueue = pRenderContext - > GetCallQueue ( ) ;
if ( ! pCallQueue | | studio_queue_mode . GetInt ( ) = = 0 )
{
g_pStudioRenderImp - > AddShadow ( pMaterial , pProxyData , pFlashlightState , pWorldToTexture , pFlashlightDepthTexture ) ;
}
else
{
// NOTE: We don't need to make proxies work, because proxies are only ever used
// when casting shadows onto props, which we don't do..that feature is disabled.
// When casting flashlights onto mdls, which we *do* use, the proxy is NULL.
Assert ( pProxyData = = NULL ) ;
if ( pProxyData ! = NULL )
{
Warning ( " Cannot call CStudioRenderContext::AddShadows w/ proxies in queued mode! \n " ) ;
return ;
}
CMatRenderData < FlashlightState_t > rdFlashlight ( pRenderContext , 1 , pFlashlightState ) ;
CMatRenderData < VMatrix > rdMatrix ( pRenderContext , 1 , pWorldToTexture ) ;
pCallQueue - > QueueCall ( g_pStudioRenderImp , & CStudioRender : : AddShadow , pMaterial ,
( void * ) NULL , rdFlashlight . Base ( ) , rdMatrix . Base ( ) , pFlashlightDepthTexture ) ;
}
}
void CStudioRenderContext : : ClearAllShadows ( )
{
QUEUE_STUDIORENDER_CALL ( ClearAllShadows , CStudioRender , g_pStudioRenderImp ) ;
}
//-----------------------------------------------------------------------------
// Methods related to decals
//-----------------------------------------------------------------------------
void CStudioRenderContext : : DestroyDecalList ( StudioDecalHandle_t handle )
{
QUEUE_STUDIORENDER_CALL ( DestroyDecalList , CStudioRender , g_pStudioRenderImp , handle ) ;
}
void CStudioRenderContext : : AddDecal ( StudioDecalHandle_t handle , studiohdr_t * pStudioHdr ,
matrix3x4_t * pBoneToWorld , const Ray_t & ray , const Vector & decalUp ,
IMaterial * pDecalMaterial , float radius , int body , bool noPokethru , int maxLODToDecal )
{
// This substition always has to be done in the main thread, so do it here.
pDecalMaterial = GetModelSpecificDecalMaterial ( pDecalMaterial ) ;
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
Assert ( pRenderContext - > IsRenderData ( pBoneToWorld ) ) ;
QUEUE_STUDIORENDER_CALL_RC ( AddDecal , CStudioRender , g_pStudioRenderImp , pRenderContext ,
handle , m_RC , pBoneToWorld , pStudioHdr , ray , decalUp , pDecalMaterial , radius ,
body , noPokethru , maxLODToDecal ) ;
}
// Function to do replacement because we always need to do this from the main thread.
IMaterial * GetModelSpecificDecalMaterial ( IMaterial * pDecalMaterial )
{
Assert ( ThreadInMainThread ( ) ) ;
// Since we're adding this to a studio model, check the decal to see if
// there's an alternate form used for static props...
bool found ;
IMaterialVar * pModelMaterialVar = pDecalMaterial - > FindVar ( " $modelmaterial " , & found , false ) ;
if ( found )
{
IMaterial * pModelMaterial = g_pMaterialSystem - > FindMaterial ( pModelMaterialVar - > GetStringValue ( ) , TEXTURE_GROUP_DECAL , false ) ;
if ( ! IsErrorMaterial ( pModelMaterial ) )
{
return pModelMaterial ;
}
}
return pDecalMaterial ;
}