2020-04-23 00:56:21 +08:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// models are the only shared resource between a client and server running
// on the same machine.
//===========================================================================//
# include "render_pch.h"
# include "client.h"
# include "gl_model_private.h"
# include "studio.h"
# include "phyfile.h"
# include "cdll_int.h"
# include "istudiorender.h"
# include "client_class.h"
# include "float.h"
# include "materialsystem/imaterialsystemhardwareconfig.h"
# include "materialsystem/ivballoctracker.h"
# include "modelloader.h"
# include "lightcache.h"
# include "studio_internal.h"
# include "cdll_engine_int.h"
# include "vphysics_interface.h"
# include "utllinkedlist.h"
# include "studio.h"
# include "icliententitylist.h"
# include "engine/ivmodelrender.h"
# include "optimize.h"
# include "icliententity.h"
# include "sys_dll.h"
# include "debugoverlay.h"
# include "enginetrace.h"
# include "l_studio.h"
# include "filesystem_engine.h"
# include "ModelInfo.h"
# include "cl_main.h"
# include "tier0/vprof.h"
# include "r_decal.h"
# include "vstdlib/random.h"
# include "datacache/idatacache.h"
# include "materialsystem/materialsystem_config.h"
# include "materialsystem/itexture.h"
# include "IHammer.h"
# if defined( _WIN32 ) && !defined( _X360 )
# include <xmmintrin.h>
# endif
# include "staticpropmgr.h"
# include "materialsystem/hardwaretexels.h"
# include "materialsystem/hardwareverts.h"
# include "tier1/callqueue.h"
# include "filesystem/IQueuedLoader.h"
# include "tier2/tier2.h"
# include "tier1/UtlSortVector.h"
# include "tier1/lzmaDecoder.h"
# include "ipooledvballocator.h"
# include "shaderapi/ishaderapi.h"
// memdbgon must be the last include file in a .cpp file!!!
# include "tier0/memdbgon.h"
// #define VISUALIZE_TIME_AVERAGE 1
extern ConVar r_flashlight_version2 ;
//-----------------------------------------------------------------------------
// Forward declarations
//-----------------------------------------------------------------------------
void R_StudioInitLightingCache ( void ) ;
float Engine_WorldLightDistanceFalloff ( const dworldlight_t * wl , const Vector & delta , bool bNoRadiusCheck = false ) ;
void SetRootLOD_f ( IConVar * var , const char * pOldString , float flOldValue ) ;
void r_lod_f ( IConVar * var , const char * pOldValue , float flOldValue ) ;
void FlushLOD_f ( ) ;
class CColorMeshData ;
static void CreateLightmapsFromData ( CColorMeshData * _colorMeshData ) ;
//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
ConVar r_drawmodelstatsoverlay ( " r_drawmodelstatsoverlay " , " 0 " , FCVAR_CHEAT ) ;
ConVar r_drawmodelstatsoverlaydistance ( " r_drawmodelstatsoverlaydistance " , " 500 " , FCVAR_CHEAT ) ;
ConVar r_drawmodellightorigin ( " r_DrawModelLightOrigin " , " 0 " , FCVAR_CHEAT ) ;
extern ConVar r_worldlights ;
ConVar r_lod ( " r_lod " , " -1 " , 0 , " " , r_lod_f ) ;
static ConVar r_entity ( " r_entity " , " -1 " , FCVAR_CHEAT | FCVAR_DEVELOPMENTONLY ) ;
static ConVar r_lightaverage ( " r_lightaverage " , " 1 " , 0 , " Activates/deactivate light averaging " ) ;
static ConVar r_lightinterp ( " r_lightinterp " , " 5 " , FCVAR_CHEAT , " Controls the speed of light interpolation, 0 turns off interpolation " ) ;
static ConVar r_eyeglintlodpixels ( " r_eyeglintlodpixels " , " 20.0 " , FCVAR_CHEAT , " The number of pixels wide an eyeball has to be before rendering an eyeglint. Is a floating point value. " ) ;
ConVar r_rootlod ( " r_rootlod " , " 0 " , FCVAR_MATERIAL_SYSTEM_THREAD | FCVAR_ARCHIVE , " Root LOD " , true , 0 , true , MAX_NUM_LODS - 1 , SetRootLOD_f ) ;
static ConVar r_decalstaticprops ( " r_decalstaticprops " , " 1 " , 0 , " Decal static props test " ) ;
static ConCommand r_flushlod ( " r_flushlod " , FlushLOD_f , " Flush and reload LODs. " ) ;
ConVar r_debugrandomstaticlighting ( " r_debugrandomstaticlighting " , " 0 " , FCVAR_CHEAT , " Set to 1 to randomize static lighting for debugging. Must restart for change to take affect. " ) ;
ConVar r_proplightingfromdisk ( " r_proplightingfromdisk " , " 1 " , FCVAR_CHEAT , " 0=Off, 1=On, 2=Show Errors " ) ;
static ConVar r_itemblinkmax ( " r_itemblinkmax " , " .3 " , FCVAR_CHEAT ) ;
static ConVar r_itemblinkrate ( " r_itemblinkrate " , " 4.5 " , FCVAR_CHEAT ) ;
static ConVar r_proplightingpooling ( " r_proplightingpooling " , " -1.0 " , FCVAR_CHEAT , " 0 - off, 1 - static prop color meshes are allocated from a single shared vertex buffer (on hardware that supports stream offset) " ) ;
//-----------------------------------------------------------------------------
// StudioRender config
//-----------------------------------------------------------------------------
static ConVar r_showenvcubemap ( " r_showenvcubemap " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_eyemove ( " r_eyemove " , " 1 " , FCVAR_ARCHIVE ) ; // look around
static ConVar r_eyeshift_x ( " r_eyeshift_x " , " 0 " , FCVAR_ARCHIVE ) ; // eye X position
static ConVar r_eyeshift_y ( " r_eyeshift_y " , " 0 " , FCVAR_ARCHIVE ) ; // eye Y position
static ConVar r_eyeshift_z ( " r_eyeshift_z " , " 0 " , FCVAR_ARCHIVE ) ; // eye Z position
static ConVar r_eyesize ( " r_eyesize " , " 0 " , FCVAR_ARCHIVE ) ; // adjustment to iris textures
static ConVar mat_softwareskin ( " mat_softwareskin " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_nohw ( " r_nohw " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_nosw ( " r_nosw " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_teeth ( " r_teeth " , " 1 " ) ;
static ConVar r_drawentities ( " r_drawentities " , " 1 " , FCVAR_CHEAT ) ;
static ConVar r_flex ( " r_flex " , " 1 " ) ;
static ConVar r_eyes ( " r_eyes " , " 1 " ) ;
static ConVar r_skin ( " r_skin " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_modelwireframedecal ( " r_modelwireframedecal " , " 0 " , FCVAR_CHEAT ) ;
static ConVar r_maxmodeldecal ( " r_maxmodeldecal " , " 50 " ) ;
static StudioRenderConfig_t s_StudioRenderConfig ;
void UpdateStudioRenderConfig ( void )
{
// This can happen during initialization
if ( ! g_pMaterialSystemConfig | | ! g_pStudioRender )
return ;
memset ( & s_StudioRenderConfig , 0 , sizeof ( s_StudioRenderConfig ) ) ;
s_StudioRenderConfig . bEyeMove = ! ! r_eyemove . GetInt ( ) ;
s_StudioRenderConfig . fEyeShiftX = r_eyeshift_x . GetFloat ( ) ;
s_StudioRenderConfig . fEyeShiftY = r_eyeshift_y . GetFloat ( ) ;
s_StudioRenderConfig . fEyeShiftZ = r_eyeshift_z . GetFloat ( ) ;
s_StudioRenderConfig . fEyeSize = r_eyesize . GetFloat ( ) ;
if ( IsPC ( ) & & ( mat_softwareskin . GetInt ( ) | | ShouldDrawInWireFrameMode ( ) ) )
{
s_StudioRenderConfig . bSoftwareSkin = true ;
}
else
{
s_StudioRenderConfig . bSoftwareSkin = false ;
}
s_StudioRenderConfig . bNoHardware = ! ! r_nohw . GetInt ( ) ;
s_StudioRenderConfig . bNoSoftware = ! ! r_nosw . GetInt ( ) ;
s_StudioRenderConfig . bTeeth = ! ! r_teeth . GetInt ( ) ;
s_StudioRenderConfig . drawEntities = r_drawentities . GetInt ( ) ;
s_StudioRenderConfig . bFlex = ! ! r_flex . GetInt ( ) ;
s_StudioRenderConfig . bEyes = ! ! r_eyes . GetInt ( ) ;
s_StudioRenderConfig . bWireframe = ShouldDrawInWireFrameMode ( ) ;
s_StudioRenderConfig . bDrawNormals = mat_normals . GetBool ( ) ;
s_StudioRenderConfig . skin = r_skin . GetInt ( ) ;
s_StudioRenderConfig . maxDecalsPerModel = r_maxmodeldecal . GetInt ( ) ;
s_StudioRenderConfig . bWireframeDecals = r_modelwireframedecal . GetInt ( ) ! = 0 ;
s_StudioRenderConfig . fullbright = g_pMaterialSystemConfig - > nFullbright ;
s_StudioRenderConfig . bSoftwareLighting = g_pMaterialSystemConfig - > bSoftwareLighting ;
s_StudioRenderConfig . bShowEnvCubemapOnly = r_showenvcubemap . GetInt ( ) ? true : false ;
s_StudioRenderConfig . fEyeGlintPixelWidthLODThreshold = r_eyeglintlodpixels . GetFloat ( ) ;
g_pStudioRender - > UpdateConfig ( s_StudioRenderConfig ) ;
}
void R_InitStudio ( void )
{
# ifndef SWDS
R_StudioInitLightingCache ( ) ;
# endif
}
//-----------------------------------------------------------------------------
// Converts world lights to materialsystem lights
//-----------------------------------------------------------------------------
# define MIN_LIGHT_VALUE 0.03f
bool WorldLightToMaterialLight ( dworldlight_t * pWorldLight , LightDesc_t & light )
{
// BAD
light . m_Attenuation0 = 0.0f ;
light . m_Attenuation1 = 0.0f ;
light . m_Attenuation2 = 0.0f ;
switch ( pWorldLight - > type )
{
case emit_spotlight :
light . m_Type = MATERIAL_LIGHT_SPOT ;
light . m_Attenuation0 = pWorldLight - > constant_attn ;
light . m_Attenuation1 = pWorldLight - > linear_attn ;
light . m_Attenuation2 = pWorldLight - > quadratic_attn ;
light . m_Theta = 2.0 * acos ( pWorldLight - > stopdot ) ;
light . m_Phi = 2.0 * acos ( pWorldLight - > stopdot2 ) ;
light . m_ThetaDot = pWorldLight - > stopdot ;
light . m_PhiDot = pWorldLight - > stopdot2 ;
light . m_Falloff = pWorldLight - > exponent ? pWorldLight - > exponent : 1.0f ;
break ;
case emit_surface :
// A 180 degree spotlight
light . m_Type = MATERIAL_LIGHT_SPOT ;
light . m_Attenuation2 = 1.0 ;
light . m_Theta = M_PI ;
light . m_Phi = M_PI ;
light . m_ThetaDot = 0.0f ;
light . m_PhiDot = 0.0f ;
light . m_Falloff = 1.0f ;
break ;
case emit_point :
light . m_Type = MATERIAL_LIGHT_POINT ;
light . m_Attenuation0 = pWorldLight - > constant_attn ;
light . m_Attenuation1 = pWorldLight - > linear_attn ;
light . m_Attenuation2 = pWorldLight - > quadratic_attn ;
break ;
case emit_skylight :
light . m_Type = MATERIAL_LIGHT_DIRECTIONAL ;
break ;
// NOTE: Can't do quake lights in hardware (x-r factor)
case emit_quakelight : // not supported
case emit_skyambient : // doesn't factor into local lighting
// skip these
return false ;
}
// No attenuation case..
if ( ( light . m_Attenuation0 = = 0.0f ) & & ( light . m_Attenuation1 = = 0.0f ) & &
( light . m_Attenuation2 = = 0.0f ) )
{
light . m_Attenuation0 = 1.0f ;
}
// renormalize light intensity...
memcpy ( & light . m_Position , & pWorldLight - > origin , 3 * sizeof ( float ) ) ;
memcpy ( & light . m_Direction , & pWorldLight - > normal , 3 * sizeof ( float ) ) ;
light . m_Color [ 0 ] = pWorldLight - > intensity [ 0 ] ;
light . m_Color [ 1 ] = pWorldLight - > intensity [ 1 ] ;
light . m_Color [ 2 ] = pWorldLight - > intensity [ 2 ] ;
// Make it stop when the lighting gets to min%...
float intensity = sqrtf ( DotProduct ( light . m_Color , light . m_Color ) ) ;
// Compute the light range based on attenuation factors
if ( pWorldLight - > radius ! = 0 )
{
light . m_Range = pWorldLight - > radius ;
}
else
{
// FALLBACK: older lights use this
if ( light . m_Attenuation2 = = 0.0f )
{
if ( light . m_Attenuation1 = = 0.0f )
{
light . m_Range = sqrtf ( FLT_MAX ) ;
}
else
{
light . m_Range = ( intensity / MIN_LIGHT_VALUE - light . m_Attenuation0 ) / light . m_Attenuation1 ;
}
}
else
{
float a = light . m_Attenuation2 ;
float b = light . m_Attenuation1 ;
float c = light . m_Attenuation0 - intensity / MIN_LIGHT_VALUE ;
float discrim = b * b - 4 * a * c ;
if ( discrim < 0.0f )
light . m_Range = sqrtf ( FLT_MAX ) ;
else
{
light . m_Range = ( - b + sqrtf ( discrim ) ) / ( 2.0f * a ) ;
if ( light . m_Range < 0 )
light . m_Range = 0 ;
}
}
}
light . m_Flags = LIGHTTYPE_OPTIMIZATIONFLAGS_DERIVED_VALUES_CALCED ;
if ( light . m_Attenuation0 ! = 0.0f )
{
light . m_Flags | = LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0 ;
}
if ( light . m_Attenuation1 ! = 0.0f )
{
light . m_Flags | = LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1 ;
}
if ( light . m_Attenuation2 ! = 0.0f )
{
light . m_Flags | = LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2 ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Sets the hardware lighting state
//-----------------------------------------------------------------------------
static void R_SetNonAmbientLightingState ( int numLights , dworldlight_t * locallight [ MAXLOCALLIGHTS ] ,
int * pNumLightDescs , LightDesc_t * pLightDescs , bool bUpdateStudioRenderLights )
{
Assert ( numLights > = 0 & & numLights < = MAXLOCALLIGHTS ) ;
// convert dworldlight_t's to LightDesc_t's and send 'em down to g_pStudioRender->
* pNumLightDescs = 0 ;
LightDesc_t * pLightDesc ;
for ( int i = 0 ; i < numLights ; i + + )
{
pLightDesc = & pLightDescs [ * pNumLightDescs ] ;
if ( ! WorldLightToMaterialLight ( locallight [ i ] , * pLightDesc ) )
continue ;
// Apply lightstyle
float bias = LightStyleValue ( locallight [ i ] - > style ) ;
// Deal with overbrighting + bias
pLightDesc - > m_Color [ 0 ] * = bias ;
pLightDesc - > m_Color [ 1 ] * = bias ;
pLightDesc - > m_Color [ 2 ] * = bias ;
* pNumLightDescs + = 1 ;
Assert ( * pNumLightDescs < = MAXLOCALLIGHTS ) ;
}
if ( bUpdateStudioRenderLights )
{
g_pStudioRender - > SetLocalLights ( * pNumLightDescs , pLightDescs ) ;
}
}
//-----------------------------------------------------------------------------
// Computes the center of the studio model for illumination purposes
//-----------------------------------------------------------------------------
void R_ComputeLightingOrigin ( IClientRenderable * pRenderable , studiohdr_t * pStudioHdr , const matrix3x4_t & matrix , Vector & center )
{
int nAttachmentIndex = pStudioHdr - > IllumPositionAttachmentIndex ( ) ;
if ( nAttachmentIndex < = 0 )
{
VectorTransform ( pStudioHdr - > illumposition , matrix , center ) ;
}
else
{
matrix3x4_t attachment ;
pRenderable - > GetAttachment ( nAttachmentIndex , attachment ) ;
VectorTransform ( pStudioHdr - > illumposition , attachment , center ) ;
}
}
#if 0
// garymct - leave this in here for now. . we might need this for bumped models
void R_StudioCalculateVirtualLightAndLightCube ( Vector & mid , Vector & virtualLightPosition ,
Vector & virtualLightColor , Vector * lightBoxColor )
{
int i , j ;
Vector delta ;
float dist2 , ratio ;
byte * pvis ;
float t ;
static ConVar bumpLightBlendRatioMin ( " bump_light_blend_ratio_min " , " 0.00002 " ) ;
static ConVar bumpLightBlendRatioMax ( " bump_light_blend_ratio_max " , " 0.00004 " ) ;
if ( g_pMaterialSystemConfig - > nFullbright = = 1 )
return ;
VectorClear ( virtualLightPosition ) ;
VectorClear ( virtualLightColor ) ;
for ( i = 0 ; i < 6 ; i + + )
{
VectorClear ( lightBoxColor [ i ] ) ;
}
byte pvs [ MAX_MAP_LEAFS / 8 ] ;
pvis = CM_Vis ( pvs , sizeof ( pvs ) , CM_LeafCluster ( CM_PointLeafnum ( mid ) , DVIS_PVS ) ;
float sumBumpBlendParam = 0 ;
for ( i = 0 ; i < host_state . worldbrush - > numworldlights ; i + + )
{
dworldlight_t * wl = & host_state . worldbrush - > worldlights [ i ] ;
if ( wl - > cluster < 0 )
continue ;
// only do it if the entity can see into the lights leaf
if ( ! BIT_SET ( pvis , ( wl - > cluster ) ) )
continue ;
// hack: for this test, only deal with point light sources.
if ( wl - > type ! = emit_point )
continue ;
// check distance
VectorSubtract ( wl - > origin , mid , delta ) ;
dist2 = DotProduct ( delta , delta ) ;
ratio = R_WorldLightDistanceFalloff ( wl , delta ) ;
VectorNormalize ( delta ) ;
ratio = ratio * R_WorldLightAngle ( wl , wl - > normal , delta , delta ) ;
float bumpBlendParam ; // 0.0 = all cube, 1.0 = all bump
// lerp
bumpBlendParam =
( ratio - bumpLightBlendRatioMin . GetFloat ( ) ) /
( bumpLightBlendRatioMax . GetFloat ( ) - bumpLightBlendRatioMin . GetFloat ( ) ) ;
if ( bumpBlendParam > 0.0 )
{
// Get the bit that goes into the bump light
sumBumpBlendParam + = bumpBlendParam ;
VectorMA ( virtualLightPosition , bumpBlendParam , wl - > origin , virtualLightPosition ) ;
VectorMA ( virtualLightColor , bumpBlendParam , wl - > intensity , virtualLightColor ) ;
}
if ( bumpBlendParam < 1.0f )
{
// Get the bit that goes into the cube
float cubeBlendParam ;
cubeBlendParam = 1.0f - bumpBlendParam ;
if ( cubeBlendParam < 0.0f )
{
cubeBlendParam = 0.0f ;
}
for ( j = 0 ; j < numBoxDir ; j + + )
{
t = DotProduct ( r_boxdir [ j ] , delta ) ;
if ( t > 0 )
{
VectorMA ( lightBoxColor [ j ] , ratio * t * cubeBlendParam , wl - > intensity , lightBoxColor [ j ] ) ;
}
}
}
}
// Get the final virtual light position and color.
VectorMultiply ( virtualLightPosition , 1.0f / sumBumpBlendParam , virtualLightPosition ) ;
VectorMultiply ( virtualLightColor , 1.0f / sumBumpBlendParam , virtualLightColor ) ;
}
# endif
// TODO: move cone calcs to position
// TODO: cone clipping calc's wont work for boxlight since the player asks for a single point. Not sure what the volume is.
float Engine_WorldLightDistanceFalloff ( const dworldlight_t * wl , const Vector & delta , bool bNoRadiusCheck )
{
float falloff ;
switch ( wl - > type )
{
case emit_surface :
# if 1
// Cull out stuff that's too far
if ( wl - > radius ! = 0 )
{
if ( DotProduct ( delta , delta ) > ( wl - > radius * wl - > radius ) )
return 0.0f ;
}
return InvRSquared ( delta ) ;
# else
// 1/r*r
falloff = DotProduct ( delta , delta ) ;
if ( falloff < 1 )
return 1.f ;
else
return 1.f / falloff ;
# endif
break ;
case emit_skylight :
return 1.f ;
break ;
case emit_quakelight :
// X - r;
falloff = wl - > linear_attn - FastSqrt ( DotProduct ( delta , delta ) ) ;
if ( falloff < 0 )
return 0.f ;
return falloff ;
break ;
case emit_skyambient :
return 1.f ;
break ;
case emit_point :
case emit_spotlight : // directional & positional
{
float dist2 , dist ;
dist2 = DotProduct ( delta , delta ) ;
dist = FastSqrt ( dist2 ) ;
// Cull out stuff that's too far
if ( ! bNoRadiusCheck & & ( wl - > radius ! = 0 ) & & ( dist > wl - > radius ) )
return 0.f ;
return 1.f / ( wl - > constant_attn + wl - > linear_attn * dist + wl - > quadratic_attn * dist2 ) ;
}
break ;
default :
// Bug: need to return an error
break ;
}
return 1.f ;
}
/*
light_normal ( lights normal translated to same space as other normals )
surface_normal
light_direction_normal | ( light_pos - vertex_pos ) |
*/
float Engine_WorldLightAngle ( const dworldlight_t * wl , const Vector & lnormal , const Vector & snormal , const Vector & delta )
{
float dot , dot2 , ratio = 0 ;
switch ( wl - > type )
{
case emit_surface :
dot = DotProduct ( snormal , delta ) ;
if ( dot < 0 )
return 0 ;
dot2 = - DotProduct ( delta , lnormal ) ;
if ( dot2 < = ON_EPSILON / 10 )
return 0 ; // behind light surface
return dot * dot2 ;
case emit_point :
dot = DotProduct ( snormal , delta ) ;
if ( dot < 0 )
return 0 ;
return dot ;
case emit_spotlight :
// return 1.0; // !!!
dot = DotProduct ( snormal , delta ) ;
if ( dot < 0 )
return 0 ;
dot2 = - DotProduct ( delta , lnormal ) ;
if ( dot2 < = wl - > stopdot2 )
return 0 ; // outside light cone
ratio = dot ;
if ( dot2 > = wl - > stopdot )
return ratio ; // inside inner cone
if ( ( wl - > exponent = = 1 ) | | ( wl - > exponent = = 0 ) )
{
ratio * = ( dot2 - wl - > stopdot2 ) / ( wl - > stopdot - wl - > stopdot2 ) ;
}
else
{
ratio * = pow ( ( dot2 - wl - > stopdot2 ) / ( wl - > stopdot - wl - > stopdot2 ) , wl - > exponent ) ;
}
return ratio ;
case emit_skylight :
dot2 = - DotProduct ( snormal , lnormal ) ;
if ( dot2 < 0 )
return 0 ;
return dot2 ;
case emit_quakelight :
// linear falloff
dot = DotProduct ( snormal , delta ) ;
if ( dot < 0 )
return 0 ;
return dot ;
case emit_skyambient :
// not supported
return 1 ;
default :
// Bug: need to return an error
break ;
}
return 0 ;
}
//-----------------------------------------------------------------------------
// Allocator for color mesh vertex buffers (for use with static props only).
// It uses a trivial allocation scheme, which assumes that allocations and
// deallocations are not interleaved (you do all allocs, then all deallocs).
//-----------------------------------------------------------------------------
class CPooledVBAllocator_ColorMesh : public IPooledVBAllocator
{
public :
CPooledVBAllocator_ColorMesh ( ) ;
virtual ~ CPooledVBAllocator_ColorMesh ( ) ;
// Allocate the shared mesh (vertex buffer)
virtual bool Init ( VertexFormat_t format , int numVerts ) ;
// Free the shared mesh (after Deallocate is called for all sub-allocs)
virtual void Clear ( ) ;
// Get the shared mesh (vertex buffer) from which sub-allocations are made
virtual IMesh * GetSharedMesh ( ) { return m_pMesh ; }
// Get a pointer to the start of the vertex buffer data
virtual void * GetVertexBufferBase ( ) { return m_pVertexBufferBase ; }
virtual int GetNumVertsAllocated ( ) { return m_totalVerts ; }
// Allocate a sub-range of 'numVerts' from free space in the shared vertex buffer
// (returns the byte offset from the start of the VB to the new allocation)
virtual int Allocate ( int numVerts ) ;
// Deallocate an existing allocation
virtual void Deallocate ( int offset , int numVerts ) ;
private :
// Assert/warn that the allocator is in a clear/empty state (returns FALSE if not)
bool CheckIsClear ( void ) ;
IMesh * m_pMesh ; // The shared mesh (vertex buffer) from which sub-allocations are made
void * m_pVertexBufferBase ; // A pointer to the start of the vertex buffer data
int m_totalVerts ; // The number of verts in the shared vertex buffer
int m_vertexSize ; // The stride of the shared vertex buffer
int m_numAllocations ; // The number of extant allocations
int m_numVertsAllocated ; // The number of vertices in extant allocations
int m_nextFreeOffset ; // The offset to be returned by the next call to Allocate()
// (incremented as a simple stack)
bool m_bStartedDeallocation ; // This is set when Deallocate() is called for the first time,
// at which point Allocate() cannot be called again until all
// extant allocations have been deallocated.
} ;
struct colormeshparams_t
{
int m_nMeshes ;
int m_nTotalVertexes ;
// Given memory alignment (VBs must be 4-KB aligned on X360, for example), it can be more efficient
// to allocate many color meshes out of a single shared vertex buffer (using vertex 'stream offset')
IPooledVBAllocator * m_pPooledVBAllocator ;
int m_nVertexes [ 256 ] ;
FileNameHandle_t m_fnHandle ;
} ;
class CColorMeshData
{
public :
void DestroyResource ( )
{
g_pFileSystem - > AsyncFinish ( m_hAsyncControlVertex , true ) ;
g_pFileSystem - > AsyncRelease ( m_hAsyncControlVertex ) ;
g_pFileSystem - > AsyncFinish ( m_hAsyncControlTexel , true ) ;
g_pFileSystem - > AsyncRelease ( m_hAsyncControlTexel ) ;
// release the array of meshes
CMatRenderContextPtr pRenderContext ( materials ) ;
for ( int i = 0 ; i < m_nMeshes ; i + + )
{
if ( m_pMeshInfos [ i ] . m_pPooledVBAllocator )
{
// Let the pooling allocator dealloc this sub-range of the shared vertex buffer
m_pMeshInfos [ i ] . m_pPooledVBAllocator - > Deallocate ( m_pMeshInfos [ i ] . m_nVertOffsetInBytes , m_pMeshInfos [ i ] . m_nNumVerts ) ;
}
else
{
// Free this standalone mesh
pRenderContext - > DestroyStaticMesh ( m_pMeshInfos [ i ] . m_pMesh ) ;
}
if ( m_pMeshInfos [ i ] . m_pLightmap )
{
m_pMeshInfos [ i ] . m_pLightmap - > Release ( ) ;
m_pMeshInfos [ i ] . m_pLightmap = NULL ;
}
if ( m_pMeshInfos [ i ] . m_pLightmapData )
{
delete [ ] m_pMeshInfos [ i ] . m_pLightmapData - > m_pTexelData ;
delete m_pMeshInfos [ i ] . m_pLightmapData ;
}
}
delete [ ] m_pMeshInfos ;
delete [ ] m_ppTargets ;
delete this ;
}
CColorMeshData * GetData ( )
{
return this ;
}
unsigned int Size ( )
{
// TODO: This is wrong because we don't currently account for the size of the textures we create.
// However, that data isn't available until way after this query is made, so just live with
// this for now I guess?
return m_nTotalSize ;
}
static CColorMeshData * CreateResource ( const colormeshparams_t & params )
{
CColorMeshData * data = new CColorMeshData ;
data - > m_bHasInvalidVB = false ;
data - > m_bColorMeshValid = false ;
data - > m_bColorTextureValid = false ;
data - > m_bColorTextureCreated = false ;
data - > m_bNeedsRetry = false ;
data - > m_hAsyncControlVertex = NULL ;
data - > m_hAsyncControlTexel = NULL ;
data - > m_fnHandle = params . m_fnHandle ;
data - > m_nTotalSize = params . m_nMeshes * sizeof ( IMesh * ) + params . m_nTotalVertexes * 4 ;
data - > m_nMeshes = params . m_nMeshes ;
data - > m_pMeshInfos = new ColorMeshInfo_t [ params . m_nMeshes ] ;
Q_memset ( data - > m_pMeshInfos , 0 , params . m_nMeshes * sizeof ( ColorMeshInfo_t ) ) ;
data - > m_ppTargets = new unsigned char * [ params . m_nMeshes ] ;
CMeshBuilder meshBuilder ;
CMatRenderContextPtr pRenderContext ( materials ) ;
for ( int i = 0 ; i < params . m_nMeshes ; i + + )
{
VertexFormat_t vertexFormat = VERTEX_SPECULAR ;
data - > m_pMeshInfos [ i ] . m_pMesh = NULL ;
data - > m_pMeshInfos [ i ] . m_pPooledVBAllocator = params . m_pPooledVBAllocator ;
data - > m_pMeshInfos [ i ] . m_nVertOffsetInBytes = 0 ;
data - > m_pMeshInfos [ i ] . m_nNumVerts = params . m_nVertexes [ i ] ;
data - > m_pMeshInfos [ i ] . m_pLightmapData = NULL ;
data - > m_pMeshInfos [ i ] . m_pLightmap = NULL ;
if ( params . m_pPooledVBAllocator ! = NULL )
{
// Allocate a portion of a single, shared VB for each color mesh
data - > m_pMeshInfos [ i ] . m_nVertOffsetInBytes = params . m_pPooledVBAllocator - > Allocate ( params . m_nVertexes [ i ] ) ;
if ( data - > m_pMeshInfos [ i ] . m_nVertOffsetInBytes = = - 1 )
{
// Failed (fall back to regular allocations)
data - > m_pMeshInfos [ i ] . m_pPooledVBAllocator = NULL ;
data - > m_pMeshInfos [ i ] . m_nVertOffsetInBytes = 0 ;
}
else
{
// Set up the mesh+data pointers
data - > m_pMeshInfos [ i ] . m_pMesh = params . m_pPooledVBAllocator - > GetSharedMesh ( ) ;
data - > m_ppTargets [ i ] = ( ( unsigned char * ) params . m_pPooledVBAllocator - > GetVertexBufferBase ( ) ) + data - > m_pMeshInfos [ i ] . m_nVertOffsetInBytes ;
}
}
if ( data - > m_pMeshInfos [ i ] . m_pMesh = = NULL )
{
if ( g_VBAllocTracker )
g_VBAllocTracker - > TrackMeshAllocations ( " CColorMeshData::CreateResource " ) ;
// Allocate a standalone VB per color mesh
data - > m_pMeshInfos [ i ] . m_pMesh = pRenderContext - > CreateStaticMesh ( vertexFormat , TEXTURE_GROUP_STATIC_VERTEX_BUFFER_COLOR ) ;
if ( g_VBAllocTracker )
g_VBAllocTracker - > TrackMeshAllocations ( NULL ) ;
}
Assert ( data - > m_pMeshInfos [ i ] . m_pMesh ) ;
if ( ! data - > m_pMeshInfos [ i ] . m_pMesh )
{
data - > DestroyResource ( ) ;
data = NULL ;
break ;
}
}
return data ;
}
static unsigned int EstimatedSize ( const colormeshparams_t & params )
{
// each vertex is a 4 byte color
return params . m_nMeshes * sizeof ( IMesh * ) + params . m_nTotalVertexes * 4 ;
}
int m_nMeshes ;
ColorMeshInfo_t * m_pMeshInfos ;
unsigned char * * m_ppTargets ;
unsigned int m_nTotalSize ;
FSAsyncControl_t m_hAsyncControlVertex ;
FSAsyncControl_t m_hAsyncControlTexel ;
unsigned int m_bHasInvalidVB : 1 ;
unsigned int m_bColorMeshValid : 1 ;
unsigned int m_bColorTextureValid : 1 ; // Whether the texture data is valid, but not necessarily created
unsigned int m_bColorTextureCreated : 1 ; // Whether the texture data has actually been created.
unsigned int m_bNeedsRetry : 1 ;
FileNameHandle_t m_fnHandle ;
} ;
//-----------------------------------------------------------------------------
//
// Implementation of IVModelRender
//
//-----------------------------------------------------------------------------
// UNDONE: Move this to hud export code, subsume previous functions
class CModelRender : public IVModelRender ,
public CManagedDataCacheClient < CColorMeshData , colormeshparams_t >
{
public :
// members of the IVModelRender interface
virtual void ForcedMaterialOverride ( IMaterial * newMaterial , OverrideType_t nOverrideType = OVERRIDE_NORMAL ) ;
virtual int DrawModel (
int flags , IClientRenderable * cliententity ,
ModelInstanceHandle_t instance , int entity_index , const model_t * model ,
const Vector & origin , QAngle const & angles ,
int skin , int body , int hitboxset ,
const matrix3x4_t * pModelToWorld ,
const matrix3x4_t * pLightingOffset ) ;
virtual void SetViewTarget ( const CStudioHdr * pStudioHdr , int nBodyIndex , const Vector & target ) ;
// Creates, destroys instance data to be associated with the model
virtual ModelInstanceHandle_t CreateInstance ( IClientRenderable * pRenderable , LightCacheHandle_t * pHandle ) ;
virtual void SetStaticLighting ( ModelInstanceHandle_t handle , LightCacheHandle_t * pCache ) ;
virtual LightCacheHandle_t GetStaticLighting ( ModelInstanceHandle_t handle ) ;
virtual void DestroyInstance ( ModelInstanceHandle_t handle ) ;
virtual bool ChangeInstance ( ModelInstanceHandle_t handle , IClientRenderable * pRenderable ) ;
// Creates a decal on a model instance by doing a planar projection
// along the ray. The material is the decal material, the radius is the
// radius of the decal to create.
virtual void AddDecal ( ModelInstanceHandle_t handle , Ray_t const & ray ,
const Vector & decalUp , int decalIndex , int body , bool noPokethru = false , int maxLODToDecal = ADDDECAL_TO_ALL_LODS ) ;
virtual void AddColoredDecal ( ModelInstanceHandle_t handle , Ray_t const & ray ,
const Vector & decalUp , int decalIndex , int body , Color cColor , bool noPokethru = false , int maxLODToDecal = ADDDECAL_TO_ALL_LODS ) ;
virtual void GetMaterialOverride ( IMaterial * * ppOutForcedMaterial , OverrideType_t * pOutOverrideType ) ;
// Removes all the decals on a model instance
virtual void RemoveAllDecals ( ModelInstanceHandle_t handle ) ;
// Remove all decals from all models
virtual void RemoveAllDecalsFromAllModels ( ) ;
// Shadow rendering (render-to-texture)
virtual matrix3x4_t * DrawModelShadowSetup ( IClientRenderable * pRenderable , int body , int skin , DrawModelInfo_t * pInfo , matrix3x4_t * pBoneToWorld ) ;
virtual void DrawModelShadow ( IClientRenderable * pRenderable , const DrawModelInfo_t & info , matrix3x4_t * pBoneToWorld ) ;
// Used to allow the shadow mgr to manage a list of shadows per model
unsigned short & FirstShadowOnModelInstance ( ModelInstanceHandle_t handle ) { return m_ModelInstances [ handle ] . m_FirstShadow ; }
// This gets called when overbright, etc gets changed to recompute static prop lighting.
virtual bool RecomputeStaticLighting ( ModelInstanceHandle_t handle ) ;
// Handlers for alt-tab
virtual void ReleaseAllStaticPropColorData ( void ) ;
virtual void RestoreAllStaticPropColorData ( void ) ;
// Extended version of drawmodel
virtual bool DrawModelSetup ( ModelRenderInfo_t & pInfo , DrawModelState_t * pState , matrix3x4_t * pBoneToWorld , matrix3x4_t * * ppBoneToWorldOut ) ;
virtual int DrawModelEx ( ModelRenderInfo_t & pInfo ) ;
virtual int DrawModelExStaticProp ( ModelRenderInfo_t & pInfo ) ;
virtual int DrawStaticPropArrayFast ( StaticPropRenderInfo_t * pProps , int count , bool bShadowDepth ) ;
// Sets up lighting context for a point in space
virtual void SetupLighting ( const Vector & vecCenter ) ;
virtual void SuppressEngineLighting ( bool bSuppress ) ;
2022-02-23 19:50:30 +08:00
inline vertexFileHeader_t * CacheVertexData ( ) { return g_pMDLCache - > GetVertexData ( VoidPtrToMDLHandle ( m_pStudioHdr - > VirtualModel ( ) ) ) ; }
2020-04-23 00:56:21 +08:00
bool Init ( ) ;
void Shutdown ( ) ;
bool GetItemName ( DataCacheClientID_t clientId , const void * pItem , char * pDest , unsigned nMaxLen ) ;
struct staticPropAsyncContext_t
{
DataCacheHandle_t m_ColorMeshHandle ;
CColorMeshData * m_pColorMeshData ;
int m_nMeshes ;
unsigned int m_nRootLOD ;
char m_szFilenameVertex [ MAX_PATH ] ;
char m_szFilenameTexel [ MAX_PATH ] ;
} ;
void StaticPropColorMeshCallback ( void * pContext , const void * pData , int numReadBytes , FSAsyncStatus_t asyncStatus ) ;
void StaticPropColorTexelCallback ( void * pContext , const void * pData , int numReadBytes , FSAsyncStatus_t asyncStatus ) ;
// 360 holds onto static prop color meshes during same map transitions
void PurgeCachedStaticPropColorData ( ) ;
bool IsStaticPropColorDataCached ( const char * pName ) ;
DataCacheHandle_t GetCachedStaticPropColorData ( const char * pName ) ;
virtual void SetupColorMeshes ( int nTotalVerts ) ;
private :
enum
{
CURRENT_LIGHTING_UNINITIALIZED = - 999999
} ;
enum ModelInstanceFlags_t
{
MODEL_INSTANCE_HAS_STATIC_LIGHTING = 0x1 ,
MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR = 0x2 ,
MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD = 0x4 ,
MODEL_INSTANCE_HAS_COLOR_DATA = 0x8
} ;
struct ModelInstance_t
{
IClientRenderable * m_pRenderable ;
// Need to store off the model. When it changes, we lose all instance data..
model_t * m_pModel ;
StudioDecalHandle_t m_DecalHandle ;
// Stores off the current lighting state
LightingState_t m_CurrentLightingState ;
LightingState_t m_AmbientLightingState ;
Vector m_flLightIntensity [ MAXLOCALLIGHTS ] ;
float m_flLightingTime ;
// First shadow projected onto the model
unsigned short m_FirstShadow ;
unsigned short m_nFlags ;
// Static lighting
LightCacheHandle_t m_LightCacheHandle ;
// Color mesh managed by cache
DataCacheHandle_t m_ColorMeshHandle ;
} ;
// Sets up the render state for a model
matrix3x4_t * SetupModelState ( IClientRenderable * pRenderable ) ;
int ComputeLOD ( const ModelRenderInfo_t & info , studiohwdata_t * pStudioHWData ) ;
void DrawModelExecute ( const DrawModelState_t & state , const ModelRenderInfo_t & pInfo , matrix3x4_t * pCustomBoneToWorld = NULL ) ;
void InitColormeshParams ( ModelInstance_t & instance , studiohwdata_t * pStudioHWData , colormeshparams_t * pColorMeshParams ) ;
CColorMeshData * FindOrCreateStaticPropColorData ( ModelInstanceHandle_t handle ) ;
void DestroyStaticPropColorData ( ModelInstanceHandle_t handle ) ;
bool UpdateStaticPropColorData ( IHandleEntity * pEnt , ModelInstanceHandle_t handle ) ;
void ProtectColorDataIfQueued ( DataCacheHandle_t ) ;
void ValidateStaticPropColorData ( ModelInstanceHandle_t handle ) ;
bool LoadStaticPropColorData ( IHandleEntity * pProp , DataCacheHandle_t colorMeshHandle , studiohwdata_t * pStudioHWData ) ;
// Returns true if the model instance is valid
bool IsModelInstanceValid ( ModelInstanceHandle_t handle ) ;
void DebugDrawLightingOrigin ( const DrawModelState_t & state , const ModelRenderInfo_t & pInfo ) ;
LightingState_t * TimeAverageLightingState ( ModelInstanceHandle_t handle ,
LightingState_t * pLightingState , int nEntIndex , const Vector * pLightingOrigin ) ;
// Cause the current lighting state to match the given one
void SnapCurrentLightingState ( ModelInstance_t & inst , LightingState_t * pLightingState ) ;
// Sets up lighting state for rendering
void StudioSetupLighting ( const DrawModelState_t & state , const Vector & absEntCenter ,
LightCacheHandle_t * pLightcache , bool bVertexLit , bool bNeedsEnvCubemap , bool & bStaticLighting ,
DrawModelInfo_t & drawInfo , const ModelRenderInfo_t & pInfo , int drawFlags ) ;
// Time average the ambient term
void TimeAverageAmbientLight ( LightingState_t & actualLightingState , ModelInstance_t & inst ,
float flAttenFactor , LightingState_t * pLightingState , const Vector * pLightingOrigin ) ;
// Old-style computation of vertex lighting
void ComputeModelVertexLightingOld ( mstudiomodel_t * pModel ,
matrix3x4_t & matrix , const LightingState_t & lightingState , color24 * pLighting ,
bool bUseConstDirLighting , float flConstDirLightAmount ) ;
// New-style computation of vertex lighting
void ComputeModelVertexLighting ( IHandleEntity * pProp ,
mstudiomodel_t * pModel , OptimizedModel : : ModelLODHeader_t * pVtxLOD ,
matrix3x4_t & matrix , Vector4D * pTempMem , color24 * pLighting ) ;
// Internal Decal
void AddDecalInternal ( ModelInstanceHandle_t handle , Ray_t const & ray , const Vector & decalUp , int decalIndex , int body , bool bUseColor , Color cColor , bool noPokeThru , int maxLODToDecal ) ;
// Model instance data
CUtlLinkedList < ModelInstance_t , ModelInstanceHandle_t > m_ModelInstances ;
// current active model
studiohdr_t * m_pStudioHdr ;
bool m_bSuppressEngineLighting ;
CUtlDict < DataCacheHandle_t , int > m_CachedStaticPropColorData ;
CThreadFastMutex m_CachedStaticPropMutex ;
// Allocator for static prop color mesh vertex buffers (all are pooled into one VB)
CPooledVBAllocator_ColorMesh m_colorMeshVBAllocator ;
} ;
static CModelRender s_ModelRender ;
EXPOSE_SINGLE_INTERFACE_GLOBALVAR ( CModelRender , IVModelRender , VENGINE_HUDMODEL_INTERFACE_VERSION , s_ModelRender ) ;
IVModelRender * modelrender = & s_ModelRender ;
//-----------------------------------------------------------------------------
// Resource loading for static prop lighting
//-----------------------------------------------------------------------------
class CResourcePreloadPropLighting : public CResourcePreload
{
virtual bool CreateResource ( const char * pName )
{
if ( ! r_proplightingfromdisk . GetBool ( ) )
{
// do nothing, not an error
return true ;
}
char szBasename [ MAX_PATH ] ;
char szFilename [ MAX_PATH ] ;
V_FileBase ( pName , szBasename , sizeof ( szBasename ) ) ;
V_snprintf ( szFilename , sizeof ( szFilename ) , " %s%s.vhv " , szBasename , GetPlatformExt ( ) ) ;
// static props have the same name across maps
// can check if loading the same map and early out if data present
if ( g_pQueuedLoader - > IsSameMapLoading ( ) & & s_ModelRender . IsStaticPropColorDataCached ( szFilename ) )
{
// same map is loading, all disk prop lighting was left in the cache
// otherwise the pre-purge operation below will do the cleanup
return true ;
}
// create an anonymous job to get the lighting data in memory, claim during static prop instancing
LoaderJob_t loaderJob ;
loaderJob . m_pFilename = szFilename ;
loaderJob . m_pPathID = " GAME " ;
loaderJob . m_Priority = LOADERPRIORITY_DURINGPRELOAD ;
g_pQueuedLoader - > AddJob ( & loaderJob ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Pre purge operation before i/o commences
//-----------------------------------------------------------------------------
virtual void PurgeUnreferencedResources ( )
{
if ( g_pQueuedLoader - > IsSameMapLoading ( ) )
{
// do nothing, same map is loading, correct disk prop lighting will still be in data cache
return ;
}
// Map is different, need to purge any existing disk prop lighting
// before anonymous i/o commences, otherwise 2x memory usage
s_ModelRender . PurgeCachedStaticPropColorData ( ) ;
}
virtual void PurgeAll ( )
{
s_ModelRender . PurgeCachedStaticPropColorData ( ) ;
}
} ;
static CResourcePreloadPropLighting s_ResourcePreloadPropLighting ;
//-----------------------------------------------------------------------------
// Init, shutdown studiorender
//-----------------------------------------------------------------------------
void InitStudioRender ( void )
{
UpdateStudioRenderConfig ( ) ;
s_ModelRender . Init ( ) ;
}
void ShutdownStudioRender ( void )
{
s_ModelRender . Shutdown ( ) ;
}
//-----------------------------------------------------------------------------
// Hook needed for shadows to work
//-----------------------------------------------------------------------------
unsigned short & FirstShadowOnModelInstance ( ModelInstanceHandle_t handle )
{
return s_ModelRender . FirstShadowOnModelInstance ( handle ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void R_RemoveAllDecalsFromAllModels ( )
{
s_ModelRender . RemoveAllDecalsFromAllModels ( ) ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CModelRender : : Init ( )
{
// start a managed section in the cache
CCacheClientBaseClass : : Init ( g_pDataCache , " ColorMesh " ) ;
if ( IsX360 ( ) )
{
g_pQueuedLoader - > InstallLoader ( RESOURCEPRELOAD_STATICPROPLIGHTING , & s_ResourcePreloadPropLighting ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void CModelRender : : Shutdown ( )
{
// end the managed section
CCacheClientBaseClass : : Shutdown ( ) ;
}
//-----------------------------------------------------------------------------
// Used by the client to allow it to set lighting state instead of this code
//-----------------------------------------------------------------------------
void CModelRender : : SuppressEngineLighting ( bool bSuppress )
{
m_bSuppressEngineLighting = bSuppress ;
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
bool CModelRender : : GetItemName ( DataCacheClientID_t clientId , const void * pItem , char * pDest , unsigned nMaxLen )
{
CColorMeshData * pColorMeshData = ( CColorMeshData * ) pItem ;
g_pFileSystem - > String ( pColorMeshData - > m_fnHandle , pDest , nMaxLen ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Cause the current lighting state to match the given one
//-----------------------------------------------------------------------------
void CModelRender : : SnapCurrentLightingState ( ModelInstance_t & inst , LightingState_t * pLightingState )
{
inst . m_CurrentLightingState = * pLightingState ;
for ( int i = 0 ; i < MAXLOCALLIGHTS ; + + i )
{
if ( i < pLightingState - > numlights )
{
inst . m_flLightIntensity [ i ] = pLightingState - > locallight [ i ] - > intensity ;
}
else
{
inst . m_flLightIntensity [ i ] . Init ( 0.0f , 0.0f , 0.0f ) ;
}
}
# ifndef SWDS
inst . m_flLightingTime = cl . GetTime ( ) ;
# endif
}
# define AMBIENT_MAX 8.0f
//-----------------------------------------------------------------------------
// Time average the ambient term
//-----------------------------------------------------------------------------
void CModelRender : : TimeAverageAmbientLight ( LightingState_t & actualLightingState ,
ModelInstance_t & inst , float flAttenFactor , LightingState_t * pLightingState , const Vector * pLightingOrigin )
{
flAttenFactor = clamp ( flAttenFactor , 0.f , 1.f ) ; // don't need this but alex is a coward
Vector vecDelta ;
for ( int i = 0 ; i < 6 ; + + i )
{
VectorSubtract ( pLightingState - > r_boxcolor [ i ] , inst . m_CurrentLightingState . r_boxcolor [ i ] , vecDelta ) ;
vecDelta * = flAttenFactor ;
inst . m_CurrentLightingState . r_boxcolor [ i ] = pLightingState - > r_boxcolor [ i ] - vecDelta ;
# if defined( VISUALIZE_TIME_AVERAGE ) && !defined( SWDS )
if ( pLightingOrigin )
{
Vector vecDir = vec3_origin ;
vecDir [ i > > 1 ] = ( i & 0x1 ) ? - 1.0f : 1.0f ;
CDebugOverlay : : AddLineOverlay ( * pLightingOrigin , * pLightingOrigin + vecDir * 20 ,
255 * inst . m_CurrentLightingState . r_boxcolor [ i ] . x ,
255 * inst . m_CurrentLightingState . r_boxcolor [ i ] . y ,
255 * inst . m_CurrentLightingState . r_boxcolor [ i ] . z , 255 , false , 5.0f ) ;
CDebugOverlay : : AddLineOverlay ( * pLightingOrigin + Vector ( 5 , 5 , 5 ) , * pLightingOrigin + vecDir * 50 ,
255 * pLightingState - > r_boxcolor [ i ] . x ,
255 * pLightingState - > r_boxcolor [ i ] . y ,
255 * pLightingState - > r_boxcolor [ i ] . z , 255 , true , 5.0f ) ;
}
# endif
// haven't been able to find this rare bug which results in ambient light getting "stuck"
// on the viewmodel extremely rarely , presumably with infinities. So, mask the bug
// (hopefully) and warn by clamping.
# ifndef NDEBUG
Assert ( inst . m_CurrentLightingState . r_boxcolor [ i ] . IsValid ( ) ) ;
for ( int nComp = 0 ; nComp < 3 ; nComp + + )
{
Assert ( inst . m_CurrentLightingState . r_boxcolor [ i ] [ nComp ] > = 0.0 ) ;
Assert ( inst . m_CurrentLightingState . r_boxcolor [ i ] [ nComp ] < = AMBIENT_MAX ) ;
}
# endif
inst . m_CurrentLightingState . r_boxcolor [ i ] . x = clamp ( inst . m_CurrentLightingState . r_boxcolor [ i ] . x , 0.f , AMBIENT_MAX ) ;
inst . m_CurrentLightingState . r_boxcolor [ i ] . y = clamp ( inst . m_CurrentLightingState . r_boxcolor [ i ] . y , 0.f , AMBIENT_MAX ) ;
inst . m_CurrentLightingState . r_boxcolor [ i ] . z = clamp ( inst . m_CurrentLightingState . r_boxcolor [ i ] . z , 0.f , AMBIENT_MAX ) ;
}
memcpy ( & actualLightingState . r_boxcolor , & inst . m_CurrentLightingState . r_boxcolor , sizeof ( inst . m_CurrentLightingState . r_boxcolor ) ) ;
}
//-----------------------------------------------------------------------------
// Do time averaging of the lighting state to avoid popping...
//-----------------------------------------------------------------------------
LightingState_t * CModelRender : : TimeAverageLightingState ( ModelInstanceHandle_t handle , LightingState_t * pLightingState , int nEntIndex , const Vector * pLightingOrigin )
{
if ( r_lightaverage . GetInt ( ) = = 0 )
return pLightingState ;
# ifndef SWDS
float flInterpFactor = r_lightinterp . GetFloat ( ) ;
if ( flInterpFactor = = 0 )
return pLightingState ;
if ( handle = = MODEL_INSTANCE_INVALID )
return pLightingState ;
ModelInstance_t & inst = m_ModelInstances [ handle ] ;
if ( inst . m_flLightingTime = = CURRENT_LIGHTING_UNINITIALIZED )
{
SnapCurrentLightingState ( inst , pLightingState ) ;
return pLightingState ;
}
float dt = ( cl . GetTime ( ) - inst . m_flLightingTime ) ;
if ( dt < = 0.0f )
{
dt = 0.0f ;
}
else
{
inst . m_flLightingTime = cl . GetTime ( ) ;
}
static LightingState_t actualLightingState ;
static dworldlight_t s_WorldLights [ MAXLOCALLIGHTS ] ;
// I'm creating the equation v = vf - (vf-vi)e^-at
// where vf = this frame's lighting value, vi = current time averaged lighting value
int i ;
Vector vecDelta ;
float flAttenFactor = exp ( - flInterpFactor * dt ) ;
TimeAverageAmbientLight ( actualLightingState , inst , flAttenFactor , pLightingState , pLightingOrigin ) ;
// Max # of lights...
int nWorldLights ;
if ( ! g_pMaterialSystemConfig - > bSoftwareLighting )
{
nWorldLights = min ( g_pMaterialSystemHardwareConfig - > MaxNumLights ( ) , r_worldlights . GetInt ( ) ) ;
}
else
{
nWorldLights = r_worldlights . GetInt ( ) ;
}
// Create a mapping of identical lights
int nMatchCount = 0 ;
bool pMatch [ MAXLOCALLIGHTS ] ;
Vector pLight [ MAXLOCALLIGHTS ] ;
dworldlight_t * pSourceLight [ MAXLOCALLIGHTS ] ;
memset ( pMatch , 0 , sizeof ( pMatch ) ) ;
for ( i = 0 ; i < pLightingState - > numlights ; + + i )
{
// By default, assume the light doesn't match an existing light, so blend up from 0
pLight [ i ] . Init ( 0.0f , 0.0f , 0.0f ) ;
int j ;
for ( j = 0 ; j < inst . m_CurrentLightingState . numlights ; + + j )
{
if ( pLightingState - > locallight [ i ] = = inst . m_CurrentLightingState . locallight [ j ] )
{
// Ok, we found a matching light, so use the intensity of that light at the moment
+ + nMatchCount ;
pMatch [ j ] = true ;
pLight [ i ] = inst . m_flLightIntensity [ j ] ;
break ;
}
}
}
// For the lights in the current lighting state, attenuate them toward their actual value
for ( i = 0 ; i < pLightingState - > numlights ; + + i )
{
actualLightingState . locallight [ i ] = & s_WorldLights [ i ] ;
memcpy ( & s_WorldLights [ i ] , pLightingState - > locallight [ i ] , sizeof ( dworldlight_t ) ) ;
// Light already exists? Attenuate to it...
VectorSubtract ( pLightingState - > locallight [ i ] - > intensity , pLight [ i ] , vecDelta ) ;
vecDelta * = flAttenFactor ;
s_WorldLights [ i ] . intensity = pLightingState - > locallight [ i ] - > intensity - vecDelta ;
pSourceLight [ i ] = pLightingState - > locallight [ i ] ;
}
// Ramp down any light we can; we may not be able to ramp them all down
int nCurrLight = pLightingState - > numlights ;
for ( i = 0 ; i < inst . m_CurrentLightingState . numlights ; + + i )
{
if ( pMatch [ i ] )
continue ;
// Has it faded out to black? Then remove it.
if ( inst . m_flLightIntensity [ i ] . LengthSqr ( ) < 1 )
continue ;
if ( nCurrLight > = MAXLOCALLIGHTS )
break ;
actualLightingState . locallight [ nCurrLight ] = & s_WorldLights [ nCurrLight ] ;
memcpy ( & s_WorldLights [ nCurrLight ] , inst . m_CurrentLightingState . locallight [ i ] , sizeof ( dworldlight_t ) ) ;
// Attenuate to black (fade out)
VectorMultiply ( inst . m_flLightIntensity [ i ] , flAttenFactor , vecDelta ) ;
s_WorldLights [ nCurrLight ] . intensity = vecDelta ;
pSourceLight [ nCurrLight ] = inst . m_CurrentLightingState . locallight [ i ] ;
if ( ( nCurrLight > = nWorldLights ) & & pLightingOrigin )
{
AddWorldLightToAmbientCube ( & s_WorldLights [ nCurrLight ] , * pLightingOrigin , actualLightingState . r_boxcolor ) ;
}
+ + nCurrLight ;
}
actualLightingState . numlights = min ( nCurrLight , nWorldLights ) ;
inst . m_CurrentLightingState . numlights = nCurrLight ;
for ( i = 0 ; i < nCurrLight ; + + i )
{
inst . m_CurrentLightingState . locallight [ i ] = pSourceLight [ i ] ;
inst . m_flLightIntensity [ i ] = s_WorldLights [ i ] . intensity ;
# if defined( VISUALIZE_TIME_AVERAGE ) && !defined( SWDS )
Vector vecColor = pSourceLight [ i ] - > intensity ;
float flMax = max ( vecColor . x , vecColor . y ) ;
flMax = max ( flMax , vecColor . z ) ;
if ( flMax = = 0.0f )
{
flMax = 1.0f ;
}
vecColor * = 255.0f / flMax ;
float flRatio = inst . m_flLightIntensity [ i ] . Length ( ) / pSourceLight [ i ] - > intensity . Length ( ) ;
vecColor * = flRatio ;
CDebugOverlay : : AddLineOverlay ( * pLightingOrigin , pSourceLight [ i ] - > origin ,
vecColor . x , vecColor . y , vecColor . z , 255 , false , 5.0f ) ;
# endif
}
return & actualLightingState ;
# else
return pLightingState ;
# endif
}
// Ambient boost settings
static ConVar r_ambientboost ( " r_ambientboost " , " 1 " , FCVAR_ARCHIVE , " Set to boost ambient term if it is totally swamped by local lights " ) ;
static ConVar r_ambientmin ( " r_ambientmin " , " 0.3 " , FCVAR_ARCHIVE , " Threshold above which ambient cube will not boost (i.e. it's already sufficiently bright " ) ;
static ConVar r_ambientfraction ( " r_ambientfraction " , " 0.1 " , FCVAR_CHEAT , " Fraction of direct lighting that ambient cube must be below to trigger boosting " ) ;
static ConVar r_ambientfactor ( " r_ambientfactor " , " 5 " , FCVAR_ARCHIVE , " Boost ambient cube by no more than this factor " ) ;
static ConVar r_lightcachemodel ( " r_lightcachemodel " , " -1 " , FCVAR_CHEAT , " " ) ;
static ConVar r_drawlightcache ( " r_drawlightcache " , " 0 " , FCVAR_CHEAT , " 0: off \n 1: draw light cache entries \n 2: draw rays \n " ) ;
//-----------------------------------------------------------------------------
// Sets up lighting state for rendering
//-----------------------------------------------------------------------------
void CModelRender : : StudioSetupLighting ( const DrawModelState_t & state , const Vector & absEntCenter ,
LightCacheHandle_t * pLightcache , bool bVertexLit , bool bNeedsEnvCubemap , bool & bStaticLighting ,
DrawModelInfo_t & drawInfo , const ModelRenderInfo_t & pInfo , int drawFlags )
{
if ( m_bSuppressEngineLighting )
return ;
# ifndef SWDS
ITexture * pEnvCubemapTexture = NULL ;
LightingState_t lightingState ;
Vector pSaveLightPos [ MAXLOCALLIGHTS ] ;
Vector * pDebugLightingOrigin = NULL ;
Vector vecDebugLightingOrigin = vec3_origin ;
// Cache off lighting data for rendering decals - only on dx8/dx9.
LightingState_t lightingDecalState ;
drawInfo . m_bStaticLighting = bStaticLighting & & g_pMaterialSystemHardwareConfig - > SupportsStaticPlusDynamicLighting ( ) ;
drawInfo . m_nLocalLightCount = 0 ;
// Compute lighting origin from input
Vector vLightingOrigin ( 0.0f , 0.0f , 0.0f ) ;
CMatRenderContextPtr pRenderContext ( materials ) ;
if ( pInfo . pLightingOrigin )
{
vLightingOrigin = * pInfo . pLightingOrigin ;
}
else
{
vLightingOrigin = absEntCenter ;
if ( pInfo . pLightingOffset )
{
VectorTransform ( absEntCenter , * pInfo . pLightingOffset , vLightingOrigin ) ;
}
}
// Set the lighting origin state
pRenderContext - > SetLightingOrigin ( vLightingOrigin ) ;
ModelInstance_t * pModelInst = NULL ;
bool bHasDecals = false ;
if ( pInfo . instance ! = m_ModelInstances . InvalidIndex ( ) )
{
pModelInst = & m_ModelInstances [ pInfo . instance ] ;
if ( pModelInst )
{
bHasDecals = ( pModelInst - > m_DecalHandle ! = STUDIORENDER_DECAL_INVALID ) ;
}
}
if ( pLightcache )
{
// static prop case.
if ( bStaticLighting )
{
if ( g_pMaterialSystemHardwareConfig - > SupportsStaticPlusDynamicLighting ( ) )
{
LightingState_t * pLightingState = NULL ;
// dx8 and dx9 case. . .hardware can do baked lighting plus other dynamic lighting
// We already have the static part baked into a color mesh, so just get the dynamic stuff.
if ( StaticLightCacheAffectedByDynamicLight ( * pLightcache ) )
{
pLightingState = LightcacheGetStatic ( * pLightcache , & pEnvCubemapTexture ) ;
Assert ( lightingState . numlights > = 0 & & lightingState . numlights < = MAXLOCALLIGHTS ) ;
}
else
{
pLightingState = LightcacheGetStatic ( * pLightcache , & pEnvCubemapTexture , LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE ) ;
Assert ( lightingState . numlights > = 0 & & lightingState . numlights < = MAXLOCALLIGHTS ) ;
}
lightingState = * pLightingState ;
}
else
{
// dx6 and dx7 case. . .hardware can either do software lighting or baked lighting only.
if ( StaticLightCacheAffectedByDynamicLight ( * pLightcache ) | |
StaticLightCacheAffectedByAnimatedLightStyle ( * pLightcache ) )
{
bStaticLighting = false ;
}
else if ( StaticLightCacheNeedsSwitchableLightUpdate ( * pLightcache ) )
{
// Need to rebake lighting since a switch has turned off/on.
UpdateStaticPropColorData ( state . m_pRenderable - > GetIClientUnknown ( ) , pInfo . instance ) ;
}
}
}
if ( ! bStaticLighting )
{
lightingState = * ( LightcacheGetStatic ( * pLightcache , & pEnvCubemapTexture ) ) ;
Assert ( lightingState . numlights > = 0 & & lightingState . numlights < = MAXLOCALLIGHTS ) ;
}
if ( r_decalstaticprops . GetBool ( ) & & pModelInst & & drawInfo . m_bStaticLighting & & bHasDecals )
{
for ( int iCube = 0 ; iCube < 6 ; + + iCube )
{
drawInfo . m_vecAmbientCube [ iCube ] = pModelInst - > m_AmbientLightingState . r_boxcolor [ iCube ] + lightingState . r_boxcolor [ iCube ] ;
}
lightingDecalState . CopyLocalLights ( pModelInst - > m_AmbientLightingState ) ;
lightingDecalState . AddAllLocalLights ( lightingState ) ;
Assert ( lightingDecalState . numlights > = 0 & & lightingDecalState . numlights < = MAXLOCALLIGHTS ) ;
}
}
else // !pLightcache
{
vecDebugLightingOrigin = vLightingOrigin ;
pDebugLightingOrigin = & vecDebugLightingOrigin ;
// If we don't have a lightcache entry, but we have bStaticLighting, that means
// that we are a prop_physics that has fallen asleep.
if ( bStaticLighting )
{
LightcacheGetDynamic_Stats stats ;
pEnvCubemapTexture = LightcacheGetDynamic ( vLightingOrigin , lightingState ,
stats , LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE ) ;
Assert ( lightingState . numlights > = 0 & & lightingState . numlights < = MAXLOCALLIGHTS ) ;
// Deal with all the dx6/dx7 issues (ie. can't do anything besides either baked lighting
// or software dynamic lighting.
if ( ! g_pMaterialSystemHardwareConfig - > SupportsStaticPlusDynamicLighting ( ) )
{
if ( ( stats . m_bHasDLights | | stats . m_bHasNonSwitchableLightStyles ) )
{
// We either have a light switch, or a dynamic light. . do it in software.
// We'll reget the cache entry with different flags below.
bStaticLighting = false ;
}
else if ( stats . m_bNeedsSwitchableLightStyleUpdate )
{
// Need to rebake lighting since a switch has turned off/on.
UpdateStaticPropColorData ( state . m_pRenderable - > GetIClientUnknown ( ) , pInfo . instance ) ;
}
}
}
if ( ! bStaticLighting )
{
LightcacheGetDynamic_Stats stats ;
// For special r_drawlightcache mode, we only draw models containing the substring set in r_lightcachemodel
bool bDebugModel = false ;
if ( r_drawlightcache . GetInt ( ) = = 5 )
{
if ( pModelInst & & pModelInst - > m_pModel & & ! pModelInst - > m_pModel - > strName . IsEmpty ( ) )
{
const char * szModelName = r_lightcachemodel . GetString ( ) ;
bDebugModel = V_stristr ( pModelInst - > m_pModel - > strName , szModelName ) ! = NULL ;
}
}
pEnvCubemapTexture = LightcacheGetDynamic ( vLightingOrigin , lightingState , stats ,
LIGHTCACHEFLAGS_STATIC | LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE | LIGHTCACHEFLAGS_ALLOWFAST , bDebugModel ) ;
Assert ( lightingState . numlights > = 0 & & lightingState . numlights < = MAXLOCALLIGHTS ) ;
}
if ( pInfo . pLightingOffset & & ! pInfo . pLightingOrigin )
{
for ( int i = 0 ; i < lightingState . numlights ; + + i )
{
pSaveLightPos [ i ] = lightingState . locallight [ i ] - > origin ;
VectorITransform ( pSaveLightPos [ i ] , * pInfo . pLightingOffset , lightingState . locallight [ i ] - > origin ) ;
}
}
// Cache lighting for decals.
if ( pModelInst & & drawInfo . m_bStaticLighting & & bHasDecals )
{
// Only do this on dx8/dx9.
LightcacheGetDynamic_Stats stats ;
LightcacheGetDynamic ( vLightingOrigin , lightingDecalState , stats ,
LIGHTCACHEFLAGS_STATIC | LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE | LIGHTCACHEFLAGS_ALLOWFAST ) ;
Assert ( lightingDecalState . numlights > = 0 & & lightingDecalState . numlights < = MAXLOCALLIGHTS ) ;
for ( int iCube = 0 ; iCube < 6 ; + + iCube )
{
VectorCopy ( lightingDecalState . r_boxcolor [ iCube ] , drawInfo . m_vecAmbientCube [ iCube ] ) ;
}
if ( pInfo . pLightingOffset & & ! pInfo . pLightingOrigin )
{
for ( int i = 0 ; i < lightingDecalState . numlights ; + + i )
{
pSaveLightPos [ i ] = lightingDecalState . locallight [ i ] - > origin ;
VectorITransform ( pSaveLightPos [ i ] , * pInfo . pLightingOffset , lightingDecalState . locallight [ i ] - > origin ) ;
}
}
}
}
Assert ( lightingState . numlights > = 0 & & lightingState . numlights < = MAXLOCALLIGHTS ) ;
// Do time averaging of the lighting state to avoid popping...
LightingState_t * pState ;
if ( ! bStaticLighting & & ! pLightcache )
{
pState = TimeAverageLightingState ( pInfo . instance , & lightingState , pInfo . entity_index , pDebugLightingOrigin ) ;
}
else
{
pState = & lightingState ;
}
if ( bNeedsEnvCubemap & & pEnvCubemapTexture )
{
pRenderContext - > BindLocalCubemap ( pEnvCubemapTexture ) ;
}
if ( g_pMaterialSystemConfig - > nFullbright = = 1 )
{
pRenderContext - > SetAmbientLight ( 1.0 , 1.0 , 1.0 ) ;
static Vector white [ 6 ] =
{
Vector ( 1.0 , 1.0 , 1.0 ) ,
Vector ( 1.0 , 1.0 , 1.0 ) ,
Vector ( 1.0 , 1.0 , 1.0 ) ,
Vector ( 1.0 , 1.0 , 1.0 ) ,
Vector ( 1.0 , 1.0 , 1.0 ) ,
Vector ( 1.0 , 1.0 , 1.0 ) ,
} ;
g_pStudioRender - > SetAmbientLightColors ( white ) ;
// Disable all the lights..
pRenderContext - > DisableAllLocalLights ( ) ;
}
else if ( bVertexLit )
{
if ( drawFlags & STUDIORENDER_DRAW_ITEM_BLINK )
{
float add = r_itemblinkmax . GetFloat ( ) * ( FastCos ( r_itemblinkrate . GetFloat ( ) * Sys_FloatTime ( ) ) + 1.0f ) ;
Vector additiveColor ( add , add , add ) ;
static Vector temp [ 6 ] ;
int i ;
for ( i = 0 ; i < 6 ; i + + )
{
temp [ i ] = pState - > r_boxcolor [ i ] + additiveColor ;
}
g_pStudioRender - > SetAmbientLightColors ( temp ) ;
}
else
{
// If we have any lights and want to do ambient boost on this model
if ( ( pState - > numlights > 0 ) & & ( pInfo . pModel - > flags & MODELFLAG_STUDIOHDR_AMBIENT_BOOST ) & & r_ambientboost . GetBool ( ) )
{
Vector lumCoeff ( 0.3f , 0.59f , 0.11f ) ;
float avgCubeLuminance = 0.0f ;
float minCubeLuminance = FLT_MAX ;
float maxCubeLuminance = 0.0f ;
// Compute average luminance of ambient cube
for ( int i = 0 ; i < 6 ; i + + )
{
float luminance = DotProduct ( pState - > r_boxcolor [ i ] , lumCoeff ) ; // compute luminance
minCubeLuminance = fpmin ( minCubeLuminance , luminance ) ; // min luminance
maxCubeLuminance = fpmax ( maxCubeLuminance , luminance ) ; // max luminance
avgCubeLuminance + = luminance ; // accumulate luminance
}
avgCubeLuminance / = 6.0f ; // average luminance
// Compute the amount of direct light reaching the center of the model (attenuated by distance)
float fDirectLight = 0.0f ;
for ( int i = 0 ; i < pState - > numlights ; i + + )
{
Vector vLight = pState - > locallight [ i ] - > origin - vLightingOrigin ;
float d2 = DotProduct ( vLight , vLight ) ;
float d = sqrtf ( d2 ) ;
float fAtten = 1.0f ;
float denom = pState - > locallight [ i ] - > constant_attn +
pState - > locallight [ i ] - > linear_attn * d +
pState - > locallight [ i ] - > quadratic_attn * d2 ;
if ( denom > 0.00001f )
{
fAtten = 1.0f / denom ;
}
Vector vLit = pState - > locallight [ i ] - > intensity * fAtten ;
fDirectLight + = DotProduct ( vLit , lumCoeff ) ;
}
// If ambient cube is sufficiently dim in absolute terms and ambient cube is swamped by direct lights
if ( avgCubeLuminance < r_ambientmin . GetFloat ( ) & & ( avgCubeLuminance < ( fDirectLight * r_ambientfraction . GetFloat ( ) ) ) )
{
Vector vFinalAmbientCube [ 6 ] ;
float fBoostFactor = min ( ( fDirectLight * r_ambientfraction . GetFloat ( ) ) / maxCubeLuminance , 5.f ) ; // boost no more than a certain factor
for ( int i = 0 ; i < 6 ; i + + )
{
vFinalAmbientCube [ i ] = pState - > r_boxcolor [ i ] * fBoostFactor ;
}
g_pStudioRender - > SetAmbientLightColors ( vFinalAmbientCube ) ; // Boost
}
else
{
g_pStudioRender - > SetAmbientLightColors ( pState - > r_boxcolor ) ; // No Boost
}
}
else // Don't bother with ambient boost, just use the ambient cube as is
{
g_pStudioRender - > SetAmbientLightColors ( pState - > r_boxcolor ) ; // No Boost
}
}
pRenderContext - > SetAmbientLight ( 0.0 , 0.0 , 0.0 ) ;
R_SetNonAmbientLightingState ( pState - > numlights , pState - > locallight ,
& drawInfo . m_nLocalLightCount , & drawInfo . m_LocalLightDescs [ 0 ] , true ) ;
// Cache lighting for decals.
if ( pModelInst & & drawInfo . m_bStaticLighting & & bHasDecals )
{
R_SetNonAmbientLightingState ( lightingDecalState . numlights , lightingDecalState . locallight ,
& drawInfo . m_nLocalLightCount , & drawInfo . m_LocalLightDescs [ 0 ] , false ) ;
}
}
if ( pInfo . pLightingOffset & & ! pInfo . pLightingOrigin )
{
for ( int i = 0 ; i < lightingState . numlights ; + + i )
{
lightingState . locallight [ i ] - > origin = pSaveLightPos [ i ] ;
}
}
# endif
}
//-----------------------------------------------------------------------------
// Uses this material instead of the one the model was compiled with
//-----------------------------------------------------------------------------
// FIXME: a duplicate of what's in CEngineTool::GetLightingConditions
int GetLightingConditions ( const Vector & vecLightingOrigin , Vector * pColors , int nMaxLocalLights , LightDesc_t * pLocalLights , ITexture * & pEnvCubemapTexture )
{
# ifndef SWDS
LightcacheGetDynamic_Stats stats ;
LightingState_t state ;
pEnvCubemapTexture = NULL ;
pEnvCubemapTexture = LightcacheGetDynamic ( vecLightingOrigin , state , stats ) ;
Assert ( state . numlights > = 0 & & state . numlights < = MAXLOCALLIGHTS ) ;
memcpy ( pColors , state . r_boxcolor , sizeof ( state . r_boxcolor ) ) ;
int nLightCount = 0 ;
for ( int i = 0 ; i < state . numlights ; + + i )
{
LightDesc_t * pLightDesc = & pLocalLights [ nLightCount ] ;
if ( ! WorldLightToMaterialLight ( state . locallight [ i ] , * pLightDesc ) )
continue ;
// Apply lightstyle
float bias = LightStyleValue ( state . locallight [ i ] - > style ) ;
// Deal with overbrighting + bias
pLightDesc - > m_Color [ 0 ] * = bias ;
pLightDesc - > m_Color [ 1 ] * = bias ;
pLightDesc - > m_Color [ 2 ] * = bias ;
if ( + + nLightCount > = nMaxLocalLights )
break ;
}
return nLightCount ;
# endif
return 0 ;
}
// FIXME: a duplicate of what's in CCDmeMdlRenderable<T>::SetUpLighting and CDmeEmitter::SetUpLighting
void CModelRender : : SetupLighting ( const Vector & vecCenter )
{
# ifndef SWDS
// Set up lighting conditions
Vector vecAmbient [ 6 ] ;
Vector4D vecAmbient4D [ 6 ] ;
LightDesc_t desc [ 2 ] ;
ITexture * pEnvCubemapTexture = NULL ;
int nLightCount = GetLightingConditions ( vecCenter , vecAmbient , 2 , desc , pEnvCubemapTexture ) ;
int nMaxLights = g_pMaterialSystemHardwareConfig - > MaxNumLights ( ) ;
if ( nLightCount > nMaxLights )
{
nLightCount = nMaxLights ;
}
int i ;
for ( i = 0 ; i < 6 ; i + + )
{
VectorCopy ( vecAmbient [ i ] , vecAmbient4D [ i ] . AsVector3D ( ) ) ;
vecAmbient4D [ i ] [ 3 ] = 1.0f ;
}
CMatRenderContextPtr pRenderContext ( g_pMaterialSystem ) ;
pRenderContext - > SetAmbientLightCube ( vecAmbient4D ) ;
if ( pEnvCubemapTexture )
{
pRenderContext - > BindLocalCubemap ( pEnvCubemapTexture ) ;
}
for ( i = 0 ; i < nLightCount ; i + + )
{
LightDesc_t * pLight = & desc [ i ] ;
pLight - > m_Flags = 0 ;
if ( pLight - > m_Attenuation0 ! = 0.0f )
{
pLight - > m_Flags | = LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0 ;
}
if ( pLight - > m_Attenuation1 ! = 0.0f )
{
pLight - > m_Flags | = LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION1 ;
}
if ( pLight - > m_Attenuation2 ! = 0.0f )
{
pLight - > m_Flags | = LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION2 ;
}
pRenderContext - > SetLight ( i , desc [ i ] ) ;
}
for ( ; i < nMaxLights ; i + + )
{
LightDesc_t disableDesc ;
disableDesc . m_Type = MATERIAL_LIGHT_DISABLE ;
pRenderContext - > SetLight ( i , disableDesc ) ;
}
# endif
}
//-----------------------------------------------------------------------------
// Uses this material instead of the one the model was compiled with
//-----------------------------------------------------------------------------
void CModelRender : : ForcedMaterialOverride ( IMaterial * newMaterial , OverrideType_t nOverrideType )
{
tmZone ( TELEMETRY_LEVEL0 , TMZF_NONE , " %s " , __FUNCTION__ ) ;
g_pStudioRender - > ForcedMaterialOverride ( newMaterial , nOverrideType ) ;
}
//-----------------------------------------------------------------------------
// Sets up the render state for a model
//-----------------------------------------------------------------------------
matrix3x4_t * CModelRender : : SetupModelState ( IClientRenderable * pRenderable )
{
const model_t * pModel = pRenderable - > GetModel ( ) ;
if ( ! pModel )
return NULL ;
studiohdr_t * pStudioHdr = modelinfo - > GetStudiomodel ( const_cast < model_t * > ( pModel ) ) ;
if ( pStudioHdr - > numbodyparts = = 0 )
return NULL ;
matrix3x4_t * pBoneMatrices = NULL ;
# ifndef SWDS
// Set up skinning state
Assert ( pRenderable ) ;
{
int nBoneCount = pStudioHdr - > numbones ;
pBoneMatrices = g_pStudioRender - > LockBoneMatrices ( pStudioHdr - > numbones ) ;
pRenderable - > SetupBones ( pBoneMatrices , nBoneCount , BONE_USED_BY_ANYTHING , cl . GetTime ( ) ) ; // hack hack
g_pStudioRender - > UnlockBoneMatrices ( ) ;
}
# endif
return pBoneMatrices ;
}
struct ModelDebugOverlayData_t
{
DrawModelInfo_t m_ModelInfo ;
DrawModelResults_t m_ModelResults ;
Vector m_Origin ;
2022-11-05 19:22:27 +08:00
ModelDebugOverlayData_t ( ) = default ;
2020-04-23 00:56:21 +08:00
private :
ModelDebugOverlayData_t ( const ModelDebugOverlayData_t & vOther ) ;
} ;
static CUtlVector < ModelDebugOverlayData_t > s_SavedModelInfo ;
void DrawModelDebugOverlay ( const DrawModelInfo_t & info , const DrawModelResults_t & results , const Vector & origin , float r = 1.0f , float g = 1.0f , float b = 1.0f )
{
# ifndef SWDS
float alpha = 1 ;
if ( r_drawmodelstatsoverlaydistance . GetFloat ( ) = = 1 )
{
alpha = 1.f - clamp ( CurrentViewOrigin ( ) . DistTo ( origin ) / r_drawmodelstatsoverlaydistance . GetFloat ( ) , 0.f , 1.f ) ;
}
else
{
float flDistance = CurrentViewOrigin ( ) . DistTo ( origin ) ;
// The view model keeps throwing up its data and it looks like garbage, so I am trying to get rid of it.
if ( flDistance < 36.0f )
return ;
if ( flDistance > r_drawmodelstatsoverlaydistance . GetFloat ( ) )
return ;
}
Assert ( info . m_pStudioHdr ) ;
Assert ( info . m_pStudioHdr - > pszName ( ) ) ;
Assert ( info . m_pHardwareData ) ;
float duration = 0.0f ;
int lineOffset = 0 ;
if ( ! info . m_pStudioHdr | | ! info . m_pStudioHdr - > pszName ( ) | | ! info . m_pHardwareData )
{
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , " This model has problems. . see a programmer. " ) ;
return ;
}
char buf [ 1024 ] ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , info . m_pStudioHdr - > pszName ( ) ) ;
Q_snprintf ( buf , sizeof ( buf ) , " lod: %d/%d \n " , results . m_nLODUsed + 1 , ( int ) info . m_pHardwareData - > m_NumLODs ) ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , buf ) ;
Q_snprintf ( buf , sizeof ( buf ) , " tris: %d \n " , results . m_ActualTriCount ) ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , buf ) ;
Q_snprintf ( buf , sizeof ( buf ) , " hardware bones: %d \n " , results . m_NumHardwareBones ) ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , buf ) ;
Q_snprintf ( buf , sizeof ( buf ) , " num batches: %d \n " , results . m_NumBatches ) ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , buf ) ;
Q_snprintf ( buf , sizeof ( buf ) , " has shadow lod: %s \n " , ( info . m_pStudioHdr - > flags & STUDIOHDR_FLAGS_HASSHADOWLOD ) ? " true " : " false " ) ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , buf ) ;
Q_snprintf ( buf , sizeof ( buf ) , " num materials: %d \n " , results . m_NumMaterials ) ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , buf ) ;
int i ;
for ( i = 0 ; i < results . m_Materials . Count ( ) ; i + + )
{
IMaterial * pMaterial = results . m_Materials [ i ] ;
if ( pMaterial )
{
int numPasses = pMaterial - > GetNumPasses ( ) ;
Q_snprintf ( buf , sizeof ( buf ) , " \t %s (%d %s) \n " , results . m_Materials [ i ] - > GetName ( ) , numPasses , numPasses > 1 ? " passes " : " pass " ) ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , buf ) ;
}
}
if ( results . m_Materials . Count ( ) > results . m_NumMaterials )
{
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , " (Remaining materials not shown) \n " ) ;
}
if ( r_drawmodelstatsoverlay . GetInt ( ) = = 2 )
{
Q_snprintf ( buf , sizeof ( buf ) , " Render Time: %0.1f ms \n " , results . m_RenderTime . GetDuration ( ) . GetMillisecondsF ( ) ) ;
CDebugOverlay : : AddTextOverlay ( origin , lineOffset + + , duration , r , g , b , alpha , buf ) ;
}
//Q_snprintf( buf, sizeof( buf ), "Render Time: %0.1f ms\n", info.m_pClientEntity
# endif
}
void AddModelDebugOverlay ( const DrawModelInfo_t & info , const DrawModelResults_t & results , const Vector & origin )
{
ModelDebugOverlayData_t & tmp = s_SavedModelInfo [ s_SavedModelInfo . AddToTail ( ) ] ;
tmp . m_ModelInfo = info ;
tmp . m_ModelResults = results ;
tmp . m_Origin = origin ;
}
void ClearSaveModelDebugOverlays ( void )
{
s_SavedModelInfo . RemoveAll ( ) ;
}
int SavedModelInfo_Compare_f ( const void * l , const void * r )
{
ModelDebugOverlayData_t * left = ( ModelDebugOverlayData_t * ) l ;
ModelDebugOverlayData_t * right = ( ModelDebugOverlayData_t * ) r ;
return left - > m_ModelResults . m_RenderTime . GetDuration ( ) . GetSeconds ( ) < right - > m_ModelResults . m_RenderTime . GetDuration ( ) . GetSeconds ( ) ;
}
static ConVar r_drawmodelstatsoverlaymin ( " r_drawmodelstatsoverlaymin " , " 0.1 " , FCVAR_ARCHIVE , " time in milliseconds that a model must take to render before showing an overlay in r_drawmodelstatsoverlay 2 " ) ;
static ConVar r_drawmodelstatsoverlaymax ( " r_drawmodelstatsoverlaymax " , " 1.5 " , FCVAR_ARCHIVE , " time in milliseconds beyond which a model overlay is fully red in r_drawmodelstatsoverlay 2 " ) ;
void DrawSavedModelDebugOverlays ( void )
{
if ( s_SavedModelInfo . Count ( ) = = 0 )
{
return ;
}
float min = r_drawmodelstatsoverlaymin . GetFloat ( ) ;
float max = r_drawmodelstatsoverlaymax . GetFloat ( ) ;
float ooRange = 1.0f / ( max - min ) ;
int i ;
for ( i = 0 ; i < s_SavedModelInfo . Count ( ) ; i + + )
{
float r , g , b ;
float t = s_SavedModelInfo [ i ] . m_ModelResults . m_RenderTime . GetDuration ( ) . GetMillisecondsF ( ) ;
if ( t > min )
{
if ( t > = max )
{
r = 1.0f ; g = 0.0f ; b = 0.0f ;
}
else
{
r = ( t - min ) * ooRange ;
g = 1.0f - r ;
b = 0.0f ;
}
DrawModelDebugOverlay ( s_SavedModelInfo [ i ] . m_ModelInfo , s_SavedModelInfo [ i ] . m_ModelResults , s_SavedModelInfo [ i ] . m_Origin , r , g , b ) ;
}
}
ClearSaveModelDebugOverlays ( ) ;
}
void CModelRender : : DebugDrawLightingOrigin ( const DrawModelState_t & state , const ModelRenderInfo_t & pInfo )
{
# ifndef SWDS
// determine light origin in world space
Vector illumPosition ;
Vector lightOrigin ;
if ( pInfo . pLightingOrigin )
{
illumPosition = * pInfo . pLightingOrigin ;
lightOrigin = illumPosition ;
}
else
{
R_ComputeLightingOrigin ( state . m_pRenderable , state . m_pStudioHdr , * state . m_pModelToWorld , illumPosition ) ;
lightOrigin = illumPosition ;
if ( pInfo . pLightingOffset )
{
VectorTransform ( illumPosition , * pInfo . pLightingOffset , lightOrigin ) ;
}
}
// draw z planar cross at lighting origin
Vector pt0 ;
Vector pt1 ;
pt0 = lightOrigin ;
pt1 = lightOrigin ;
pt0 . x - = 4 ;
pt1 . x + = 4 ;
CDebugOverlay : : AddLineOverlay ( pt0 , pt1 , 0 , 255 , 0 , 255 , true , 0.0f ) ;
pt0 = lightOrigin ;
pt1 = lightOrigin ;
pt0 . y - = 4 ;
pt1 . y + = 4 ;
CDebugOverlay : : AddLineOverlay ( pt0 , pt1 , 0 , 255 , 0 , 255 , true , 0.0f ) ;
// draw lines from the light origin to the hull boundaries to identify model
Vector pt ;
pt0 . x = state . m_pStudioHdr - > hull_min . x ;
pt0 . y = state . m_pStudioHdr - > hull_min . y ;
pt0 . z = state . m_pStudioHdr - > hull_min . z ;
VectorTransform ( pt0 , * state . m_pModelToWorld , pt1 ) ;
CDebugOverlay : : AddLineOverlay ( lightOrigin , pt1 , 100 , 100 , 150 , 255 , true , 0.0f ) ;
pt0 . x = state . m_pStudioHdr - > hull_min . x ;
pt0 . y = state . m_pStudioHdr - > hull_max . y ;
pt0 . z = state . m_pStudioHdr - > hull_min . z ;
VectorTransform ( pt0 , * state . m_pModelToWorld , pt1 ) ;
CDebugOverlay : : AddLineOverlay ( lightOrigin , pt1 , 100 , 100 , 150 , 255 , true , 0.0f ) ;
pt0 . x = state . m_pStudioHdr - > hull_max . x ;
pt0 . y = state . m_pStudioHdr - > hull_max . y ;
pt0 . z = state . m_pStudioHdr - > hull_min . z ;
VectorTransform ( pt0 , * state . m_pModelToWorld , pt1 ) ;
CDebugOverlay : : AddLineOverlay ( lightOrigin , pt1 , 100 , 100 , 150 , 255 , true , 0.0f ) ;
pt0 . x = state . m_pStudioHdr - > hull_max . x ;
pt0 . y = state . m_pStudioHdr - > hull_min . y ;
pt0 . z = state . m_pStudioHdr - > hull_min . z ;
VectorTransform ( pt0 , * state . m_pModelToWorld , pt1 ) ;
CDebugOverlay : : AddLineOverlay ( lightOrigin , pt1 , 100 , 100 , 150 , 255 , true , 0.0f ) ;
pt0 . x = state . m_pStudioHdr - > hull_min . x ;
pt0 . y = state . m_pStudioHdr - > hull_min . y ;
pt0 . z = state . m_pStudioHdr - > hull_max . z ;
VectorTransform ( pt0 , * state . m_pModelToWorld , pt1 ) ;
CDebugOverlay : : AddLineOverlay ( lightOrigin , pt1 , 100 , 100 , 150 , 255 , true , 0.0f ) ;
pt0 . x = state . m_pStudioHdr - > hull_min . x ;
pt0 . y = state . m_pStudioHdr - > hull_max . y ;
pt0 . z = state . m_pStudioHdr - > hull_max . z ;
VectorTransform ( pt0 , * state . m_pModelToWorld , pt1 ) ;
CDebugOverlay : : AddLineOverlay ( lightOrigin , pt1 , 100 , 100 , 150 , 255 , true , 0.0f ) ;
pt0 . x = state . m_pStudioHdr - > hull_max . x ;
pt0 . y = state . m_pStudioHdr - > hull_max . y ;
pt0 . z = state . m_pStudioHdr - > hull_max . z ;
VectorTransform ( pt0 , * state . m_pModelToWorld , pt1 ) ;
CDebugOverlay : : AddLineOverlay ( lightOrigin , pt1 , 100 , 100 , 150 , 255 , true , 0.0f ) ;
pt0 . x = state . m_pStudioHdr - > hull_max . x ;
pt0 . y = state . m_pStudioHdr - > hull_min . y ;
pt0 . z = state . m_pStudioHdr - > hull_max . z ;
VectorTransform ( pt0 , * state . m_pModelToWorld , pt1 ) ;
CDebugOverlay : : AddLineOverlay ( lightOrigin , pt1 , 100 , 100 , 150 , 255 , true , 0.0f ) ;
# endif
}
//-----------------------------------------------------------------------------
// Actually renders the model
//-----------------------------------------------------------------------------
void CModelRender : : DrawModelExecute ( const DrawModelState_t & state , const ModelRenderInfo_t & pInfo , matrix3x4_t * pBoneToWorld )
{
# ifndef SWDS
bool bShadowDepth = ( pInfo . flags & STUDIO_SHADOWDEPTHTEXTURE ) ! = 0 ;
bool bSSAODepth = ( pInfo . flags & STUDIO_SSAODEPTHTEXTURE ) ! = 0 ;
// Bail if we're rendering into shadow depth map and this model doesn't cast shadows
if ( bShadowDepth & & ( ( pInfo . pModel - > flags & MODELFLAG_STUDIOHDR_DO_NOT_CAST_SHADOWS ) ! = 0 ) )
return ;
// Shadow state...
g_pShadowMgr - > SetModelShadowState ( pInfo . instance ) ;
if ( g_bTextMode )
return ;
// Sets up flexes
float * pFlexWeights = NULL ;
float * pFlexDelayedWeights = NULL ;
int nFlexCount = state . m_pStudioHdr - > numflexdesc ;
if ( nFlexCount > 0 )
{
// Does setup for flexes
Assert ( pBoneToWorld ) ;
bool bUsesDelayedWeights = state . m_pRenderable - > UsesFlexDelayedWeights ( ) ;
g_pStudioRender - > LockFlexWeights ( nFlexCount , & pFlexWeights , bUsesDelayedWeights ? & pFlexDelayedWeights : NULL ) ;
state . m_pRenderable - > SetupWeights ( pBoneToWorld , nFlexCount , pFlexWeights , pFlexDelayedWeights ) ;
g_pStudioRender - > UnlockFlexWeights ( ) ;
}
// OPTIMIZE: Try to precompute part of this mess once a frame at the very least.
bool bUsesBumpmapping = ( g_pMaterialSystemHardwareConfig - > GetDXSupportLevel ( ) > = 80 ) & & ( pInfo . pModel - > flags & MODELFLAG_STUDIOHDR_USES_BUMPMAPPING ) ;
bool bStaticLighting = ( state . m_drawFlags & STUDIORENDER_DRAW_STATIC_LIGHTING ) & &
( state . m_pStudioHdr - > flags & STUDIOHDR_FLAGS_STATIC_PROP ) & &
( ! bUsesBumpmapping ) & &
( pInfo . instance ! = MODEL_INSTANCE_INVALID ) & &
g_pMaterialSystemHardwareConfig - > SupportsColorOnSecondStream ( ) ;
bool bVertexLit = ( pInfo . pModel - > flags & MODELFLAG_VERTEXLIT ) ! = 0 ;
bool bNeedsEnvCubemap = r_showenvcubemap . GetInt ( ) | | ( pInfo . pModel - > flags & MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP ) ;
if ( r_drawmodellightorigin . GetBool ( ) & & ! bShadowDepth & & ! bSSAODepth )
{
DebugDrawLightingOrigin ( state , pInfo ) ;
}
ColorMeshInfo_t * pColorMeshes = NULL ;
DataCacheHandle_t hColorMeshData = DC_INVALID_HANDLE ;
if ( bStaticLighting )
{
// have static lighting, get from cache
hColorMeshData = m_ModelInstances [ pInfo . instance ] . m_ColorMeshHandle ;
CColorMeshData * pColorMeshData = CacheGet ( hColorMeshData ) ;
if ( ! pColorMeshData | | pColorMeshData - > m_bNeedsRetry )
{
// color meshes are not present, try to re-establish
if ( RecomputeStaticLighting ( pInfo . instance ) )
{
pColorMeshData = CacheGet ( hColorMeshData ) ;
}
else if ( ! pColorMeshData | | ! pColorMeshData - > m_bNeedsRetry )
{
// can't draw
return ;
}
}
if ( pColorMeshData & & ( pColorMeshData - > m_bColorMeshValid | | pColorMeshData - > m_bColorTextureValid ) )
{
pColorMeshes = pColorMeshData - > m_pMeshInfos ;
if ( pColorMeshData - > m_bColorTextureValid & & ! pColorMeshData - > m_bColorTextureCreated )
{
CreateLightmapsFromData ( pColorMeshData ) ;
}
}
else
{
// failed, draw without static lighting
bStaticLighting = false ;
}
}
DrawModelInfo_t info ;
info . m_bStaticLighting = false ;
// get lighting from ambient light sources and radiosity bounces
// also set up the env_cubemap from the light cache if necessary.
if ( ( bVertexLit | | bNeedsEnvCubemap ) & & ! bShadowDepth & & ! bSSAODepth )
{
// See if we're using static lighting
LightCacheHandle_t * pLightCache = NULL ;
if ( pInfo . instance ! = MODEL_INSTANCE_INVALID )
{
if ( ( m_ModelInstances [ pInfo . instance ] . m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING ) & & m_ModelInstances [ pInfo . instance ] . m_LightCacheHandle )
{
pLightCache = & m_ModelInstances [ pInfo . instance ] . m_LightCacheHandle ;
}
}
// Choose the lighting origin
Vector entOrigin ;
R_ComputeLightingOrigin ( state . m_pRenderable , state . m_pStudioHdr , * state . m_pModelToWorld , entOrigin ) ;
// Set up lighting based on the lighting origin
StudioSetupLighting ( state , entOrigin , pLightCache , bVertexLit , bNeedsEnvCubemap , bStaticLighting , info , pInfo , state . m_drawFlags ) ;
}
// Set up the camera state
g_pStudioRender - > SetViewState ( CurrentViewOrigin ( ) , CurrentViewRight ( ) , CurrentViewUp ( ) , CurrentViewForward ( ) ) ;
// Color + alpha modulation
g_pStudioRender - > SetColorModulation ( r_colormod ) ;
g_pStudioRender - > SetAlphaModulation ( r_blend ) ;
Assert ( modelloader - > IsLoaded ( pInfo . pModel ) ) ;
info . m_pStudioHdr = state . m_pStudioHdr ;
info . m_pHardwareData = state . m_pStudioHWData ;
info . m_Skin = pInfo . skin ;
info . m_Body = pInfo . body ;
info . m_HitboxSet = pInfo . hitboxset ;
info . m_pClientEntity = ( void * ) state . m_pRenderable ;
info . m_Lod = state . m_lod ;
info . m_pColorMeshes = pColorMeshes ;
// Don't do decals if shadow depth mapping...
info . m_Decals = ( bShadowDepth | | bSSAODepth ) ? STUDIORENDER_DECAL_INVALID : state . m_decals ;
// Get perf stats if we are going to use them.
int overlayVal = r_drawmodelstatsoverlay . GetInt ( ) ;
int drawFlags = state . m_drawFlags ;
if ( bShadowDepth )
{
drawFlags | = STUDIORENDER_DRAW_OPAQUE_ONLY ;
drawFlags | = STUDIORENDER_SHADOWDEPTHTEXTURE ;
}
if ( bSSAODepth = = true )
{
drawFlags | = STUDIORENDER_DRAW_OPAQUE_ONLY ;
drawFlags | = STUDIORENDER_SSAODEPTHTEXTURE ;
}
if ( overlayVal & & ! bShadowDepth & & ! bSSAODepth )
{
drawFlags | = STUDIORENDER_DRAW_GET_PERF_STATS ;
}
if ( ( pInfo . flags & STUDIO_GENERATE_STATS ) ! = 0 )
{
drawFlags | = STUDIORENDER_GENERATE_STATS ;
}
DrawModelResults_t results ;
g_pStudioRender - > DrawModel ( & results , info , pBoneToWorld , pFlexWeights ,
pFlexDelayedWeights , pInfo . origin , drawFlags ) ;
info . m_Lod = results . m_nLODUsed ;
if ( overlayVal & & ! bShadowDepth & & ! bSSAODepth )
{
if ( overlayVal ! = 2 )
{
DrawModelDebugOverlay ( info , results , pInfo . origin ) ;
}
else
{
AddModelDebugOverlay ( info , results , pInfo . origin ) ;
}
}
if ( pColorMeshes )
{
ProtectColorDataIfQueued ( hColorMeshData ) ;
}
# endif
}
//-----------------------------------------------------------------------------
// Main entry point for model rendering in the engine
//-----------------------------------------------------------------------------
int CModelRender : : DrawModel (
int flags ,
IClientRenderable * pRenderable ,
ModelInstanceHandle_t instance ,
int entity_index ,
const model_t * pModel ,
const Vector & origin ,
const QAngle & angles ,
int skin ,
int body ,
int hitboxset ,
const matrix3x4_t * pModelToWorld ,
const matrix3x4_t * pLightingOffset )
{
ModelRenderInfo_t sInfo ;
sInfo . flags = flags ;
sInfo . pRenderable = pRenderable ;
sInfo . instance = instance ;
sInfo . entity_index = entity_index ;
sInfo . pModel = pModel ;
sInfo . origin = origin ;
sInfo . angles = angles ;
sInfo . skin = skin ;
sInfo . body = body ;
sInfo . hitboxset = hitboxset ;
sInfo . pModelToWorld = pModelToWorld ;
sInfo . pLightingOffset = pLightingOffset ;
if ( ( r_entity . GetInt ( ) = = - 1 ) | | ( r_entity . GetInt ( ) = = entity_index ) )
{
return DrawModelEx ( sInfo ) ;
}
return 0 ;
}
int CModelRender : : ComputeLOD ( const ModelRenderInfo_t & info , studiohwdata_t * pStudioHWData )
{
int lod = r_lod . GetInt ( ) ;
// FIXME!!! This calc should be in studiorender, not here!!!!! But since the bone setup
// is done here, and we need the bone mask, we'll do it here for now.
if ( lod = = - 1 )
{
CMatRenderContextPtr pRenderContext ( materials ) ;
float screenSize = pRenderContext - > ComputePixelWidthOfSphere ( info . pRenderable - > GetRenderOrigin ( ) , 0.5f ) ;
float metric = pStudioHWData - > LODMetric ( screenSize ) ;
lod = pStudioHWData - > GetLODForMetric ( metric ) ;
}
else
{
if ( ( info . flags & STUDIOHDR_FLAGS_HASSHADOWLOD ) & & ( lod > pStudioHWData - > m_NumLODs - 2 ) )
{
lod = pStudioHWData - > m_NumLODs - 2 ;
}
else if ( lod > pStudioHWData - > m_NumLODs - 1 )
{
lod = pStudioHWData - > m_NumLODs - 1 ;
}
else if ( lod < 0 )
{
lod = 0 ;
}
}
if ( lod < 0 )
{
lod = 0 ;
}
else if ( lod > = pStudioHWData - > m_NumLODs )
{
lod = pStudioHWData - > m_NumLODs - 1 ;
}
// clamp to root lod
if ( lod < pStudioHWData - > m_RootLOD )
{
lod = pStudioHWData - > m_RootLOD ;
}
Assert ( lod > = 0 & & lod < pStudioHWData - > m_NumLODs ) ;
return lod ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &pInfo -
//-----------------------------------------------------------------------------
bool CModelRender : : DrawModelSetup ( ModelRenderInfo_t & pInfo , DrawModelState_t * pState , matrix3x4_t * pCustomBoneToWorld , matrix3x4_t * * ppBoneToWorldOut )
{
* ppBoneToWorldOut = NULL ;
# ifdef SWDS
return false ;
# endif
# if _DEBUG
if ( ( char * ) pInfo . pRenderable < ( char * ) 1024 )
{
Error ( " CModelRender::DrawModel: pRenderable == 0x%p " , pInfo . pRenderable ) ;
}
# endif
// Can only deal with studio models
Assert ( pInfo . pModel - > type = = mod_studio ) ;
Assert ( modelloader - > IsLoaded ( pInfo . pModel ) ) ;
DrawModelState_t & state = * pState ;
state . m_pStudioHdr = g_pMDLCache - > GetStudioHdr ( pInfo . pModel - > studio ) ;
state . m_pRenderable = pInfo . pRenderable ;
// Quick exit if we're just supposed to draw a specific model...
if ( ( r_entity . GetInt ( ) ! = - 1 ) & & ( r_entity . GetInt ( ) ! = pInfo . entity_index ) )
return false ;
// quick exit
if ( state . m_pStudioHdr - > numbodyparts = = 0 )
return false ;
if ( ! pInfo . pModelToWorld )
{
Assert ( 0 ) ;
return false ;
}
state . m_pModelToWorld = pInfo . pModelToWorld ;
Assert ( pInfo . pRenderable ) ;
state . m_pStudioHWData = g_pMDLCache - > GetHardwareData ( pInfo . pModel - > studio ) ;
if ( ! state . m_pStudioHWData )
return false ;
state . m_lod = ComputeLOD ( pInfo , state . m_pStudioHWData ) ;
int boneMask = BONE_USED_BY_VERTEX_AT_LOD ( state . m_lod ) ;
// Why isn't this always set?!?
if ( ( pInfo . flags & STUDIO_RENDER ) = = 0 )
{
// no rendering, just force a bone setup. Don't copy the bones
bool bOk = pInfo . pRenderable - > SetupBones ( NULL , MAXSTUDIOBONES , boneMask , cl . GetTime ( ) ) ;
return bOk ;
}
int nBoneCount = state . m_pStudioHdr - > numbones ;
matrix3x4_t * pBoneToWorld = pCustomBoneToWorld ;
if ( ! pCustomBoneToWorld )
{
pBoneToWorld = g_pStudioRender - > LockBoneMatrices ( nBoneCount ) ;
}
const bool bOk = pInfo . pRenderable - > SetupBones ( pBoneToWorld , nBoneCount , boneMask , cl . GetTime ( ) ) ;
if ( ! pCustomBoneToWorld )
{
g_pStudioRender - > UnlockBoneMatrices ( ) ;
}
if ( ! bOk )
return false ;
* ppBoneToWorldOut = pBoneToWorld ;
// Convert the instance to a decal handle.
state . m_decals = STUDIORENDER_DECAL_INVALID ;
if ( pInfo . instance ! = MODEL_INSTANCE_INVALID )
{
state . m_decals = m_ModelInstances [ pInfo . instance ] . m_DecalHandle ;
}
state . m_drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL ;
if ( pInfo . flags & STUDIO_TWOPASS )
{
if ( pInfo . flags & STUDIO_TRANSPARENCY )
{
state . m_drawFlags = STUDIORENDER_DRAW_TRANSLUCENT_ONLY ;
}
else
{
state . m_drawFlags = STUDIORENDER_DRAW_OPAQUE_ONLY ;
}
}
if ( pInfo . flags & STUDIO_STATIC_LIGHTING )
{
state . m_drawFlags | = STUDIORENDER_DRAW_STATIC_LIGHTING ;
}
if ( pInfo . flags & STUDIO_ITEM_BLINK )
{
state . m_drawFlags | = STUDIORENDER_DRAW_ITEM_BLINK ;
}
if ( pInfo . flags & STUDIO_WIREFRAME )
{
state . m_drawFlags | = STUDIORENDER_DRAW_WIREFRAME ;
}
if ( pInfo . flags & STUDIO_NOSHADOWS )
{
state . m_drawFlags | = STUDIORENDER_DRAW_NO_SHADOWS ;
}
if ( r_drawmodelstatsoverlay . GetInt ( ) = = 2 )
{
state . m_drawFlags | = STUDIORENDER_DRAW_ACCURATETIME ;
}
if ( pInfo . flags & STUDIO_SHADOWDEPTHTEXTURE )
{
state . m_drawFlags | = STUDIORENDER_SHADOWDEPTHTEXTURE ;
}
return true ;
}
int CModelRender : : DrawModelEx ( ModelRenderInfo_t & pInfo )
{
# ifndef SWDS
DrawModelState_t state ;
matrix3x4_t tmpmat ;
if ( ! pInfo . pModelToWorld )
{
pInfo . pModelToWorld = & tmpmat ;
// Turns the origin + angles into a matrix
AngleMatrix ( pInfo . angles , pInfo . origin , tmpmat ) ;
}
matrix3x4_t * pBoneToWorld ;
if ( ! DrawModelSetup ( pInfo , & state , NULL , & pBoneToWorld ) )
return 0 ;
if ( pInfo . flags & STUDIO_RENDER )
{
DrawModelExecute ( state , pInfo , pBoneToWorld ) ;
}
return 1 ;
# else
return 0 ;
# endif
}
int CModelRender : : DrawModelExStaticProp ( ModelRenderInfo_t & pInfo )
{
# ifndef SWDS
bool bShadowDepth = ( pInfo . flags & STUDIO_SHADOWDEPTHTEXTURE ) ! = 0 ;
# if _DEBUG
if ( ( char * ) pInfo . pRenderable < ( char * ) 1024 )
{
Error ( " CModelRender::DrawModel: pRenderable == %p " , pInfo . pRenderable ) ;
}
// Can only deal with studio models
if ( pInfo . pModel - > type ! = mod_studio )
return 0 ;
# endif
Assert ( modelloader - > IsLoaded ( pInfo . pModel ) ) ;
DrawModelState_t state ;
state . m_pStudioHdr = g_pMDLCache - > GetStudioHdr ( pInfo . pModel - > studio ) ;
state . m_pRenderable = pInfo . pRenderable ;
// quick exit
if ( state . m_pStudioHdr - > numbodyparts = = 0 | | g_bTextMode )
return 1 ;
state . m_pStudioHWData = g_pMDLCache - > GetHardwareData ( pInfo . pModel - > studio ) ;
if ( ! state . m_pStudioHWData )
return 0 ;
Assert ( pInfo . pModelToWorld ) ;
state . m_pModelToWorld = pInfo . pModelToWorld ;
Assert ( pInfo . pRenderable ) ;
int lod = ComputeLOD ( pInfo , state . m_pStudioHWData ) ;
// int boneMask = BONE_USED_BY_VERTEX_AT_LOD( lod );
// Why isn't this always set?!?
if ( ! ( pInfo . flags & STUDIO_RENDER ) )
return 0 ;
// Convert the instance to a decal handle.
StudioDecalHandle_t decalHandle = STUDIORENDER_DECAL_INVALID ;
if ( ( pInfo . instance ! = MODEL_INSTANCE_INVALID ) & & ! ( pInfo . flags & STUDIO_SHADOWDEPTHTEXTURE ) )
{
decalHandle = m_ModelInstances [ pInfo . instance ] . m_DecalHandle ;
}
int drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL ;
if ( pInfo . flags & STUDIO_TWOPASS )
{
if ( pInfo . flags & STUDIO_TRANSPARENCY )
{
drawFlags = STUDIORENDER_DRAW_TRANSLUCENT_ONLY ;
}
else
{
drawFlags = STUDIORENDER_DRAW_OPAQUE_ONLY ;
}
}
if ( pInfo . flags & STUDIO_STATIC_LIGHTING )
{
drawFlags | = STUDIORENDER_DRAW_STATIC_LIGHTING ;
}
if ( pInfo . flags & STUDIO_WIREFRAME )
{
drawFlags | = STUDIORENDER_DRAW_WIREFRAME ;
}
if ( pInfo . flags & STUDIO_GENERATE_STATS )
{
drawFlags | = STUDIORENDER_GENERATE_STATS ;
}
// Shadow state...
g_pShadowMgr - > SetModelShadowState ( pInfo . instance ) ;
// OPTIMIZE: Try to precompute part of this mess once a frame at the very least.
bool bUsesBumpmapping = ( g_pMaterialSystemHardwareConfig - > GetDXSupportLevel ( ) > = 80 ) & & ( pInfo . pModel - > flags & MODELFLAG_STUDIOHDR_USES_BUMPMAPPING ) ;
bool bStaticLighting = ( ( drawFlags & STUDIORENDER_DRAW_STATIC_LIGHTING ) & &
( state . m_pStudioHdr - > flags & STUDIOHDR_FLAGS_STATIC_PROP ) & &
( ! bUsesBumpmapping ) & &
( pInfo . instance ! = MODEL_INSTANCE_INVALID ) & &
g_pMaterialSystemHardwareConfig - > SupportsColorOnSecondStream ( ) ) ;
bool bVertexLit = ( pInfo . pModel - > flags & MODELFLAG_VERTEXLIT ) ! = 0 ;
bool bNeedsEnvCubemap = r_showenvcubemap . GetInt ( ) | | ( pInfo . pModel - > flags & MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP ) ;
if ( r_drawmodellightorigin . GetBool ( ) )
{
DebugDrawLightingOrigin ( state , pInfo ) ;
}
ColorMeshInfo_t * pColorMeshes = NULL ;
DataCacheHandle_t hColorMeshData = DC_INVALID_HANDLE ;
if ( bStaticLighting )
{
// have static lighting, get from cache
hColorMeshData = m_ModelInstances [ pInfo . instance ] . m_ColorMeshHandle ;
CColorMeshData * pColorMeshData = CacheGet ( hColorMeshData ) ;
if ( ! pColorMeshData | | pColorMeshData - > m_bNeedsRetry )
{
// color meshes are not present, try to re-establish
if ( RecomputeStaticLighting ( pInfo . instance ) )
{
pColorMeshData = CacheGet ( hColorMeshData ) ;
}
else if ( ! pColorMeshData | | ! pColorMeshData - > m_bNeedsRetry )
{
// can't draw
return 0 ;
}
}
if ( pColorMeshData & & ( pColorMeshData - > m_bColorMeshValid | | pColorMeshData - > m_bColorTextureValid ) )
{
pColorMeshes = pColorMeshData - > m_pMeshInfos ;
if ( pColorMeshData - > m_bColorTextureValid & & ! pColorMeshData - > m_bColorTextureCreated )
{
CreateLightmapsFromData ( pColorMeshData ) ;
}
}
else
{
// failed, draw without static lighting
bStaticLighting = false ;
}
}
DrawModelInfo_t info ;
info . m_bStaticLighting = false ;
// Get lighting from ambient light sources and radiosity bounces
// also set up the env_cubemap from the light cache if necessary.
// Don't bother if we're rendering to shadow depth texture
if ( ( bVertexLit | | bNeedsEnvCubemap ) & & ! bShadowDepth )
{
// See if we're using static lighting
LightCacheHandle_t * pLightCache = NULL ;
if ( pInfo . instance ! = MODEL_INSTANCE_INVALID )
{
if ( ( m_ModelInstances [ pInfo . instance ] . m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING ) & & m_ModelInstances [ pInfo . instance ] . m_LightCacheHandle )
{
pLightCache = & m_ModelInstances [ pInfo . instance ] . m_LightCacheHandle ;
}
}
// Choose the lighting origin
Vector entOrigin ;
if ( ! pLightCache )
{
R_ComputeLightingOrigin ( state . m_pRenderable , state . m_pStudioHdr , * state . m_pModelToWorld , entOrigin ) ;
}
// Set up lighting based on the lighting origin
StudioSetupLighting ( state , entOrigin , pLightCache , bVertexLit , bNeedsEnvCubemap , bStaticLighting , info , pInfo , drawFlags ) ;
}
Assert ( modelloader - > IsLoaded ( pInfo . pModel ) ) ;
info . m_pStudioHdr = state . m_pStudioHdr ;
info . m_pHardwareData = state . m_pStudioHWData ;
info . m_Decals = decalHandle ;
info . m_Skin = pInfo . skin ;
info . m_Body = pInfo . body ;
info . m_HitboxSet = pInfo . hitboxset ;
info . m_pClientEntity = ( void * ) state . m_pRenderable ;
info . m_Lod = lod ;
info . m_pColorMeshes = pColorMeshes ;
if ( bShadowDepth )
{
drawFlags | = STUDIORENDER_SHADOWDEPTHTEXTURE ;
}
# ifdef _DEBUG
Vector tmp ;
MatrixGetColumn ( * pInfo . pModelToWorld , 3 , & tmp ) ;
Assert ( VectorsAreEqual ( pInfo . origin , tmp , 1e-3 ) ) ;
# endif
g_pStudioRender - > DrawModelStaticProp ( info , * pInfo . pModelToWorld , drawFlags ) ;
if ( pColorMeshes )
{
ProtectColorDataIfQueued ( hColorMeshData ) ;
}
return 1 ;
# else
return 0 ;
# endif
}
struct robject_t
{
const matrix3x4_t * pMatrix ;
IClientRenderable * pRenderable ;
ColorMeshInfo_t * pColorMeshes ;
ITexture * pEnvCubeMap ;
Vector * pLightingOrigin ;
short modelIndex ;
short lod ;
ModelInstanceHandle_t instance ;
short skin ;
short lightIndex ;
short pad0 ;
} ;
struct rmodel_t
{
const model_t * pModel ;
studiohdr_t * pStudioHdr ;
studiohwdata_t * pStudioHWData ;
float maxArea ;
short lodStart ;
byte lodCount ;
byte bVertexLit : 1 ;
byte bNeedsCubemap : 1 ;
byte bStaticLighting : 1 ;
} ;
class CRobjectLess
{
public :
bool Less ( const robject_t & lhs , const robject_t & rhs , void * pContext )
{
rmodel_t * pModels = static_cast < rmodel_t * > ( pContext ) ;
if ( lhs . modelIndex = = rhs . modelIndex )
{
if ( lhs . skin ! = rhs . skin )
return lhs . skin < rhs . skin ;
return lhs . lod < rhs . lod ;
}
if ( pModels [ lhs . modelIndex ] . maxArea = = pModels [ rhs . modelIndex ] . maxArea )
return lhs . modelIndex < rhs . modelIndex ;
return pModels [ lhs . modelIndex ] . maxArea > pModels [ rhs . modelIndex ] . maxArea ;
}
} ;
struct rdecalmodel_t
{
short objectIndex ;
short lightIndex ;
} ;
/*
// ----------------------------------------
// not yet implemented
struct rlod_t
{
short groupCount ;
short groupStart ;
} ;
struct rgroup_t
{
IMesh * pMesh ;
short batchCount ;
short batchStart ;
short colorMeshIndex ;
short pad0 ;
} ;
struct rbatch_t
{
IMaterial * pMaterial ;
short primitiveType ;
short pad0 ;
unsigned short indexOffset ;
unsigned short indexCount ;
} ;
// ----------------------------------------
*/
inline int FindModel ( const CUtlVector < rmodel_t > & list , const model_t * pModel )
{
for ( int j = list . Count ( ) ; - - j > = 0 ; )
{
if ( list [ j ] . pModel = = pModel )
return j ;
}
return - 1 ;
}
// NOTE: UNDONE: This is a work in progress of a new static prop rendering pipeline
// UNDONE: Expose drawing commands from studiorender and draw here
// UNDONE: Build a similar pipeline for non-static prop models
// UNDONE: Split this into several functions in a sub-object
ConVar r_staticprop_lod ( " r_staticprop_lod " , " -1 " ) ;
int CModelRender : : DrawStaticPropArrayFast ( StaticPropRenderInfo_t * pProps , int count , bool bShadowDepth )
{
# ifndef SWDS
MDLCACHE_CRITICAL_SECTION_ ( g_pMDLCache ) ;
CMatRenderContextPtr pRenderContext ( materials ) ;
const int MAX_OBJECTS = 1024 ;
CUtlSortVector < robject_t , CRobjectLess > objectList ( 0 , MAX_OBJECTS ) ;
CUtlVector < rmodel_t > modelList ( 0 , 256 ) ;
CUtlVector < short > lightObjects ( 0 , 256 ) ;
CUtlVector < short > shadowObjects ( 0 , 64 ) ;
CUtlVector < rdecalmodel_t > decalObjects ( 0 , 64 ) ;
CUtlVector < LightingState_t > lightStates ( 0 , 256 ) ;
bool bForceCubemap = r_showenvcubemap . GetBool ( ) ;
int drawnCount = 0 ;
int forcedLodSetting = r_lod . GetInt ( ) ;
if ( r_staticprop_lod . GetInt ( ) > = 0 )
{
forcedLodSetting = r_staticprop_lod . GetInt ( ) ;
}
// build list of objects and unique models
for ( int i = 0 ; i < count ; i + + )
{
drawnCount + + ;
// UNDONE: This is a perf hit in some scenes! Use a hash?
int modelIndex = FindModel ( modelList , pProps [ i ] . pModel ) ;
if ( modelIndex < 0 )
{
modelIndex = modelList . AddToTail ( ) ;
modelList [ modelIndex ] . pModel = pProps [ i ] . pModel ;
modelList [ modelIndex ] . pStudioHWData = g_pMDLCache - > GetHardwareData ( modelList [ modelIndex ] . pModel - > studio ) ;
}
if ( modelList [ modelIndex ] . pStudioHWData )
{
robject_t obj ;
obj . pMatrix = pProps [ i ] . pModelToWorld ;
obj . pRenderable = pProps [ i ] . pRenderable ;
obj . modelIndex = modelIndex ;
obj . instance = pProps [ i ] . instance ;
obj . skin = pProps [ i ] . skin ;
obj . lod = 0 ;
obj . pColorMeshes = NULL ;
obj . pEnvCubeMap = NULL ;
obj . lightIndex = - 1 ;
obj . pLightingOrigin = pProps [ i ] . pLightingOrigin ;
objectList . InsertNoSort ( obj ) ;
}
}
// process list of unique models
int lodStart = 0 ;
for ( int i = 0 ; i < modelList . Count ( ) ; i + + )
{
const model_t * pModel = modelList [ i ] . pModel ;
Assert ( modelloader - > IsLoaded ( pModel ) ) ;
unsigned int flags = pModel - > flags ;
modelList [ i ] . pStudioHdr = g_pMDLCache - > GetStudioHdr ( pModel - > studio ) ;
modelList [ i ] . maxArea = 1.0f ;
modelList [ i ] . lodStart = lodStart ;
modelList [ i ] . lodCount = modelList [ i ] . pStudioHWData ? modelList [ i ] . pStudioHWData - > m_NumLODs : 0 ;
bool bBumpMapped = ( flags & MODELFLAG_STUDIOHDR_USES_BUMPMAPPING ) ! = 0 ;
modelList [ i ] . bStaticLighting = ( ( modelList [ i ] . pStudioHdr - > flags & STUDIOHDR_FLAGS_STATIC_PROP ) ! = 0 ) & & ! bBumpMapped ;
modelList [ i ] . bVertexLit = ( flags & MODELFLAG_VERTEXLIT ) ! = 0 ;
modelList [ i ] . bNeedsCubemap = ( flags & MODELFLAG_STUDIOHDR_USES_ENV_CUBEMAP ) ! = 0 ;
lodStart + = modelList [ i ] . lodCount ;
}
// -1 is automatic lod
if ( forcedLodSetting < 0 )
{
// compute the lod of each object
for ( int i = 0 ; i < objectList . Count ( ) ; i + + )
{
Vector org ;
MatrixGetColumn ( * objectList [ i ] . pMatrix , 3 , org ) ;
float screenSize = pRenderContext - > ComputePixelWidthOfSphere ( org , 0.5f ) ;
const rmodel_t & model = modelList [ objectList [ i ] . modelIndex ] ;
float metric = model . pStudioHWData - > LODMetric ( screenSize ) ;
objectList [ i ] . lod = model . pStudioHWData - > GetLODForMetric ( metric ) ;
if ( objectList [ i ] . lod < model . pStudioHWData - > m_RootLOD )
{
objectList [ i ] . lod = model . pStudioHWData - > m_RootLOD ;
}
modelList [ objectList [ i ] . modelIndex ] . maxArea = max ( modelList [ objectList [ i ] . modelIndex ] . maxArea , screenSize ) ;
}
}
else
{
// force the lod of each object
for ( int i = 0 ; i < objectList . Count ( ) ; i + + )
{
const rmodel_t & model = modelList [ objectList [ i ] . modelIndex ] ;
objectList [ i ] . lod = clamp ( forcedLodSetting , model . pStudioHWData - > m_RootLOD , model . lodCount - 1 ) ;
}
}
// UNDONE: Don't sort if rendering transparent objects - for now this isn't called in the transparent case
// sort by model, then by lod
objectList . SetLessContext ( static_cast < void * > ( modelList . Base ( ) ) ) ;
objectList . RedoSort ( true ) ;
ICallQueue * pCallQueue = pRenderContext - > GetCallQueue ( ) ;
// now build out the lighting states
if ( ! bShadowDepth )
{
for ( int i = 0 ; i < objectList . Count ( ) ; i + + )
{
robject_t & obj = objectList [ i ] ;
rmodel_t & model = modelList [ obj . modelIndex ] ;
bool bStaticLighting = ( model . bStaticLighting & & obj . instance ! = MODEL_INSTANCE_INVALID ) ;
bool bVertexLit = model . bVertexLit ;
bool bNeedsEnvCubemap = bForceCubemap | | model . bNeedsCubemap ;
bool bHasDecals = ( m_ModelInstances [ obj . instance ] . m_DecalHandle ! = STUDIORENDER_DECAL_INVALID ) ? true : false ;
LightingState_t * pDecalLightState = NULL ;
if ( bHasDecals )
{
rdecalmodel_t decalModel ;
decalModel . lightIndex = lightStates . AddToTail ( ) ;
pDecalLightState = & lightStates [ decalModel . lightIndex ] ;
decalModel . objectIndex = i ;
decalObjects . AddToTail ( decalModel ) ;
}
// for now we skip models that have shadows - will update later to include them in a post-pass
if ( g_pShadowMgr - > ModelHasShadows ( obj . instance ) )
{
shadowObjects . AddToTail ( i ) ;
}
// get the static lighting from the cache
DataCacheHandle_t hColorMeshData = DC_INVALID_HANDLE ;
if ( bStaticLighting )
{
// have static lighting, get from cache
hColorMeshData = m_ModelInstances [ obj . instance ] . m_ColorMeshHandle ;
CColorMeshData * pColorMeshData = CacheGet ( hColorMeshData ) ;
if ( ! pColorMeshData | | pColorMeshData - > m_bNeedsRetry )
{
// color meshes are not present, try to re-establish
if ( UpdateStaticPropColorData ( obj . pRenderable - > GetIClientUnknown ( ) , obj . instance ) )
{
pColorMeshData = CacheGet ( hColorMeshData ) ;
}
else if ( ! pColorMeshData | | ! pColorMeshData - > m_bNeedsRetry )
{
// can't draw
continue ;
}
}
if ( pColorMeshData & & ( pColorMeshData - > m_bColorMeshValid | | pColorMeshData - > m_bColorTextureValid ) )
{
obj . pColorMeshes = pColorMeshData - > m_pMeshInfos ;
if ( pColorMeshData - > m_bColorTextureValid & & ! pColorMeshData - > m_bColorTextureCreated )
{
CreateLightmapsFromData ( pColorMeshData ) ;
}
if ( pCallQueue )
{
if ( CacheLock ( hColorMeshData ) ) // CacheCreate above will call functions that won't take place until later. If color mesh isn't used right away, it could get dumped
{
pCallQueue - > QueueCall ( this , & CModelRender : : CacheUnlock , hColorMeshData ) ;
}
}
}
else
{
// failed, draw without static lighting
bStaticLighting = false ;
}
}
// Get lighting from ambient light sources and radiosity bounces
// also set up the env_cubemap from the light cache if necessary.
if ( ( bVertexLit | | bNeedsEnvCubemap ) )
{
// See if we're using static lighting
LightCacheHandle_t * pLightCache = NULL ;
ITexture * pEnvCubemapTexture = NULL ;
if ( obj . instance ! = MODEL_INSTANCE_INVALID )
{
if ( ( m_ModelInstances [ obj . instance ] . m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING ) & & m_ModelInstances [ obj . instance ] . m_LightCacheHandle )
{
pLightCache = & m_ModelInstances [ obj . instance ] . m_LightCacheHandle ;
}
}
Assert ( pLightCache ) ;
LightingState_t lightingState ;
LightingState_t * pState = & lightingState ;
if ( pLightCache )
{
// dx8 and dx9 case. . .hardware can do baked lighting plus other dynamic lighting
// We already have the static part baked into a color mesh, so just get the dynamic stuff.
if ( ! bStaticLighting | | StaticLightCacheAffectedByDynamicLight ( * pLightCache ) )
{
pState = LightcacheGetStatic ( * pLightCache , & pEnvCubemapTexture , LIGHTCACHEFLAGS_STATIC | LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE ) ;
Assert ( pState - > numlights > = 0 & & pState - > numlights < = MAXLOCALLIGHTS ) ;
}
else
{
pState = LightcacheGetStatic ( * pLightCache , & pEnvCubemapTexture , LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE ) ;
Assert ( pState - > numlights > = 0 & & pState - > numlights < = MAXLOCALLIGHTS ) ;
}
if ( bHasDecals )
{
for ( int iCube = 0 ; iCube < 6 ; + + iCube )
{
pDecalLightState - > r_boxcolor [ iCube ] = m_ModelInstances [ obj . instance ] . m_AmbientLightingState . r_boxcolor [ iCube ] + pState - > r_boxcolor [ iCube ] ;
}
pDecalLightState - > CopyLocalLights ( m_ModelInstances [ obj . instance ] . m_AmbientLightingState ) ;
pDecalLightState - > AddAllLocalLights ( * pState ) ;
}
}
else // !pLightcache
{
// UNDONE: is it possible to end up here in the static prop case?
Vector vLightingOrigin = * obj . pLightingOrigin ;
int lightCacheFlags = bStaticLighting ? ( LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE )
: ( LIGHTCACHEFLAGS_STATIC | LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE | LIGHTCACHEFLAGS_ALLOWFAST ) ;
LightcacheGetDynamic_Stats stats ;
pEnvCubemapTexture = LightcacheGetDynamic ( vLightingOrigin , lightingState ,
stats , lightCacheFlags , false ) ;
Assert ( lightingState . numlights > = 0 & & lightingState . numlights < = MAXLOCALLIGHTS ) ;
pState = & lightingState ;
if ( bHasDecals )
{
LightcacheGetDynamic_Stats tempStats ;
LightcacheGetDynamic ( vLightingOrigin , * pDecalLightState , tempStats ,
LIGHTCACHEFLAGS_STATIC | LIGHTCACHEFLAGS_DYNAMIC | LIGHTCACHEFLAGS_LIGHTSTYLE | LIGHTCACHEFLAGS_ALLOWFAST ) ;
}
}
if ( bNeedsEnvCubemap & & pEnvCubemapTexture )
{
obj . pEnvCubeMap = pEnvCubemapTexture ;
}
if ( bVertexLit )
{
// if we have any real lighting state we need to save it for this object
if ( pState - > numlights | | pState - > HasAmbientColors ( ) )
{
obj . lightIndex = lightStates . AddToTail ( * pState ) ;
lightObjects . AddToTail ( i ) ;
}
}
}
}
}
// now render the baked lighting props with no lighting state
float color [ 3 ] ;
color [ 0 ] = color [ 1 ] = color [ 2 ] = 1.0f ;
g_pStudioRender - > SetColorModulation ( color ) ;
g_pStudioRender - > SetAlphaModulation ( 1.0f ) ;
g_pStudioRender - > SetViewState ( CurrentViewOrigin ( ) , CurrentViewRight ( ) , CurrentViewUp ( ) , CurrentViewForward ( ) ) ;
pRenderContext - > MatrixMode ( MATERIAL_MODEL ) ;
pRenderContext - > PushMatrix ( ) ;
pRenderContext - > LoadIdentity ( ) ;
g_pStudioRender - > ClearAllShadows ( ) ;
pRenderContext - > DisableAllLocalLights ( ) ;
DrawModelInfo_t info ;
for ( int i = 0 ; i < 6 ; i + + )
info . m_vecAmbientCube [ i ] . Init ( ) ;
g_pStudioRender - > SetAmbientLightColors ( info . m_vecAmbientCube ) ;
pRenderContext - > SetAmbientLight ( 0.0 , 0.0 , 0.0 ) ;
info . m_nLocalLightCount = 0 ;
info . m_bStaticLighting = false ;
int drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL | STUDIORENDER_DRAW_STATIC_LIGHTING ;
if ( bShadowDepth )
drawFlags | = STUDIO_SHADOWDEPTHTEXTURE ;
info . m_Decals = STUDIORENDER_DECAL_INVALID ;
info . m_Body = 0 ;
info . m_HitboxSet = 0 ;
for ( int i = 0 ; i < objectList . Count ( ) ; i + + )
{
robject_t & obj = objectList [ i ] ;
if ( obj . lightIndex > = 0 )
continue ;
rmodel_t & model = modelList [ obj . modelIndex ] ;
if ( obj . pEnvCubeMap )
{
pRenderContext - > BindLocalCubemap ( obj . pEnvCubeMap ) ;
}
info . m_pStudioHdr = model . pStudioHdr ;
info . m_pHardwareData = model . pStudioHWData ;
info . m_Skin = obj . skin ;
info . m_pClientEntity = static_cast < void * > ( obj . pRenderable ) ;
info . m_Lod = obj . lod ;
info . m_pColorMeshes = obj . pColorMeshes ;
g_pStudioRender - > DrawModelStaticProp ( info , * obj . pMatrix , drawFlags ) ;
}
// now render the vertex lit props
int nLocalLightCount = 0 ;
LightDesc_t localLightDescs [ 4 ] ;
drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL | STUDIORENDER_DRAW_STATIC_LIGHTING ;
if ( lightObjects . Count ( ) )
{
for ( int i = 0 ; i < lightObjects . Count ( ) ; i + + )
{
robject_t & obj = objectList [ lightObjects [ i ] ] ;
rmodel_t & model = modelList [ obj . modelIndex ] ;
if ( obj . pEnvCubeMap )
{
pRenderContext - > BindLocalCubemap ( obj . pEnvCubeMap ) ;
}
LightingState_t * pState = & lightStates [ obj . lightIndex ] ;
g_pStudioRender - > SetAmbientLightColors ( pState - > r_boxcolor ) ;
pRenderContext - > SetLightingOrigin ( * obj . pLightingOrigin ) ;
R_SetNonAmbientLightingState ( pState - > numlights , pState - > locallight , & nLocalLightCount , localLightDescs , true ) ;
info . m_pStudioHdr = model . pStudioHdr ;
info . m_pHardwareData = model . pStudioHWData ;
info . m_Skin = obj . skin ;
info . m_pClientEntity = static_cast < void * > ( obj . pRenderable ) ;
info . m_Lod = obj . lod ;
info . m_pColorMeshes = obj . pColorMeshes ;
g_pStudioRender - > DrawModelStaticProp ( info , * obj . pMatrix , drawFlags ) ;
}
}
if ( ! IsX360 ( ) & & ( r_flashlight_version2 . GetInt ( ) = = 0 ) & & shadowObjects . Count ( ) )
{
drawFlags = STUDIORENDER_DRAW_ENTIRE_MODEL ;
for ( int i = 0 ; i < shadowObjects . Count ( ) ; i + + )
{
// draw just the shadows!
robject_t & obj = objectList [ shadowObjects [ i ] ] ;
rmodel_t & model = modelList [ obj . modelIndex ] ;
g_pShadowMgr - > SetModelShadowState ( obj . instance ) ;
info . m_pStudioHdr = model . pStudioHdr ;
info . m_pHardwareData = model . pStudioHWData ;
info . m_Skin = obj . skin ;
info . m_pClientEntity = static_cast < void * > ( obj . pRenderable ) ;
info . m_Lod = obj . lod ;
info . m_pColorMeshes = obj . pColorMeshes ;
g_pStudioRender - > DrawStaticPropShadows ( info , * obj . pMatrix , drawFlags ) ;
}
g_pStudioRender - > ClearAllShadows ( ) ;
}
for ( int i = 0 ; i < decalObjects . Count ( ) ; i + + )
{
// draw just the decals!
robject_t & obj = objectList [ decalObjects [ i ] . objectIndex ] ;
rmodel_t & model = modelList [ obj . modelIndex ] ;
LightingState_t * pState = & lightStates [ decalObjects [ i ] . lightIndex ] ;
g_pStudioRender - > SetAmbientLightColors ( pState - > r_boxcolor ) ;
pRenderContext - > SetLightingOrigin ( * obj . pLightingOrigin ) ;
R_SetNonAmbientLightingState ( pState - > numlights , pState - > locallight , & nLocalLightCount , localLightDescs , true ) ;
info . m_pStudioHdr = model . pStudioHdr ;
info . m_pHardwareData = model . pStudioHWData ;
info . m_Decals = m_ModelInstances [ obj . instance ] . m_DecalHandle ;
info . m_Skin = obj . skin ;
info . m_pClientEntity = static_cast < void * > ( obj . pRenderable ) ;
info . m_Lod = obj . lod ;
info . m_pColorMeshes = obj . pColorMeshes ;
g_pStudioRender - > DrawStaticPropDecals ( info , * obj . pMatrix ) ;
}
// Restore the matrices if we're skinning
pRenderContext - > MatrixMode ( MATERIAL_MODEL ) ;
pRenderContext - > PopMatrix ( ) ;
return drawnCount ;
# else // SWDS
return 0 ;
# endif // SWDS
}
//-----------------------------------------------------------------------------
// Shadow rendering
//-----------------------------------------------------------------------------
matrix3x4_t * CModelRender : : DrawModelShadowSetup ( IClientRenderable * pRenderable , int body , int skin , DrawModelInfo_t * pInfo , matrix3x4_t * pCustomBoneToWorld )
{
# ifndef SWDS
DrawModelInfo_t & info = * pInfo ;
static ConVar r_shadowlod ( " r_shadowlod " , " -1 " ) ;
static ConVar r_shadowlodbias ( " r_shadowlodbias " , " 2 " ) ;
model_t const * pModel = pRenderable - > GetModel ( ) ;
if ( ! pModel )
return NULL ;
// FIXME: Make brush shadows work
if ( pModel - > type ! = mod_studio )
return NULL ;
Assert ( modelloader - > IsLoaded ( pModel ) & & ( pModel - > type = = mod_studio ) ) ;
info . m_pStudioHdr = g_pMDLCache - > GetStudioHdr ( pModel - > studio ) ;
info . m_pColorMeshes = NULL ;
// quick exit
if ( info . m_pStudioHdr - > numbodyparts = = 0 )
return NULL ;
Assert ( pRenderable ) ;
info . m_pHardwareData = g_pMDLCache - > GetHardwareData ( pModel - > studio ) ;
if ( ! info . m_pHardwareData )
return NULL ;
info . m_Decals = STUDIORENDER_DECAL_INVALID ;
info . m_Skin = skin ;
info . m_Body = body ;
info . m_pClientEntity = ( void * ) pRenderable ;
info . m_HitboxSet = 0 ;
info . m_Lod = r_shadowlod . GetInt ( ) ;
// If the .mdl has a shadowlod, force the use of that one instead
if ( info . m_pStudioHdr - > flags & STUDIOHDR_FLAGS_HASSHADOWLOD )
{
info . m_Lod = info . m_pHardwareData - > m_NumLODs - 1 ;
}
else if ( info . m_Lod = = USESHADOWLOD )
{
int lastlod = info . m_pHardwareData - > m_NumLODs - 1 ;
info . m_Lod = lastlod ;
}
else if ( info . m_Lod < 0 )
{
CMatRenderContextPtr pRenderContext ( materials ) ;
// Compute the shadow LOD...
float factor = r_shadowlodbias . GetFloat ( ) > 0.0f ? 1.0f / r_shadowlodbias . GetFloat ( ) : 1.0f ;
float screenSize = factor * pRenderContext - > ComputePixelWidthOfSphere ( pRenderable - > GetRenderOrigin ( ) , 0.5f ) ;
info . m_Lod = g_pStudioRender - > ComputeModelLod ( info . m_pHardwareData , screenSize ) ;
info . m_Lod = info . m_pHardwareData - > m_NumLODs - 2 ;
if ( info . m_Lod < 0 )
{
info . m_Lod = 0 ;
}
}
// clamp to root lod
if ( info . m_Lod < info . m_pHardwareData - > m_RootLOD )
{
info . m_Lod = info . m_pHardwareData - > m_RootLOD ;
}
matrix3x4_t * pBoneToWorld = pCustomBoneToWorld ;
if ( ! pBoneToWorld )
{
pBoneToWorld = g_pStudioRender - > LockBoneMatrices ( info . m_pStudioHdr - > numbones ) ;
}
const bool bOk = pRenderable - > SetupBones ( pBoneToWorld , info . m_pStudioHdr - > numbones , BONE_USED_BY_VERTEX_AT_LOD ( info . m_Lod ) , cl . GetTime ( ) ) ;
g_pStudioRender - > UnlockBoneMatrices ( ) ;
if ( ! bOk )
return NULL ;
return pBoneToWorld ;
# else
return NULL ;
# endif
}
void CModelRender : : DrawModelShadow ( IClientRenderable * pRenderable , const DrawModelInfo_t & info , matrix3x4_t * pBoneToWorld )
{
# ifndef SWDS
// Needed because we don't call SetupWeights
g_pStudioRender - > SetEyeViewTarget ( info . m_pStudioHdr , info . m_Body , vec3_origin ) ;
// Color + alpha modulation
Vector white ( 1 , 1 , 1 ) ;
g_pStudioRender - > SetColorModulation ( white . Base ( ) ) ;
g_pStudioRender - > SetAlphaModulation ( 1.0f ) ;
if ( ( info . m_pStudioHdr - > flags & STUDIOHDR_FLAGS_USE_SHADOWLOD_MATERIALS ) = = 0 )
{
g_pStudioRender - > ForcedMaterialOverride ( g_pMaterialShadowBuild , OVERRIDE_BUILD_SHADOWS ) ;
}
g_pStudioRender - > DrawModel ( NULL , info , pBoneToWorld , NULL , NULL , pRenderable - > GetRenderOrigin ( ) ,
STUDIORENDER_DRAW_NO_SHADOWS | STUDIORENDER_DRAW_ENTIRE_MODEL | STUDIORENDER_DRAW_NO_FLEXES ) ;
g_pStudioRender - > ForcedMaterialOverride ( 0 ) ;
# endif
}
void CModelRender : : SetViewTarget ( const CStudioHdr * pStudioHdr , int nBodyIndex , const Vector & target )
{
g_pStudioRender - > SetEyeViewTarget ( pStudioHdr - > GetRenderHdr ( ) , nBodyIndex , target ) ;
}
void CModelRender : : InitColormeshParams ( ModelInstance_t & instance , studiohwdata_t * pStudioHWData , colormeshparams_t * pColorMeshParams )
{
pColorMeshParams - > m_nMeshes = 0 ;
pColorMeshParams - > m_nTotalVertexes = 0 ;
pColorMeshParams - > m_pPooledVBAllocator = NULL ;
if ( ( instance . m_nFlags & MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR ) & &
g_pMaterialSystemHardwareConfig - > SupportsStreamOffset ( ) & &
( r_proplightingpooling . GetInt ( ) = = 1 ) )
{
// Color meshes can be allocated in a shared pool for static props
// (saves memory on X360 due to 4-KB VB alignment)
pColorMeshParams - > m_pPooledVBAllocator = ( IPooledVBAllocator * ) & m_colorMeshVBAllocator ;
}
for ( int lodID = pStudioHWData - > m_RootLOD ; lodID < pStudioHWData - > m_NumLODs ; lodID + + )
{
studioloddata_t * pLOD = & pStudioHWData - > m_pLODs [ lodID ] ;
for ( int meshID = 0 ; meshID < pStudioHWData - > m_NumStudioMeshes ; meshID + + )
{
studiomeshdata_t * pMesh = & pLOD - > m_pMeshData [ meshID ] ;
for ( int groupID = 0 ; groupID < pMesh - > m_NumGroup ; groupID + + )
{
pColorMeshParams - > m_nVertexes [ pColorMeshParams - > m_nMeshes + + ] = pMesh - > m_pMeshGroup [ groupID ] . m_NumVertices ;
Assert ( pColorMeshParams - > m_nMeshes < = ARRAYSIZE ( pColorMeshParams - > m_nVertexes ) ) ;
pColorMeshParams - > m_nTotalVertexes + = pMesh - > m_pMeshGroup [ groupID ] . m_NumVertices ;
}
}
}
}
//-----------------------------------------------------------------------------
// Allocates the static prop color data meshes
//-----------------------------------------------------------------------------
// FIXME? : Move this to StudioRender?
CColorMeshData * CModelRender : : FindOrCreateStaticPropColorData ( ModelInstanceHandle_t handle )
{
if ( handle = = MODEL_INSTANCE_INVALID | | ! g_pMaterialSystemHardwareConfig - > SupportsColorOnSecondStream ( ) )
{
// the card can't support it
return NULL ;
}
ModelInstance_t & instance = m_ModelInstances [ handle ] ;
CColorMeshData * pColorMeshData = CacheGet ( instance . m_ColorMeshHandle ) ;
if ( pColorMeshData )
{
// found in cache
return pColorMeshData ;
}
if ( ! instance . m_pModel )
{
return NULL ;
}
Assert ( modelloader - > IsLoaded ( instance . m_pModel ) & & ( instance . m_pModel - > type = = mod_studio ) ) ;
studiohwdata_t * pStudioHWData = g_pMDLCache - > GetHardwareData ( instance . m_pModel - > studio ) ;
Assert ( pStudioHWData ) ;
if ( ! pStudioHWData )
return NULL ;
colormeshparams_t params ;
InitColormeshParams ( instance , pStudioHWData , & params ) ;
if ( params . m_nMeshes < = 0 )
{
// nothing to create
return NULL ;
}
// create the meshes
params . m_fnHandle = instance . m_pModel - > fnHandle ;
instance . m_ColorMeshHandle = CacheCreate ( params ) ;
ProtectColorDataIfQueued ( instance . m_ColorMeshHandle ) ;
pColorMeshData = CacheGet ( instance . m_ColorMeshHandle ) ;
return pColorMeshData ;
}
//-----------------------------------------------------------------------------
// Allocates the static prop color data meshes
//-----------------------------------------------------------------------------
// FIXME? : Move this to StudioRender?
void CModelRender : : ProtectColorDataIfQueued ( DataCacheHandle_t hColorMesh )
{
if ( hColorMesh ! = DC_INVALID_HANDLE )
{
CMatRenderContextPtr pRenderContext ( materials ) ;
ICallQueue * pCallQueue = pRenderContext - > GetCallQueue ( ) ;
if ( pCallQueue )
{
if ( CacheLock ( hColorMesh ) ) // CacheCreate above will call functions that won't take place until later. If color mesh isn't used right away, it could get dumped
{
pCallQueue - > QueueCall ( this , & CModelRender : : CacheUnlock , hColorMesh ) ;
}
}
}
}
//-----------------------------------------------------------------------------
// Old-style computation of vertex lighting ( Currently In Use )
//-----------------------------------------------------------------------------
void CModelRender : : ComputeModelVertexLightingOld ( mstudiomodel_t * pModel ,
matrix3x4_t & matrix , const LightingState_t & lightingState , color24 * pLighting ,
bool bUseConstDirLighting , float flConstDirLightAmount )
{
Vector worldPos , worldNormal , destColor ;
int nNumLightDesc ;
LightDesc_t lightDesc [ MAXLOCALLIGHTS ] ;
LightingState_t * pLightingState ;
pLightingState = ( LightingState_t * ) & lightingState ;
// build the lighting descriptors
R_SetNonAmbientLightingState ( pLightingState - > numlights , pLightingState - > locallight , & nNumLightDesc , lightDesc , false ) ;
const thinModelVertices_t * thinVertData = NULL ;
const mstudio_modelvertexdata_t * vertData = pModel - > GetVertexData ( ) ;
mstudiovertex_t * pFatVerts = NULL ;
if ( vertData )
{
pFatVerts = vertData - > Vertex ( 0 ) ;
}
else
{
thinVertData = pModel - > GetThinVertexData ( ) ;
if ( ! thinVertData )
return ;
}
bool bHasSSE = MathLib_SSEEnabled ( ) ;
// light all vertexes
for ( int i = 0 ; i < pModel - > numvertices ; + + i )
{
if ( vertData )
{
# ifdef _WIN32
if ( bHasSSE )
{
// hint the next vertex
// data is loaded with one extra vertex for read past
# if !defined( _X360 ) // X360TBD
_mm_prefetch ( ( char * ) & pFatVerts [ i + 1 ] , _MM_HINT_T0 ) ;
# endif
}
# endif
VectorTransform ( pFatVerts [ i ] . m_vecPosition , matrix , worldPos ) ;
VectorRotate ( pFatVerts [ i ] . m_vecNormal , matrix , worldNormal ) ;
}
else
{
Vector position ;
Vector normal ;
thinVertData - > GetModelPosition ( pModel , i , & position ) ;
thinVertData - > GetModelNormal ( pModel , i , & normal ) ;
VectorTransform ( position , matrix , worldPos ) ;
VectorRotate ( normal , matrix , worldNormal ) ;
}
if ( bUseConstDirLighting )
{
g_pStudioRender - > ComputeLightingConstDirectional ( pLightingState - > r_boxcolor ,
nNumLightDesc , lightDesc , worldPos , worldNormal , destColor , flConstDirLightAmount ) ;
}
else
{
g_pStudioRender - > ComputeLighting ( pLightingState - > r_boxcolor ,
nNumLightDesc , lightDesc , worldPos , worldNormal , destColor ) ;
}
// to gamma space
destColor [ 0 ] = LinearToVertexLight ( destColor [ 0 ] ) ;
destColor [ 1 ] = LinearToVertexLight ( destColor [ 1 ] ) ;
destColor [ 2 ] = LinearToVertexLight ( destColor [ 2 ] ) ;
Assert ( ( destColor [ 0 ] > = 0.0f ) & & ( destColor [ 0 ] < = 1.0f ) ) ;
Assert ( ( destColor [ 1 ] > = 0.0f ) & & ( destColor [ 1 ] < = 1.0f ) ) ;
Assert ( ( destColor [ 2 ] > = 0.0f ) & & ( destColor [ 2 ] < = 1.0f ) ) ;
pLighting [ i ] . r = FastFToC ( destColor [ 0 ] ) ;
pLighting [ i ] . g = FastFToC ( destColor [ 1 ] ) ;
pLighting [ i ] . b = FastFToC ( destColor [ 2 ] ) ;
}
}
//-----------------------------------------------------------------------------
// New-style computation of vertex lighting ( Not Used Yet )
//-----------------------------------------------------------------------------
void CModelRender : : ComputeModelVertexLighting ( IHandleEntity * pProp ,
mstudiomodel_t * pModel , OptimizedModel : : ModelLODHeader_t * pVtxLOD ,
matrix3x4_t & matrix , Vector4D * pTempMem , color24 * pLighting )
{
# ifndef SWDS
if ( IsX360 ( ) )
return ;
int i ;
unsigned char * pInSolid = ( unsigned char * ) stackalloc ( ( ( pModel - > numvertices + 7 ) > > 3 ) * sizeof ( unsigned char ) ) ;
Vector worldPos , worldNormal ;
const mstudio_modelvertexdata_t * vertData = pModel - > GetVertexData ( ) ;
Assert ( vertData ) ;
if ( ! vertData )
return ;
for ( i = 0 ; i < pModel - > numvertices ; + + i )
{
const Vector & pos = * vertData - > Position ( i ) ;
const Vector & normal = * vertData - > Normal ( i ) ;
VectorTransform ( pos , matrix , worldPos ) ;
VectorRotate ( normal , matrix , worldNormal ) ;
bool bNonSolid = ComputeVertexLightingFromSphericalSamples ( worldPos , worldNormal , pProp , & ( pTempMem [ i ] . AsVector3D ( ) ) ) ;
int nByte = i > > 3 ;
int nBit = i & 0x7 ;
if ( bNonSolid )
{
pTempMem [ i ] . w = 1.0f ;
pInSolid [ nByte ] & = ~ ( 1 < < nBit ) ;
}
else
{
pTempMem [ i ] . Init ( ) ;
pInSolid [ nByte ] | = ( 1 < < nBit ) ;
}
}
// Must iterate over each triangle to average out the colors for those
// vertices in solid.
// Iterate over all the meshes....
for ( int meshID = 0 ; meshID < pModel - > nummeshes ; + + meshID )
{
Assert ( pModel - > nummeshes = = pVtxLOD - > numMeshes ) ;
mstudiomesh_t * pMesh = pModel - > pMesh ( meshID ) ;
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( meshID ) ;
// Iterate over all strip groups.
for ( int stripGroupID = 0 ; stripGroupID < pVtxMesh - > numStripGroups ; + + stripGroupID )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( stripGroupID ) ;
// Iterate over all indices
Assert ( pStripGroup - > numIndices % 3 = = 0 ) ;
for ( i = 0 ; i < pStripGroup - > numIndices ; i + = 3 )
{
unsigned short nIndex1 = * pStripGroup - > pIndex ( i ) ;
unsigned short nIndex2 = * pStripGroup - > pIndex ( i + 1 ) ;
unsigned short nIndex3 = * pStripGroup - > pIndex ( i + 2 ) ;
int v [ 3 ] ;
v [ 0 ] = pStripGroup - > pVertex ( nIndex1 ) - > origMeshVertID + pMesh - > vertexoffset ;
v [ 1 ] = pStripGroup - > pVertex ( nIndex2 ) - > origMeshVertID + pMesh - > vertexoffset ;
v [ 2 ] = pStripGroup - > pVertex ( nIndex3 ) - > origMeshVertID + pMesh - > vertexoffset ;
Assert ( v [ 0 ] < pModel - > numvertices ) ;
Assert ( v [ 1 ] < pModel - > numvertices ) ;
Assert ( v [ 2 ] < pModel - > numvertices ) ;
bool bSolid [ 3 ] ;
bSolid [ 0 ] = ( pInSolid [ v [ 0 ] > > 3 ] & ( 1 < < ( v [ 0 ] & 0x7 ) ) ) ! = 0 ;
bSolid [ 1 ] = ( pInSolid [ v [ 1 ] > > 3 ] & ( 1 < < ( v [ 1 ] & 0x7 ) ) ) ! = 0 ;
bSolid [ 2 ] = ( pInSolid [ v [ 2 ] > > 3 ] & ( 1 < < ( v [ 2 ] & 0x7 ) ) ) ! = 0 ;
int nValidCount = 0 ;
int nAverage [ 3 ] ;
if ( ! bSolid [ 0 ] ) { nAverage [ nValidCount + + ] = v [ 0 ] ; }
if ( ! bSolid [ 1 ] ) { nAverage [ nValidCount + + ] = v [ 1 ] ; }
if ( ! bSolid [ 2 ] ) { nAverage [ nValidCount + + ] = v [ 2 ] ; }
if ( nValidCount = = 3 )
continue ;
Vector vecAverage ( 0 , 0 , 0 ) ;
for ( int j = 0 ; j < nValidCount ; + + j )
{
vecAverage + = pTempMem [ nAverage [ j ] ] . AsVector3D ( ) ;
}
if ( nValidCount ! = 0 )
{
vecAverage / = nValidCount ;
}
if ( bSolid [ 0 ] ) { pTempMem [ v [ 0 ] ] . AsVector3D ( ) + = vecAverage ; pTempMem [ v [ 0 ] ] . w + = 1.0f ; }
if ( bSolid [ 1 ] ) { pTempMem [ v [ 1 ] ] . AsVector3D ( ) + = vecAverage ; pTempMem [ v [ 1 ] ] . w + = 1.0f ; }
if ( bSolid [ 2 ] ) { pTempMem [ v [ 2 ] ] . AsVector3D ( ) + = vecAverage ; pTempMem [ v [ 2 ] ] . w + = 1.0f ; }
}
}
}
Vector destColor ;
for ( i = 0 ; i < pModel - > numvertices ; + + i )
{
if ( pTempMem [ i ] . w ! = 0.0f )
{
pTempMem [ i ] / = pTempMem [ i ] . w ;
}
destColor [ 0 ] = LinearToVertexLight ( pTempMem [ i ] [ 0 ] ) ;
destColor [ 1 ] = LinearToVertexLight ( pTempMem [ i ] [ 1 ] ) ;
destColor [ 2 ] = LinearToVertexLight ( pTempMem [ i ] [ 2 ] ) ;
ColorClampTruncate ( destColor ) ;
pLighting [ i ] . r = FastFToC ( destColor [ 0 ] ) ;
pLighting [ i ] . g = FastFToC ( destColor [ 1 ] ) ;
pLighting [ i ] . b = FastFToC ( destColor [ 2 ] ) ;
}
# endif
}
//-----------------------------------------------------------------------------
// Sanity check and setup the compiled color mesh for an optimal async load
// during runtime.
//-----------------------------------------------------------------------------
void CModelRender : : ValidateStaticPropColorData ( ModelInstanceHandle_t handle )
{
if ( ! r_proplightingfromdisk . GetBool ( ) )
{
return ;
}
ModelInstance_t * pInstance = & m_ModelInstances [ handle ] ;
IHandleEntity * pProp = pInstance - > m_pRenderable - > GetIClientUnknown ( ) ;
if ( ! g_pMaterialSystemHardwareConfig - > SupportsColorOnSecondStream ( ) | | ! StaticPropMgr ( ) - > IsStaticProp ( pProp ) )
{
// can't support it or not a static prop
return ;
}
if ( ! g_bLoadedMapHasBakedPropLighting | | StaticPropMgr ( ) - > PropHasBakedLightingDisabled ( pProp ) )
{
return ;
}
MEM_ALLOC_CREDIT ( ) ;
// fetch the header
CUtlBuffer utlBuf ;
char fileName [ MAX_PATH ] ;
2022-04-16 17:54:00 +08:00
if ( ! g_pMaterialSystemHardwareConfig - > GetHDREnabled ( ) | | g_bBakedPropLightingNoSeparateHDR )
2020-04-23 00:56:21 +08:00
{
Q_snprintf ( fileName , sizeof ( fileName ) , " sp_%d%s.vhv " , StaticPropMgr ( ) - > GetStaticPropIndex ( pProp ) , GetPlatformExt ( ) ) ;
}
else
2022-03-26 08:24:41 +08:00
{
2020-04-23 00:56:21 +08:00
Q_snprintf ( fileName , sizeof ( fileName ) , " sp_hdr_%d%s.vhv " , StaticPropMgr ( ) - > GetStaticPropIndex ( pProp ) , GetPlatformExt ( ) ) ;
}
if ( IsX360 ( ) )
{
DataCacheHandle_t hColorMesh = GetCachedStaticPropColorData ( fileName ) ;
if ( hColorMesh ! = DC_INVALID_HANDLE )
{
// already have it
pInstance - > m_ColorMeshHandle = hColorMesh ;
pInstance - > m_nFlags & = ~ MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD ;
pInstance - > m_nFlags | = MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR ;
return ;
}
}
if ( ! g_pFileSystem - > ReadFile ( fileName , " GAME " , utlBuf , sizeof ( HardwareVerts : : FileHeader_t ) , 0 ) )
{
// not available
return ;
}
studiohdr_t * pStudioHdr = g_pMDLCache - > GetStudioHdr ( pInstance - > m_pModel - > studio ) ;
HardwareVerts : : FileHeader_t * pVhvHdr = ( HardwareVerts : : FileHeader_t * ) utlBuf . Base ( ) ;
if ( pVhvHdr - > m_nVersion ! = VHV_VERSION | |
pVhvHdr - > m_nChecksum ! = ( unsigned int ) pStudioHdr - > checksum | |
pVhvHdr - > m_nVertexSize ! = 4 )
{
// out of sync
// mark for debug visualization
pInstance - > m_nFlags | = MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD ;
return ;
}
// async callback can safely stream data into targets
pInstance - > m_nFlags & = ~ MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD ;
pInstance - > m_nFlags | = MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR ;
}
//-----------------------------------------------------------------------------
// Async loader callback
// Called from async i/o thread - must spend minimal cycles in this context
//-----------------------------------------------------------------------------
void CModelRender : : StaticPropColorMeshCallback ( void * pContext , const void * pData , int numReadBytes , FSAsyncStatus_t asyncStatus )
{
// get our preserved data
Assert ( pContext ) ;
staticPropAsyncContext_t * pStaticPropContext = ( staticPropAsyncContext_t * ) pContext ;
HardwareVerts : : FileHeader_t * pVhvHdr ;
byte * pOriginalData = NULL ;
int numLightingComponents = 1 ;
if ( asyncStatus ! = FSASYNC_OK )
{
// any i/o error
goto cleanUp ;
}
if ( IsX360 ( ) )
{
// only the 360 has compressed VHV data
// the compressed data is after the header
byte * pCompressedData = ( byte * ) pData + sizeof ( HardwareVerts : : FileHeader_t ) ;
if ( CLZMA : : IsCompressed ( pCompressedData ) )
{
// create a buffer that matches the original
int actualSize = CLZMA : : GetActualSize ( pCompressedData ) ;
pOriginalData = ( byte * ) malloc ( sizeof ( HardwareVerts : : FileHeader_t ) + actualSize ) ;
// place the header, then uncompress directly after it
V_memcpy ( pOriginalData , pData , sizeof ( HardwareVerts : : FileHeader_t ) ) ;
int outputLength = CLZMA : : Uncompress ( pCompressedData , pOriginalData + sizeof ( HardwareVerts : : FileHeader_t ) ) ;
if ( outputLength ! = actualSize )
{
goto cleanUp ;
}
pData = pOriginalData ;
}
}
pVhvHdr = ( HardwareVerts : : FileHeader_t * ) pData ;
int startMesh ;
for ( startMesh = 0 ; startMesh < pVhvHdr - > m_nMeshes ; startMesh + + )
{
// skip past higher detail lod meshes that must be ignored
// find first mesh that matches desired lod
if ( pVhvHdr - > pMesh ( startMesh ) - > m_nLod = = pStaticPropContext - > m_nRootLOD )
{
break ;
}
}
int meshID ;
for ( meshID = startMesh ; meshID < pVhvHdr - > m_nMeshes ; meshID + + )
{
int numVertexes = pVhvHdr - > pMesh ( meshID ) - > m_nVertexes ;
if ( numVertexes ! = pStaticPropContext - > m_pColorMeshData - > m_pMeshInfos [ meshID - startMesh ] . m_nNumVerts )
{
// meshes are out of sync, discard data
break ;
}
int nID = meshID - startMesh ;
unsigned char * pIn = ( unsigned char * ) pVhvHdr - > pVertexBase ( meshID ) ;
unsigned char * pOut = NULL ;
CMeshBuilder meshBuilder ;
meshBuilder . Begin ( pStaticPropContext - > m_pColorMeshData - > m_pMeshInfos [ nID ] . m_pMesh , MATERIAL_HETEROGENOUS , numVertexes , 0 ) ;
if ( numLightingComponents > 1 )
{
pOut = reinterpret_cast < unsigned char * > ( const_cast < float * > ( meshBuilder . Normal ( ) ) ) ;
}
else
{
pOut = meshBuilder . Specular ( ) ;
}
# ifdef DX_TO_GL_ABSTRACTION
// OPENGL_SWAP_COLORS
for ( int i = 0 ; i < ( numVertexes * numLightingComponents ) ; i + + )
{
unsigned char red = * pIn + + ;
unsigned char green = * pIn + + ;
unsigned char blue = * pIn + + ;
* pOut + + = blue ;
* pOut + + = green ;
* pOut + + = red ;
* pOut + + = * pIn + + ; // Alpha goes straight across
}
# else
V_memcpy ( pOut , pIn , numVertexes * 4 * numLightingComponents ) ;
# endif
meshBuilder . End ( ) ;
}
cleanUp :
if ( IsX360 ( ) )
{
AUTO_LOCK ( m_CachedStaticPropMutex ) ;
// track the color mesh's datacache handle so that we can find it long after the model instance's are gone
// the static prop filenames are guaranteed uniquely decorated
m_CachedStaticPropColorData . Insert ( pStaticPropContext - > m_szFilenameVertex , pStaticPropContext - > m_ColorMeshHandle ) ;
// No support for lightmap textures on X360.
}
// mark as completed in single atomic operation
pStaticPropContext - > m_pColorMeshData - > m_bColorMeshValid = true ;
CacheUnlock ( pStaticPropContext - > m_ColorMeshHandle ) ;
delete pStaticPropContext ;
if ( pOriginalData )
{
free ( pOriginalData ) ;
}
}
//-----------------------------------------------------------------------------
// Async loader callback
// Called from async i/o thread - must spend minimal cycles in this context
//-----------------------------------------------------------------------------
void CModelRender : : StaticPropColorTexelCallback ( void * pContext , const void * pData , int numReadBytes , FSAsyncStatus_t asyncStatus )
{
// get our preserved data
Assert ( pContext ) ;
staticPropAsyncContext_t * pStaticPropContext = ( staticPropAsyncContext_t * ) pContext ;
HardwareTexels : : FileHeader_t * pVhtHdr ;
// This needs to be above the goto or clang complains "goto into protected scope."
bool anyTextures = false ;
if ( asyncStatus ! = FSASYNC_OK )
{
// any i/o error
goto cleanUp ;
}
pVhtHdr = ( HardwareTexels : : FileHeader_t * ) pData ;
int startMesh ;
for ( startMesh = 0 ; startMesh < pVhtHdr - > m_nMeshes ; startMesh + + )
{
// skip past higher detail lod meshes that must be ignored
// find first mesh that matches desired lod
if ( pVhtHdr - > pMesh ( startMesh ) - > m_nLod = = pStaticPropContext - > m_nRootLOD )
{
break ;
}
}
int meshID ;
for ( meshID = startMesh ; meshID < pVhtHdr - > m_nMeshes ; meshID + + )
{
const HardwareTexels : : MeshHeader_t * pMeshData = pVhtHdr - > pMesh ( meshID ) ;
// We can't create the real texture here because that's just how the material system works.
// So instead, squirrel away what we need for later.
ColorTexelsInfo_t * newCTI = new ColorTexelsInfo_t ;
newCTI - > m_nWidth = pMeshData - > m_nWidth ;
newCTI - > m_nHeight = pMeshData - > m_nHeight ;
newCTI - > m_nMipmapCount = ImageLoader : : GetNumMipMapLevels ( newCTI - > m_nWidth , newCTI - > m_nHeight ) ;
newCTI - > m_ImageFormat = ( ImageFormat ) pVhtHdr - > m_nTexelFormat ;
newCTI - > m_nByteCount = pVhtHdr - > pMesh ( meshID ) - > m_nBytes ;
newCTI - > m_pTexelData = new byte [ newCTI - > m_nByteCount ] ;
Q_memcpy ( newCTI - > m_pTexelData , pVhtHdr - > pTexelBase ( meshID ) , newCTI - > m_nByteCount ) ;
pStaticPropContext - > m_pColorMeshData - > m_pMeshInfos [ meshID - startMesh ] . m_pLightmapData = newCTI ;
Assert ( pStaticPropContext - > m_pColorMeshData - > m_pMeshInfos [ meshID - startMesh ] . m_pLightmap = = NULL ) ;
anyTextures = true ;
}
// This only gets set if we actually have texel data. Otherwise, it remains false.
pStaticPropContext - > m_pColorMeshData - > m_bColorTextureValid = anyTextures ;
cleanUp :
// mark as completed in single atomic operation
CacheUnlock ( pStaticPropContext - > m_ColorMeshHandle ) ;
delete pStaticPropContext ;
}
//-----------------------------------------------------------------------------
// Async loader callback
// Called from async i/o thread - must spend minimal cycles in this context
//-----------------------------------------------------------------------------
static void StaticPropColorMeshCallback ( const FileAsyncRequest_t & request , int numReadBytes , FSAsyncStatus_t asyncStatus )
{
s_ModelRender . StaticPropColorMeshCallback ( request . pContext , request . pData , numReadBytes , asyncStatus ) ;
}
//-----------------------------------------------------------------------------
// Async loader callback
// Called from async i/o thread - must spend minimal cycles in this context
//-----------------------------------------------------------------------------
static void StaticPropColorTexelCallback ( const FileAsyncRequest_t & request , int numReadBytes , FSAsyncStatus_t asyncStatus )
{
s_ModelRender . StaticPropColorTexelCallback ( request . pContext , request . pData , numReadBytes , asyncStatus ) ;
}
//-----------------------------------------------------------------------------
// Queued loader callback
// Called from async i/o thread - must spend minimal cycles in this context
//-----------------------------------------------------------------------------
static void QueuedLoaderCallback_PropLighting ( void * pContext , void * pContext2 , const void * pData , int nSize , LoaderError_t loaderError )
{
// translate error
FSAsyncStatus_t asyncStatus = ( loaderError = = LOADERERROR_NONE ? FSASYNC_OK : FSASYNC_ERR_READING ) ;
// mimic async i/o completion
s_ModelRender . StaticPropColorMeshCallback ( pContext , pData , nSize , asyncStatus ) ;
}
//-----------------------------------------------------------------------------
// Loads the serialized static prop color data.
// Returns false if legacy path should be used.
//-----------------------------------------------------------------------------
bool CModelRender : : LoadStaticPropColorData ( IHandleEntity * pProp , DataCacheHandle_t colorMeshHandle , studiohwdata_t * pStudioHWData )
{
if ( ! g_bLoadedMapHasBakedPropLighting | | ! r_proplightingfromdisk . GetBool ( ) )
{
return false ;
}
// lock the mesh memory during async transfer
// the color meshes should already have low quality data to be used during rendering
CColorMeshData * pColorMeshData = CacheLock ( colorMeshHandle ) ;
if ( ! pColorMeshData )
{
return false ;
}
if ( pColorMeshData - > m_hAsyncControlVertex | | pColorMeshData - > m_hAsyncControlTexel )
{
// load in progress, ignore additional request
// or already loaded, ignore until discarded from cache
CacheUnlock ( colorMeshHandle ) ;
return true ;
}
// each static prop has its own compiled color mesh
char fileName [ MAX_PATH ] ;
2022-04-16 17:54:00 +08:00
if ( ! g_pMaterialSystemHardwareConfig - > GetHDREnabled ( ) | | g_bBakedPropLightingNoSeparateHDR )
2020-04-23 00:56:21 +08:00
{
2022-04-16 17:54:00 +08:00
Q_snprintf ( fileName , sizeof ( fileName ) , " sp_%d%s.vhv " , StaticPropMgr ( ) - > GetStaticPropIndex ( pProp ) , GetPlatformExt ( ) ) ;
2020-04-23 00:56:21 +08:00
}
else
{
2022-04-16 17:54:00 +08:00
Q_snprintf ( fileName , sizeof ( fileName ) , " sp_hdr_%d%s.vhv " , StaticPropMgr ( ) - > GetStaticPropIndex ( pProp ) , GetPlatformExt ( ) ) ;
2020-04-23 00:56:21 +08:00
}
// mark as invalid, async callback will set upon completion
// prevents rendering during async transfer into locked mesh, otherwise d3drip
pColorMeshData - > m_bColorMeshValid = false ;
pColorMeshData - > m_bColorTextureValid = false ;
pColorMeshData - > m_bColorTextureCreated = false ;
// async load high quality lighting from file
// can't optimal async yet, because need flat ppColorMesh[], so use callback to distribute
// create our private context of data for the callback
staticPropAsyncContext_t * pContextVertex = new staticPropAsyncContext_t ;
pContextVertex - > m_nRootLOD = pStudioHWData - > m_RootLOD ;
pContextVertex - > m_nMeshes = pColorMeshData - > m_nMeshes ;
pContextVertex - > m_ColorMeshHandle = colorMeshHandle ;
pContextVertex - > m_pColorMeshData = pColorMeshData ;
V_strncpy ( pContextVertex - > m_szFilenameVertex , fileName , sizeof ( pContextVertex - > m_szFilenameVertex ) ) ;
if ( IsX360 ( ) & & g_pQueuedLoader - > IsMapLoading ( ) )
{
if ( ! g_pQueuedLoader - > ClaimAnonymousJob ( fileName , QueuedLoaderCallback_PropLighting , ( void * ) pContextVertex ) )
{
// not there as expected
// as a less optimal fallback during loading, issue as a standard queued loader job
LoaderJob_t loaderJob ;
loaderJob . m_pFilename = fileName ;
loaderJob . m_pPathID = " GAME " ;
loaderJob . m_pCallback = QueuedLoaderCallback_PropLighting ;
loaderJob . m_pContext = ( void * ) pContextVertex ;
loaderJob . m_Priority = LOADERPRIORITY_BEFOREPLAY ;
g_pQueuedLoader - > AddJob ( & loaderJob ) ;
}
return true ;
}
// async load the file
FileAsyncRequest_t fileRequest ;
fileRequest . pContext = ( void * ) pContextVertex ;
fileRequest . pfnCallback = : : StaticPropColorMeshCallback ;
fileRequest . pData = NULL ;
fileRequest . pszFilename = fileName ;
fileRequest . nOffset = 0 ;
fileRequest . flags = 0 ; // FSASYNC_FLAGS_SYNC;
fileRequest . nBytes = 0 ;
fileRequest . priority = - 1 ;
fileRequest . pszPathID = " GAME " ;
// This must be done before sending pContextVertex down
staticPropAsyncContext_t * pContextTexel = new staticPropAsyncContext_t ( * pContextVertex ) ;
// queue vertex data for async load
{
MEM_ALLOC_CREDIT ( ) ;
g_pFileSystem - > AsyncRead ( fileRequest , & pColorMeshData - > m_hAsyncControlVertex ) ;
}
Q_snprintf ( fileName , sizeof ( fileName ) , " texelslighting_%d.ppl " , StaticPropMgr ( ) - > GetStaticPropIndex ( pProp ) ) ;
V_strncpy ( pContextTexel - > m_szFilenameTexel , fileName , sizeof ( pContextTexel - > m_szFilenameTexel ) ) ;
// We are already locked, but we will unlock twice--so lock once more for the texel processing.
CacheLock ( colorMeshHandle ) ;
// queue texel data for async load
fileRequest . pContext = pContextTexel ;
fileRequest . pfnCallback = : : StaticPropColorTexelCallback ;
fileRequest . pData = NULL ;
fileRequest . pszFilename = fileName ; // This doesn't need to happen, but included for clarity.
{
MEM_ALLOC_CREDIT ( ) ;
g_pFileSystem - > AsyncRead ( fileRequest , & pColorMeshData - > m_hAsyncControlTexel ) ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Computes the static prop color data.
// Data calculation may be delayed if data is disk based.
// Returns FALSE if data not available or error. For retry polling pattern.
// Resturns TRUE if operation succesful or in progress (succeeds later).
//-----------------------------------------------------------------------------
bool CModelRender : : UpdateStaticPropColorData ( IHandleEntity * pProp , ModelInstanceHandle_t handle )
{
MDLCACHE_CRITICAL_SECTION_ ( g_pMDLCache ) ;
# ifndef SWDS
// find or allocate color meshes
CColorMeshData * pColorMeshData = FindOrCreateStaticPropColorData ( handle ) ;
if ( ! pColorMeshData )
{
return false ;
}
// HACK: on PC, VB creation can fail due to device loss
if ( IsPC ( ) & & pColorMeshData - > m_bHasInvalidVB )
{
// Don't retry until color data is flushed by device restore
pColorMeshData - > m_bColorMeshValid = false ;
pColorMeshData - > m_bNeedsRetry = false ;
return false ;
}
unsigned char debugColor [ 3 ] = { 0 } ;
bool bDebugColor = false ;
if ( r_debugrandomstaticlighting . GetBool ( ) )
{
// randomize with bright colors, skip black and white
// purposely not deterministic to catch bugs with excessive re-baking (i.e. disco)
Vector fRandomColor ;
int nColor = RandomInt ( 1 , 6 ) ;
fRandomColor . x = ( nColor > > 2 ) & 1 ;
fRandomColor . y = ( nColor > > 1 ) & 1 ;
fRandomColor . z = nColor & 1 ;
VectorNormalize ( fRandomColor ) ;
debugColor [ 0 ] = fRandomColor [ 0 ] * 255.0f ;
debugColor [ 1 ] = fRandomColor [ 1 ] * 255.0f ;
debugColor [ 2 ] = fRandomColor [ 2 ] * 255.0f ;
bDebugColor = true ;
}
// FIXME? : Move this to StudioRender?
ModelInstance_t & inst = m_ModelInstances [ handle ] ;
Assert ( inst . m_pModel ) ;
Assert ( modelloader - > IsLoaded ( inst . m_pModel ) & & ( inst . m_pModel - > type = = mod_studio ) ) ;
if ( r_proplightingfromdisk . GetInt ( ) = = 2 )
{
// This visualization debug mode is strictly to debug which static prop models have valid disk
// based lighting. There should be no red models, only green or yellow. Yellow models denote the legacy
// lower quality runtime baked lighting.
if ( inst . m_nFlags & MODEL_INSTANCE_DISKCOMPILED_COLOR_BAD )
{
// prop was compiled for static prop lighting, but out of sync
// bad disk data for model, show as red
debugColor [ 0 ] = 255.0f ;
debugColor [ 1 ] = 0 ;
debugColor [ 2 ] = 0 ;
}
else if ( inst . m_nFlags & MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR )
{
// valid disk data, show as green
debugColor [ 0 ] = 0 ;
debugColor [ 1 ] = 255.0f ;
debugColor [ 2 ] = 0 ;
}
else
{
// no disk based data, using runtime method, show as yellow
// identifies a prop that wasn't compiled for static prop lighting
debugColor [ 0 ] = 255.0f ;
debugColor [ 1 ] = 255.0f ;
debugColor [ 2 ] = 0 ;
}
bDebugColor = true ;
}
studiohdr_t * pStudioHdr = g_pMDLCache - > GetStudioHdr ( inst . m_pModel - > studio ) ;
studiohwdata_t * pStudioHWData = g_pMDLCache - > GetHardwareData ( inst . m_pModel - > studio ) ;
Assert ( pStudioHdr & & pStudioHWData ) ;
if ( ! bDebugColor & & ( inst . m_nFlags & MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR ) )
{
// start an async load on available higher quality disc based data
if ( LoadStaticPropColorData ( pProp , inst . m_ColorMeshHandle , pStudioHWData ) )
{
// async in progress, operation expected to succeed
// async callback handles finalization
return true ;
}
}
// lighting calculation path
// calculation may abort due to lack of async requested data, caller should retry
pColorMeshData - > m_bColorMeshValid = false ;
pColorMeshData - > m_bColorTextureValid = false ;
pColorMeshData - > m_bColorTextureCreated = false ;
pColorMeshData - > m_bNeedsRetry = true ;
if ( ! bDebugColor )
{
// vertexes must be available for lighting calculation
2022-02-23 19:50:30 +08:00
vertexFileHeader_t * pVertexHdr = g_pMDLCache - > GetVertexData ( VoidPtrToMDLHandle ( pStudioHdr - > VirtualModel ( ) ) ) ;
2020-04-23 00:56:21 +08:00
if ( ! pVertexHdr )
{
// data not available yet
return false ;
}
}
inst . m_nFlags | = MODEL_INSTANCE_HAS_COLOR_DATA ;
// calculate lighting, set for access to verts
m_pStudioHdr = pStudioHdr ;
// Sets the model transform state in g_pStudioRender
matrix3x4_t matrix ;
AngleMatrix ( inst . m_pRenderable - > GetRenderAngles ( ) , inst . m_pRenderable - > GetRenderOrigin ( ) , matrix ) ;
// Get static lighting only!! We'll add dynamic and lightstyles in in the vertex shader. . .
unsigned int lightCacheFlags = LIGHTCACHEFLAGS_STATIC ;
if ( ! g_pMaterialSystemHardwareConfig - > SupportsStaticPlusDynamicLighting ( ) )
{
// . . . unless we can't do anything but static or dynamic simulaneously. . then
// we'll bake the lightstyle info here.
lightCacheFlags | = LIGHTCACHEFLAGS_LIGHTSTYLE ;
}
LightingState_t lightingState ;
if ( ( inst . m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING ) & & inst . m_LightCacheHandle )
{
lightingState = * ( LightcacheGetStatic ( inst . m_LightCacheHandle , NULL , lightCacheFlags ) ) ;
}
else
{
// Choose the lighting origin
Vector entOrigin ;
R_ComputeLightingOrigin ( inst . m_pRenderable , pStudioHdr , matrix , entOrigin ) ;
LightcacheGetDynamic_Stats stats ;
LightcacheGetDynamic ( entOrigin , lightingState , stats , lightCacheFlags ) ;
}
// See if the studiohdr wants to use constant directional light, ie
// the surface normal plays no part in determining light intensity
bool bUseConstDirLighting = false ;
float flConstDirLightingAmount = 0.0 ;
if ( pStudioHdr - > flags & STUDIOHDR_FLAGS_CONSTANT_DIRECTIONAL_LIGHT_DOT )
{
bUseConstDirLighting = true ;
flConstDirLightingAmount = ( float ) ( pStudioHdr - > constdirectionallightdot ) / 255.0 ;
}
CUtlMemory < color24 > tmpLightingMem ;
// Iterate over every body part...
for ( int bodyPartID = 0 ; bodyPartID < pStudioHdr - > numbodyparts ; + + bodyPartID )
{
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( bodyPartID ) ;
// Iterate over every submodel...
for ( int modelID = 0 ; modelID < pBodyPart - > nummodels ; + + modelID )
{
mstudiomodel_t * pModel = pBodyPart - > pModel ( modelID ) ;
if ( pModel - > numvertices = = 0 )
continue ;
// Make sure we've got enough space allocated
tmpLightingMem . EnsureCapacity ( pModel - > numvertices ) ;
if ( ! bDebugColor )
{
// Compute lighting for each unique vertex in the model exactly once
ComputeModelVertexLightingOld ( pModel , matrix , lightingState , tmpLightingMem . Base ( ) , bUseConstDirLighting , flConstDirLightingAmount ) ;
}
else
{
for ( int i = 0 ; i < pModel - > numvertices ; i + + )
{
tmpLightingMem [ i ] . r = debugColor [ 0 ] ;
tmpLightingMem [ i ] . g = debugColor [ 1 ] ;
tmpLightingMem [ i ] . b = debugColor [ 2 ] ;
}
}
// distribute the lighting results to the mesh's vertexes
for ( int lodID = pStudioHWData - > m_RootLOD ; lodID < pStudioHWData - > m_NumLODs ; + + lodID )
{
studioloddata_t * pStudioLODData = & pStudioHWData - > m_pLODs [ lodID ] ;
studiomeshdata_t * pStudioMeshData = pStudioLODData - > m_pMeshData ;
// Iterate over all the meshes....
for ( int meshID = 0 ; meshID < pModel - > nummeshes ; + + meshID )
{
mstudiomesh_t * pMesh = pModel - > pMesh ( meshID ) ;
// Iterate over all strip groups.
for ( int stripGroupID = 0 ; stripGroupID < pStudioMeshData [ pMesh - > meshid ] . m_NumGroup ; + + stripGroupID )
{
studiomeshgroup_t * pMeshGroup = & pStudioMeshData [ pMesh - > meshid ] . m_pMeshGroup [ stripGroupID ] ;
ColorMeshInfo_t * pColorMeshInfo = & pColorMeshData - > m_pMeshInfos [ pMeshGroup - > m_ColorMeshID ] ;
CMeshBuilder meshBuilder ;
meshBuilder . Begin ( pColorMeshInfo - > m_pMesh , MATERIAL_HETEROGENOUS , pMeshGroup - > m_NumVertices , 0 ) ;
if ( ! meshBuilder . VertexSize ( ) )
{
meshBuilder . End ( ) ;
return false ; // Aborting processing, since something was wrong with D3D
}
// We need to account for the stream offset used by pool-allocated (static-lit) color meshes:
int streamOffset = pColorMeshInfo - > m_nVertOffsetInBytes / meshBuilder . VertexSize ( ) ;
meshBuilder . AdvanceVertices ( streamOffset ) ;
// Iterate over all vertices
for ( int i = 0 ; i < pMeshGroup - > m_NumVertices ; + + i )
{
int nVertIndex = pMesh - > vertexoffset + pMeshGroup - > m_pGroupIndexToMeshIndex [ i ] ;
Assert ( nVertIndex < pModel - > numvertices ) ;
meshBuilder . Specular3ub ( tmpLightingMem [ nVertIndex ] . r , tmpLightingMem [ nVertIndex ] . g , tmpLightingMem [ nVertIndex ] . b ) ;
meshBuilder . AdvanceVertex ( ) ;
}
meshBuilder . End ( ) ;
}
}
}
}
}
pColorMeshData - > m_bColorMeshValid = true ;
pColorMeshData - > m_bNeedsRetry = false ;
# endif
return true ;
}
//-----------------------------------------------------------------------------
// FIXME? : Move this to StudioRender?
//-----------------------------------------------------------------------------
void CModelRender : : DestroyStaticPropColorData ( ModelInstanceHandle_t handle )
{
# ifndef SWDS
if ( handle = = MODEL_INSTANCE_INVALID )
return ;
if ( m_ModelInstances [ handle ] . m_ColorMeshHandle ! = DC_INVALID_HANDLE )
{
CacheRemove ( m_ModelInstances [ handle ] . m_ColorMeshHandle ) ;
m_ModelInstances [ handle ] . m_ColorMeshHandle = DC_INVALID_HANDLE ;
}
# endif
}
void CModelRender : : ReleaseAllStaticPropColorData ( void )
{
FOR_EACH_LL ( m_ModelInstances , i )
{
DestroyStaticPropColorData ( i ) ;
}
if ( IsX360 ( ) )
{
PurgeCachedStaticPropColorData ( ) ;
}
}
void CModelRender : : RestoreAllStaticPropColorData ( void )
{
# if !defined( SWDS )
if ( ! host_state . worldmodel )
return ;
// invalidate all static lighting cache data
InvalidateStaticLightingCache ( ) ;
// rebake
FOR_EACH_LL ( m_ModelInstances , i )
{
UpdateStaticPropColorData ( m_ModelInstances [ i ] . m_pRenderable - > GetIClientUnknown ( ) , i ) ;
}
# endif
}
void RestoreAllStaticPropColorData ( void )
{
s_ModelRender . RestoreAllStaticPropColorData ( ) ;
}
//-----------------------------------------------------------------------------
// Creates, destroys instance data to be associated with the model
//-----------------------------------------------------------------------------
ModelInstanceHandle_t CModelRender : : CreateInstance ( IClientRenderable * pRenderable , LightCacheHandle_t * pCache )
{
Assert ( pRenderable ) ;
// ensure all components are available
model_t * pModel = ( model_t * ) pRenderable - > GetModel ( ) ;
// We're ok, allocate a new instance handle
ModelInstanceHandle_t handle = m_ModelInstances . AddToTail ( ) ;
ModelInstance_t & instance = m_ModelInstances [ handle ] ;
instance . m_pRenderable = pRenderable ;
instance . m_DecalHandle = STUDIORENDER_DECAL_INVALID ;
instance . m_pModel = ( model_t * ) pModel ;
instance . m_ColorMeshHandle = DC_INVALID_HANDLE ;
instance . m_flLightingTime = CURRENT_LIGHTING_UNINITIALIZED ;
instance . m_nFlags = 0 ;
instance . m_LightCacheHandle = 0 ;
instance . m_AmbientLightingState . ZeroLightingState ( ) ;
for ( int i = 0 ; i < 6 ; + + i )
{
// To catch errors with uninitialized m_AmbientLightingState...
// force to pure red
instance . m_AmbientLightingState . r_boxcolor [ i ] . x = 1.0 ;
}
# ifndef SWDS
instance . m_FirstShadow = g_pShadowMgr - > InvalidShadowIndex ( ) ;
# endif
// Static props use baked lighting for performance reasons
if ( pCache )
{
SetStaticLighting ( handle , pCache ) ;
// validate static color meshes once, now at load/create time
ValidateStaticPropColorData ( handle ) ;
2023-12-14 01:18:09 +08:00
2020-04-23 00:56:21 +08:00
// 360 persists the color meshes across same map loads
2023-12-14 01:18:09 +08:00
# ifdef _X360
if ( r_decalstaticprops . GetBool ( ) & & instance . m_LightCacheHandle )
instance . m_AmbientLightingState = * ( LightcacheGetStatic ( * pCache , NULL , LIGHTCACHEFLAGS_STATIC ) ) ;
# else
if ( instance . m_ColorMeshHandle = = DC_INVALID_HANDLE )
2020-04-23 00:56:21 +08:00
{
// builds out color meshes or loads disk colors, now at load/create time
RecomputeStaticLighting ( handle ) ;
}
2023-12-14 01:18:09 +08:00
# endif
2020-04-23 00:56:21 +08:00
}
2023-12-14 01:18:09 +08:00
2020-04-23 00:56:21 +08:00
return handle ;
}
//-----------------------------------------------------------------------------
// Assigns static lighting to the model instance
//-----------------------------------------------------------------------------
void CModelRender : : SetStaticLighting ( ModelInstanceHandle_t handle , LightCacheHandle_t * pCache )
{
// FIXME: If we make static lighting available for client-side props,
// we must clean up the lightcache handles as the model instances are removed.
// At the moment, since only the static prop manager uses this, it cleans up all LightCacheHandles
// at level shutdown.
// The reason I moved the lightcache handles into here is because this place needs
// to know about lighting overrides when restoring meshes for alt-tab reasons
// It was a real pain to do this from within the static prop mgr, where the
// lightcache handle used to reside
if ( handle ! = MODEL_INSTANCE_INVALID )
{
ModelInstance_t & instance = m_ModelInstances [ handle ] ;
if ( pCache )
{
instance . m_LightCacheHandle = * pCache ;
instance . m_nFlags | = MODEL_INSTANCE_HAS_STATIC_LIGHTING ;
}
else
{
instance . m_LightCacheHandle = 0 ;
instance . m_nFlags & = ~ MODEL_INSTANCE_HAS_STATIC_LIGHTING ;
}
}
}
LightCacheHandle_t CModelRender : : GetStaticLighting ( ModelInstanceHandle_t handle )
{
if ( handle ! = MODEL_INSTANCE_INVALID )
{
ModelInstance_t & instance = m_ModelInstances [ handle ] ;
if ( instance . m_nFlags & MODEL_INSTANCE_HAS_STATIC_LIGHTING )
return instance . m_LightCacheHandle ;
return 0 ;
}
return NULL ;
}
//-----------------------------------------------------------------------------
// This gets called when overbright, etc gets changed to recompute static prop lighting.
// Returns FALSE if needed async data not available to complete computation or an error (don't draw).
// Returns TRUE if operation succeeded or computation skipped (ok to draw).
// Callers use this to track state in a retry pattern, so the expensive computation
// only happens once as needed or can continue to be polled until success.
//-----------------------------------------------------------------------------
bool CModelRender : : RecomputeStaticLighting ( ModelInstanceHandle_t handle )
{
# ifndef SWDS
if ( handle = = MODEL_INSTANCE_INVALID )
{
return false ;
}
if ( ! g_pMaterialSystemHardwareConfig - > SupportsColorOnSecondStream ( ) )
{
// static lighting not supported, but callers can proceed
return true ;
}
ModelInstance_t & instance = m_ModelInstances [ handle ] ;
Assert ( modelloader - > IsLoaded ( instance . m_pModel ) & & ( instance . m_pModel - > type = = mod_studio ) ) ;
// get data, possibly delayed due to async
studiohdr_t * pStudioHdr = g_pMDLCache - > GetStudioHdr ( instance . m_pModel - > studio ) ;
if ( ! pStudioHdr )
{
// data not available
return false ;
}
if ( pStudioHdr - > flags & STUDIOHDR_FLAGS_STATIC_PROP )
{
// get data, possibly delayed due to async
studiohwdata_t * pStudioHWData = g_pMDLCache - > GetHardwareData ( instance . m_pModel - > studio ) ;
if ( ! pStudioHWData )
{
// data not available
return false ;
}
if ( r_decalstaticprops . GetBool ( ) & & instance . m_LightCacheHandle )
{
instance . m_AmbientLightingState = * ( LightcacheGetStatic ( instance . m_LightCacheHandle , NULL , LIGHTCACHEFLAGS_STATIC ) ) ;
}
return UpdateStaticPropColorData ( instance . m_pRenderable - > GetIClientUnknown ( ) , handle ) ;
}
# endif
// success
return true ;
}
void CModelRender : : PurgeCachedStaticPropColorData ( void )
{
// valid for 360 only
Assert ( IsX360 ( ) ) ;
if ( IsPC ( ) )
{
return ;
}
// flush all the color mesh data
GetCacheSection ( ) - > Flush ( true , true ) ;
DataCacheStatus_t status ;
GetCacheSection ( ) - > GetStatus ( & status ) ;
if ( status . nBytes )
{
DevWarning ( " CModelRender: ColorMesh %d bytes failed to flush! \n " , status . nBytes ) ;
}
m_colorMeshVBAllocator . Clear ( ) ;
m_CachedStaticPropColorData . Purge ( ) ;
}
bool CModelRender : : IsStaticPropColorDataCached ( const char * pName )
{
// valid for 360 only
Assert ( IsX360 ( ) ) ;
if ( IsPC ( ) )
{
return false ;
}
DataCacheHandle_t hColorMesh = DC_INVALID_HANDLE ;
{
AUTO_LOCK ( m_CachedStaticPropMutex ) ;
int iIndex = m_CachedStaticPropColorData . Find ( pName ) ;
if ( m_CachedStaticPropColorData . IsValidIndex ( iIndex ) )
{
hColorMesh = m_CachedStaticPropColorData [ iIndex ] ;
}
}
CColorMeshData * pColorMeshData = CacheGetNoTouch ( hColorMesh ) ;
if ( pColorMeshData )
{
// color mesh data is in cache
return true ;
}
return false ;
}
DataCacheHandle_t CModelRender : : GetCachedStaticPropColorData ( const char * pName )
{
// valid for 360 only
Assert ( IsX360 ( ) ) ;
if ( IsPC ( ) )
{
return DC_INVALID_HANDLE ;
}
DataCacheHandle_t hColorMesh = DC_INVALID_HANDLE ;
{
AUTO_LOCK ( m_CachedStaticPropMutex ) ;
int iIndex = m_CachedStaticPropColorData . Find ( pName ) ;
if ( m_CachedStaticPropColorData . IsValidIndex ( iIndex ) )
{
hColorMesh = m_CachedStaticPropColorData [ iIndex ] ;
}
}
return hColorMesh ;
}
void CModelRender : : SetupColorMeshes ( int nTotalVerts )
{
Assert ( IsX360 ( ) ) ;
if ( IsPC ( ) )
{
return ;
}
if ( ! g_pQueuedLoader - > IsMapLoading ( ) )
{
// oops, the queued loader didn't run which does the pre-purge cleanup
// do the cleanup now
PurgeCachedStaticPropColorData ( ) ;
}
// Set up the appropriate default value for color mesh pooling
if ( r_proplightingpooling . GetInt ( ) = = - 1 )
{
// This is useful on X360 because VBs are 4-KB aligned, so using a shared VB saves tons of memory
r_proplightingpooling . SetValue ( true ) ;
}
if ( r_proplightingpooling . GetInt ( ) = = 1 )
{
if ( m_colorMeshVBAllocator . GetNumVertsAllocated ( ) = = 0 )
{
if ( nTotalVerts )
{
// Allocate a mesh (vertex buffer) big enough to accommodate all static prop color meshes
// (which are allocated inside CModelRender::FindOrCreateStaticPropColorData() ):
m_colorMeshVBAllocator . Init ( VERTEX_SPECULAR , nTotalVerts ) ;
}
}
else
{
// already allocated
// 360 keeps the color meshes during same map loads
// vb allocator already allocated, needs to match
Assert ( m_colorMeshVBAllocator . GetNumVertsAllocated ( ) = = nTotalVerts ) ;
}
}
}
void CModelRender : : DestroyInstance ( ModelInstanceHandle_t handle )
{
if ( handle = = MODEL_INSTANCE_INVALID )
return ;
g_pStudioRender - > DestroyDecalList ( m_ModelInstances [ handle ] . m_DecalHandle ) ;
# ifndef SWDS
g_pShadowMgr - > RemoveAllShadowsFromModel ( handle ) ;
# endif
// 360 holds onto static prop disk color data only, to avoid redundant work during same map load
// can only persist props with disk based lighting
// check for dvd mode as a reasonable assurance that the queued loader will be responsible for a possible purge
// if the queued loader doesn't run, the purge will get caught later than intended
bool bPersistLighting = IsX360 ( ) & &
( m_ModelInstances [ handle ] . m_nFlags & MODEL_INSTANCE_HAS_DISKCOMPILED_COLOR ) & &
( g_pFullFileSystem - > GetDVDMode ( ) = = DVDMODE_STRICT ) ;
if ( ! bPersistLighting )
{
DestroyStaticPropColorData ( handle ) ;
}
m_ModelInstances . Remove ( handle ) ;
}
bool CModelRender : : ChangeInstance ( ModelInstanceHandle_t handle , IClientRenderable * pRenderable )
{
if ( handle = = MODEL_INSTANCE_INVALID | | ! pRenderable )
return false ;
ModelInstance_t & instance = m_ModelInstances [ handle ] ;
if ( instance . m_pModel ! = pRenderable - > GetModel ( ) )
{
DevMsg ( " MoveInstanceHandle: models are different! \n " ) ;
return false ;
}
// ok, models are the same, change renderable pointer
instance . m_pRenderable = pRenderable ;
return true ;
}
//-----------------------------------------------------------------------------
// It's not valid if the model index changed + we have non-zero instance data
//-----------------------------------------------------------------------------
bool CModelRender : : IsModelInstanceValid ( ModelInstanceHandle_t handle )
{
if ( handle = = MODEL_INSTANCE_INVALID )
return false ;
ModelInstance_t & inst = m_ModelInstances [ handle ] ;
if ( inst . m_DecalHandle = = STUDIORENDER_DECAL_INVALID )
return false ;
model_t const * pModel = inst . m_pRenderable - > GetModel ( ) ;
return inst . m_pModel = = pModel ;
}
//-----------------------------------------------------------------------------
// Creates a decal on a model instance by doing a planar projection
//-----------------------------------------------------------------------------
void CModelRender : : AddDecal ( ModelInstanceHandle_t handle , Ray_t const & ray ,
const Vector & decalUp , int decalIndex , int body , bool noPokeThru , int maxLODToDecal )
{
Color cColorTemp ;
AddDecalInternal ( handle , ray , decalUp , decalIndex , body , false , cColorTemp , noPokeThru , maxLODToDecal ) ;
}
//-----------------------------------------------------------------------------
void CModelRender : : AddColoredDecal ( ModelInstanceHandle_t handle , Ray_t const & ray ,
const Vector & decalUp , int decalIndex , int body , Color cColor , bool noPokeThru , int maxLODToDecal )
{
AddDecalInternal ( handle , ray , decalUp , decalIndex , body , true , cColor , noPokeThru , maxLODToDecal ) ;
}
//-----------------------------------------------------------------------------
void CModelRender : : GetMaterialOverride ( IMaterial * * ppOutForcedMaterial , OverrideType_t * pOutOverrideType )
{
g_pStudioRender - > GetMaterialOverride ( ppOutForcedMaterial , pOutOverrideType ) ;
}
//-----------------------------------------------------------------------------
void CModelRender : : AddDecalInternal ( ModelInstanceHandle_t handle , Ray_t const & ray ,
const Vector & decalUp , int decalIndex , int body , bool bUseColor , Color cColor , bool noPokeThru , int maxLODToDecal )
{
if ( handle = = MODEL_INSTANCE_INVALID )
return ;
// Get the decal material + radius
IMaterial * pDecalMaterial ;
float w , h ;
R_DecalGetMaterialAndSize ( decalIndex , pDecalMaterial , w , h ) ;
if ( ! pDecalMaterial )
{
DevWarning ( " Bad decal index %d \n " , decalIndex ) ;
return ;
}
w * = 0.5f ;
h * = 0.5f ;
// FIXME: For now, don't render fading decals on props...
bool found = false ;
pDecalMaterial - > FindVar ( " $decalFadeDuration " , & found , false ) ;
if ( found )
return ;
if ( bUseColor )
{
IMaterialVar * pColor = pDecalMaterial - > FindVar ( " $color2 " , & found , false ) ;
if ( found )
{
// expects a 0..1 value. Input is 0 to 255
pColor - > SetVecValue ( cColor . r ( ) / 255.0f , cColor . g ( ) / 255.0f , cColor . b ( ) / 255.0f ) ;
}
}
// FIXME: Pass w and h into AddDecal
float radius = ( w > h ) ? w : h ;
ModelInstance_t & inst = m_ModelInstances [ handle ] ;
if ( ! IsModelInstanceValid ( handle ) )
{
g_pStudioRender - > DestroyDecalList ( inst . m_DecalHandle ) ;
inst . m_DecalHandle = STUDIORENDER_DECAL_INVALID ;
}
Assert ( modelloader - > IsLoaded ( inst . m_pModel ) & & ( inst . m_pModel - > type = = mod_studio ) ) ;
if ( inst . m_DecalHandle = = STUDIORENDER_DECAL_INVALID )
{
studiohwdata_t * pStudioHWData = g_pMDLCache - > GetHardwareData ( inst . m_pModel - > studio ) ;
inst . m_DecalHandle = g_pStudioRender - > CreateDecalList ( pStudioHWData ) ;
}
matrix3x4_t * pBoneToWorld = SetupModelState ( inst . m_pRenderable ) ;
g_pStudioRender - > AddDecal ( inst . m_DecalHandle , g_pMDLCache - > GetStudioHdr ( inst . m_pModel - > studio ) ,
pBoneToWorld , ray , decalUp , pDecalMaterial , radius , body , noPokeThru , maxLODToDecal ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Removes all the decals on a model instance
//-----------------------------------------------------------------------------
void CModelRender : : RemoveAllDecals ( ModelInstanceHandle_t handle )
{
if ( handle = = MODEL_INSTANCE_INVALID )
return ;
ModelInstance_t & inst = m_ModelInstances [ handle ] ;
if ( ! IsModelInstanceValid ( handle ) )
return ;
g_pStudioRender - > DestroyDecalList ( inst . m_DecalHandle ) ;
inst . m_DecalHandle = STUDIORENDER_DECAL_INVALID ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CModelRender : : RemoveAllDecalsFromAllModels ( )
{
for ( ModelInstanceHandle_t i = m_ModelInstances . Head ( ) ;
i ! = m_ModelInstances . InvalidIndex ( ) ;
i = m_ModelInstances . Next ( i ) )
{
RemoveAllDecals ( i ) ;
}
}
const vertexFileHeader_t * mstudiomodel_t : : CacheVertexData ( void * pModelData )
{
// make requested data resident
Assert ( pModelData = = NULL ) ;
return s_ModelRender . CacheVertexData ( ) ;
}
bool CheckVarRange_r_rootlod ( )
{
return CheckVarRange_Generic ( & r_rootlod , 0 , 2 ) ;
}
bool CheckVarRange_r_lod ( )
{
return CheckVarRange_Generic ( & r_lod , - 1 , 2 ) ;
}
// Convar callback to change lod
//-----------------------------------------------------------------------------
void r_lod_f ( IConVar * var , const char * pOldValue , float flOldValue )
{
CheckVarRange_r_lod ( ) ;
}
//-----------------------------------------------------------------------------
// Convar callback to change root lod
//-----------------------------------------------------------------------------
void SetRootLOD_f ( IConVar * pConVar , const char * pOldString , float flOldValue )
{
// Make sure the variable is in range.
if ( CheckVarRange_r_rootlod ( ) )
return ; // was called recursively.
ConVarRef var ( pConVar ) ;
UpdateStudioRenderConfig ( ) ;
if ( ! g_LostVideoMemory & & Q_strcmp ( var . GetString ( ) , pOldString ) )
{
// reload only the necessary models to desired lod
modelloader - > Studio_ReloadModels ( IModelLoader : : RELOAD_LOD_CHANGED ) ;
}
}
//-----------------------------------------------------------------------------
// Discard and reload (rebuild, rebake, etc) models to the current lod
//-----------------------------------------------------------------------------
void FlushLOD_f ( )
{
UpdateStudioRenderConfig ( ) ;
if ( ! g_LostVideoMemory )
{
// force a full discard and rebuild of all loaded models
modelloader - > Studio_ReloadModels ( IModelLoader : : RELOAD_EVERYTHING ) ;
}
}
//-----------------------------------------------------------------------------
//
// CPooledVBAllocator_ColorMesh implementation
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// CPooledVBAllocator_ColorMesh constructor
//-----------------------------------------------------------------------------
CPooledVBAllocator_ColorMesh : : CPooledVBAllocator_ColorMesh ( )
: m_pMesh ( NULL )
{
Clear ( ) ;
}
//-----------------------------------------------------------------------------
// CPooledVBAllocator_ColorMesh destructor
// - Clear should have been called
//-----------------------------------------------------------------------------
CPooledVBAllocator_ColorMesh : : ~ CPooledVBAllocator_ColorMesh ( )
{
CheckIsClear ( ) ;
// Clean up, if it hadn't been done already
Clear ( ) ;
}
//-----------------------------------------------------------------------------
// Init
// - Allocate the internal shared mesh (vertex buffer)
//-----------------------------------------------------------------------------
bool CPooledVBAllocator_ColorMesh : : Init ( VertexFormat_t format , int numVerts )
{
if ( ! CheckIsClear ( ) )
return false ;
if ( g_VBAllocTracker )
g_VBAllocTracker - > TrackMeshAllocations ( " CPooledVBAllocator_ColorMesh::Init " ) ;
CMatRenderContextPtr pRenderContext ( materials ) ;
m_pMesh = pRenderContext - > CreateStaticMesh ( format , TEXTURE_GROUP_STATIC_VERTEX_BUFFER_COLOR ) ;
if ( m_pMesh )
{
// Build out the underlying vertex buffer
CMeshBuilder meshBuilder ;
int numIndices = 0 ;
meshBuilder . Begin ( m_pMesh , MATERIAL_HETEROGENOUS , numVerts , numIndices ) ;
{
m_pVertexBufferBase = meshBuilder . Specular ( ) ;
m_totalVerts = numVerts ;
m_vertexSize = meshBuilder . VertexSize ( ) ;
// Probably good to catch any change to vertex size... there may be assumptions based on it:
Assert ( m_vertexSize = = 4 ) ;
// Start at the bottom of the VB and work your way up like a simple stack
m_nextFreeOffset = 0 ;
}
meshBuilder . End ( ) ;
}
if ( g_VBAllocTracker )
g_VBAllocTracker - > TrackMeshAllocations ( NULL ) ;
return ( m_pMesh ! = NULL ) ;
}
//-----------------------------------------------------------------------------
// Clear
// - frees the shared mesh (vertex buffer), resets member variables
//-----------------------------------------------------------------------------
void CPooledVBAllocator_ColorMesh : : Clear ( void )
{
if ( m_pMesh ! = NULL )
{
if ( m_numAllocations > 0 )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh::Clear should not be called until all allocations released! " ) ;
Assert ( m_numAllocations = = 0 ) ;
}
CMatRenderContextPtr pRenderContext ( materials ) ;
pRenderContext - > DestroyStaticMesh ( m_pMesh ) ;
m_pMesh = NULL ;
}
m_pVertexBufferBase = NULL ;
m_totalVerts = 0 ;
m_vertexSize = 0 ;
m_numAllocations = 0 ;
m_numVertsAllocated = 0 ;
m_nextFreeOffset = - 1 ;
m_bStartedDeallocation = false ;
}
//-----------------------------------------------------------------------------
// CheckIsClear
// - assert/warn if the allocator isn't in a clear state
// (no extant allocations, no internal mesh)
//-----------------------------------------------------------------------------
bool CPooledVBAllocator_ColorMesh : : CheckIsClear ( void )
{
if ( m_pMesh )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh's internal mesh (vertex buffer) should have been freed! " ) ;
Assert ( m_pMesh = = NULL ) ;
return false ;
}
if ( m_numAllocations > 0 )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh has unfreed allocations! " ) ;
Assert ( m_numAllocations = = 0 ) ;
return false ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Allocate
// - Allocate a sub-range of 'numVerts' from free space in the shared vertex buffer
// (returns the byte offset from the start of the VB to the new allocation)
// - returns -1 on failure
//-----------------------------------------------------------------------------
int CPooledVBAllocator_ColorMesh : : Allocate ( int numVerts )
{
if ( m_pMesh = = NULL )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh::Allocate cannot be called before Init (expect a crash) " ) ;
Assert ( m_pMesh ) ;
return - 1 ;
}
// Once we start deallocating, we have to keep going until everything has been freed
if ( m_bStartedDeallocation )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh::Allocate being called after some (but not all) calls to Deallocate have been called - invalid! (expect visual artifacts) " ) ;
Assert ( ! m_bStartedDeallocation ) ;
return - 1 ;
}
if ( numVerts > ( m_totalVerts - m_numVertsAllocated ) )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh::Allocate failing - not enough space left in the vertex buffer! " ) ;
Assert ( numVerts < = ( m_totalVerts - m_numVertsAllocated ) ) ;
return - 1 ;
}
int result = m_nextFreeOffset ;
m_numAllocations + = 1 ;
m_numVertsAllocated + = numVerts ;
m_nextFreeOffset + = numVerts * m_vertexSize ;
return result ;
}
//-----------------------------------------------------------------------------
// Deallocate
// - Deallocate an existing allocation
//-----------------------------------------------------------------------------
void CPooledVBAllocator_ColorMesh : : Deallocate ( int offset , int numVerts )
{
if ( m_pMesh = = NULL )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh::Deallocate cannot be called before Init " ) ;
Assert ( m_pMesh ! = NULL ) ;
return ;
}
if ( m_numAllocations = = 0 )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh::Deallocate called too many times! (bug in calling code) " ) ;
Assert ( m_numAllocations > 0 ) ;
return ;
}
if ( numVerts > m_numVertsAllocated )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh::Deallocate called with too many verts, trying to free more than were allocated (bug in calling code) " ) ;
Assert ( numVerts < = m_numVertsAllocated ) ;
numVerts = m_numVertsAllocated ; // Hack (avoid counters ever going below zero)
}
// Now all extant allocations must be freed before we make any new allocations
m_bStartedDeallocation = true ;
m_numAllocations - = 1 ;
m_numVertsAllocated - = numVerts ;
m_nextFreeOffset = 0 ; // (we shouldn't be returning this until everything's free, at which point 0 is valid)
// Are we empty?
if ( m_numAllocations = = 0 )
{
if ( m_numVertsAllocated ! = 0 )
{
Warning ( " ERROR: CPooledVBAllocator_ColorMesh::Deallocate, after all allocations have been freed too few verts total have been deallocated (bug in calling code) " ) ;
Assert ( m_numVertsAllocated = = 0 ) ;
}
// We can start allocating again, now
m_bStartedDeallocation = false ;
}
}
//-----------------------------------------------------------------------------
// CreateLightmapsFromData
// - Creates Lightmap Textures from data that was squirreled away during ASYNC load.
// This is necessary because the material system doesn't like us creating things from ASYNC loaders.
//-----------------------------------------------------------------------------
static void CreateLightmapsFromData ( CColorMeshData * _colorMeshData )
{
Assert ( _colorMeshData - > m_bColorTextureValid ) ;
Assert ( ! _colorMeshData - > m_bColorTextureCreated ) ;
for ( int mesh = 0 ; mesh < _colorMeshData - > m_nMeshes ; + + mesh )
{
ColorMeshInfo_t * meshInfo = & _colorMeshData - > m_pMeshInfos [ mesh ] ;
// Ensure that we haven't somehow already messed with these.
Assert ( meshInfo - > m_pLightmapData ) ;
Assert ( ! meshInfo - > m_pLightmap ) ;
ColorTexelsInfo_t * cti = meshInfo - > m_pLightmapData ;
Assert ( cti - > m_pTexelData ) ;
meshInfo - > m_pLightmap = g_pMaterialSystem - > CreateTextureFromBits ( cti - > m_nWidth , cti - > m_nHeight , cti - > m_nMipmapCount , cti - > m_ImageFormat , cti - > m_nByteCount , cti - > m_pTexelData ) ;
// If this triggers, we need to figure out if it's reasonable to fail. If it is, then we should figure out how to signal back
// that we shouldn't try to create this again (probably by clearing _colorMeshData->m_bColoTextureValid)
Assert ( meshInfo - > m_pLightmap ) ;
// Cleanup after ourselves.
delete [ ] cti - > m_pTexelData ;
delete cti ;
meshInfo - > m_pLightmapData = NULL ;
}
_colorMeshData - > m_bColorTextureCreated = true ;
}