2020-04-22 12:56:21 -04:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $Revision: $
// $NoKeywords: $
//
// This file contains code to allow us to associate client data with bsp leaves.
//
//=============================================================================//
# include "vrad.h"
# include "mathlib/vector.h"
2023-05-09 19:57:22 +03:00
# include "utlbuffer.h"
2020-04-22 12:56:21 -04:00
# include "utlvector.h"
2023-05-09 19:57:22 +03:00
# include "gamebspfile.h"
# include "bsptreedata.h"
# include "vphysics_interface.h"
# include "studio.h"
# include "optimize.h"
# include "bsplib.h"
# include "cmodel.h"
# include "physdll.h"
2020-04-22 12:56:21 -04:00
# include "phyfile.h"
# include "collisionutils.h"
# include "tier1/KeyValues.h"
# include "pacifier.h"
# include "materialsystem/imaterial.h"
# include "materialsystem/hardwareverts.h"
# include "byteswap.h"
2023-05-13 21:11:15 +03:00
# include "mpivrad.h"
2020-04-22 12:56:21 -04:00
# include "vtf/vtf.h"
# include "tier1/utldict.h"
# include "tier1/utlsymbol.h"
2023-05-13 21:11:15 +03:00
# include "tier3/tier3.h"
2020-04-22 12:56:21 -04:00
2023-05-09 19:57:22 +03:00
# ifdef MPI
2020-04-22 12:56:21 -04:00
# include "messbuf.h"
# include "vmpi.h"
# include "vmpi_distribute_work.h"
2023-05-09 19:57:22 +03:00
# endif
2023-05-13 21:11:15 +03:00
# include "iscratchpad3d.h"
2020-04-22 12:56:21 -04:00
# define ALIGN_TO_POW2(x,y) (((x)+(y-1))&~(y-1))
2023-05-13 21:11:15 +03:00
int g_numVradStaticPropsLightingStreams = 3 ;
static const TableVector g_localUpBumpBasis [ NUM_BUMP_VECTS ] =
{
// consistent basis wrt lightmaps
{ OO_SQRT_2_OVER_3 , 0.0f , OO_SQRT_3 } ,
{ - OO_SQRT_6 , OO_SQRT_2 , OO_SQRT_3 } ,
{ - OO_SQRT_6 , - OO_SQRT_2 , OO_SQRT_3 }
} ;
void GetStaticPropBumpNormals ( const Vector & sVect , const Vector & tVect , const Vector & flatNormal ,
const Vector & phongNormal , Vector bumpNormals [ NUM_BUMP_VECTS ] )
{
Vector tmpNormal ;
bool leftHanded ;
int i ;
assert ( NUM_BUMP_VECTS = = 3 ) ;
// Are we left or right handed?
CrossProduct ( sVect , tVect , tmpNormal ) ;
if ( DotProduct ( flatNormal , tmpNormal ) < 0.0f )
{
leftHanded = true ;
}
else
{
leftHanded = false ;
}
// Build a basis for the face around the phong normal
matrix3x4_t smoothBasis ;
CrossProduct ( phongNormal . Base ( ) , sVect . Base ( ) , smoothBasis [ 1 ] ) ;
VectorNormalize ( smoothBasis [ 1 ] ) ;
CrossProduct ( smoothBasis [ 1 ] , phongNormal . Base ( ) , smoothBasis [ 0 ] ) ;
VectorNormalize ( smoothBasis [ 0 ] ) ;
VectorCopy ( phongNormal . Base ( ) , smoothBasis [ 2 ] ) ;
if ( leftHanded )
{
VectorNegate ( smoothBasis [ 1 ] ) ;
}
// move the g_localUpBumpBasis into world space to create bumpNormals
for ( i = 0 ; i < 3 ; i + + )
{
VectorIRotate ( g_localUpBumpBasis [ i ] , smoothBasis , bumpNormals [ i ] ) ;
}
}
2020-04-22 12:56:21 -04:00
// identifies a vertex embedded in solid
// lighting will be copied from nearest valid neighbor
struct badVertex_t
{
int m_ColorVertex ;
Vector m_Position ;
2023-05-13 21:11:15 +03:00
Vector m_Normals [ NUM_BUMP_VECTS + 1 ] ;
2020-04-22 12:56:21 -04:00
} ;
// a final colored vertex
struct colorVertex_t
{
2023-05-13 21:11:15 +03:00
Vector m_Colors [ NUM_BUMP_VECTS + 1 ] ;
float m_SunAmount [ NUM_BUMP_VECTS + 1 ] ;
2020-04-22 12:56:21 -04:00
Vector m_Position ;
bool m_bValid ;
} ;
class CComputeStaticPropLightingResults
{
public :
~ CComputeStaticPropLightingResults ( )
{
m_ColorVertsArrays . PurgeAndDeleteElements ( ) ;
}
CUtlVector < CUtlVector < colorVertex_t > * > m_ColorVertsArrays ;
} ;
2023-05-13 21:11:15 +03:00
Vector NormalizeVertexBumpedLighting ( Vector const * pColorNormal , Vector * pColorBumps )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
const Vector & linearUnbumped = * ( ( const Vector * ) pColorNormal ) ;
Vector linearBump1 = * ( ( const Vector * ) ( pColorBumps + 0 ) ) ;
Vector linearBump2 = * ( ( const Vector * ) ( pColorBumps + 1 ) ) ;
Vector linearBump3 = * ( ( const Vector * ) ( pColorBumps + 2 ) ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
const float flNormalizationFactor = 1.0f / 3.0f ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
// find a scale factor which makes the average of the 3 bumped mapped vectors match the
// straight up vector (if possible), so that flat bumpmapped areas match non-bumpmapped
// areas.
Vector bumpAverage = linearBump1 ;
bumpAverage + = linearBump2 ;
bumpAverage + = linearBump3 ;
bumpAverage * = flNormalizationFactor ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
Vector correctionScale ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
if ( * ( int * ) & bumpAverage [ 0 ] ! = 0 & &
* ( int * ) & bumpAverage [ 1 ] ! = 0 & &
* ( int * ) & bumpAverage [ 2 ] ! = 0 )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
// fast path when we know that we don't have to worry about divide by zero.
VectorDivide ( linearUnbumped , bumpAverage , correctionScale ) ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
else
{
correctionScale . Init ( 0.0f , 0.0f , 0.0f ) ;
if ( bumpAverage [ 0 ] ! = 0.0f )
{
correctionScale [ 0 ] = linearUnbumped [ 0 ] / bumpAverage [ 0 ] ;
}
if ( bumpAverage [ 1 ] ! = 0.0f )
{
correctionScale [ 1 ] = linearUnbumped [ 1 ] / bumpAverage [ 1 ] ;
}
if ( bumpAverage [ 2 ] ! = 0.0f )
{
correctionScale [ 2 ] = linearUnbumped [ 2 ] / bumpAverage [ 2 ] ;
}
}
linearBump1 * = correctionScale ;
linearBump2 * = correctionScale ;
linearBump3 * = correctionScale ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
* ( ( Vector * ) ( pColorBumps + 0 ) ) = linearBump1 ;
* ( ( Vector * ) ( pColorBumps + 1 ) ) = linearBump2 ;
* ( ( Vector * ) ( pColorBumps + 2 ) ) = linearBump3 ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
return correctionScale ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
void NormalizeVertexBumpedSunAmount ( float const * pSunAmount0 , float * pSunAmount1 , float * pSunAmount2 , float * pSunAmount3 )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
const float & linearSunAmountUnbumped = * ( ( const float * ) pSunAmount0 ) ;
float linearSunAmount1 = * ( ( const float * ) ( pSunAmount1 ) ) ;
float linearSunAmount2 = * ( ( const float * ) ( pSunAmount2 ) ) ;
float linearSunAmount3 = * ( ( const float * ) ( pSunAmount3 ) ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
const float flNormalizationFactor = 1.0f ; // / 3.0f; - store in 0..1 space (for 0..255 alpha channel), multiply by 3.0 in the shader
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
// find a scale factor which makes the average of the 3 bumped mapped vectors match the
// straight up vector (if possible), so that flat bumpmapped areas match non-bumpmapped
// areas.
float bumpAverage = linearSunAmount1 ;
bumpAverage + = linearSunAmount2 ;
bumpAverage + = linearSunAmount3 ;
bumpAverage * = flNormalizationFactor ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
float correctionScale ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
if ( * ( int * ) & bumpAverage ! = 0 )
{
// fast path when we know that we don't have to worry about divide by zero.
correctionScale = linearSunAmountUnbumped / bumpAverage ;
}
else
{
correctionScale = 1.0f ;
if ( bumpAverage ! = 0.0f )
{
correctionScale = linearSunAmountUnbumped / bumpAverage ;
2020-04-22 12:56:21 -04:00
}
}
2023-05-13 21:11:15 +03:00
linearSunAmount1 * = correctionScale ;
linearSunAmount2 * = correctionScale ;
linearSunAmount3 * = correctionScale ;
* ( ( float * ) ( pSunAmount1 ) ) = linearSunAmount1 ;
* ( ( float * ) ( pSunAmount2 ) ) = linearSunAmount2 ;
* ( ( float * ) ( pSunAmount3 ) ) = linearSunAmount3 ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
void DumpElapsedTime ( int timeTaken )
{
if ( g_bDumpBumpStaticProps & & ( g_numVradStaticPropsLightingStreams = = 3 ) )
{
char mapName [ MAX_PATH ] ;
Q_FileBase ( source , mapName , sizeof ( mapName ) ) ;
char bumpPropFilename [ MAX_PATH ] ;
sprintf ( bumpPropFilename , " vrad_bumpstaticprops_%s.txt " , mapName ) ;
Msg ( " Writing %s... \n " , bumpPropFilename ) ;
FILE * fp = fopen ( bumpPropFilename , " a " ) ;
if ( ! fp )
{
Msg ( " Writing %s...failed \n " , bumpPropFilename ) ;
return ;
}
char str [ 512 ] ;
GetHourMinuteSecondsString ( timeTaken , str , sizeof ( str ) ) ;
fprintf ( fp , " \n \n Using -staticpropsamplescale %f (-final defaults to 16) \n " , g_flStaticPropSampleScale ) ;
fprintf ( fp , " \n Total time taken to bake static prop lighting: %s \n " , str ) ;
fclose ( fp ) ;
}
}
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Globals
//-----------------------------------------------------------------------------
CUtlSymbolTable g_ForcedTextureShadowsModels ;
// DON'T USE THIS FROM WITHIN A THREAD. THERE IS A THREAD CONTEXT CREATED
// INSIDE PropTested_t. USE THAT INSTEAD.
IPhysicsCollision * s_pPhysCollision = NULL ;
//-----------------------------------------------------------------------------
// Vrad's static prop manager
//-----------------------------------------------------------------------------
class CVradStaticPropMgr : public IVradStaticPropMgr
{
public :
// constructor, destructor
CVradStaticPropMgr ( ) ;
virtual ~ CVradStaticPropMgr ( ) ;
// methods of IStaticPropMgr
void Init ( ) ;
void Shutdown ( ) ;
// iterate all the instanced static props and compute their vertex lighting
void ComputeLighting ( int iThread ) ;
2023-05-13 21:11:15 +03:00
virtual void MakePatches ( ) override ;
2020-04-22 12:56:21 -04:00
private :
2023-05-09 19:57:22 +03:00
# ifdef MPI
2020-04-22 12:56:21 -04:00
// VMPI stuff.
static void VMPI_ProcessStaticProp_Static ( int iThread , uint64 iStaticProp , MessageBuffer * pBuf ) ;
static void VMPI_ReceiveStaticPropResults_Static ( uint64 iStaticProp , MessageBuffer * pBuf , int iWorker ) ;
void VMPI_ProcessStaticProp ( int iThread , int iStaticProp , MessageBuffer * pBuf ) ;
void VMPI_ReceiveStaticPropResults ( int iStaticProp , MessageBuffer * pBuf , int iWorker ) ;
2023-05-09 19:57:22 +03:00
# endif
2020-04-22 12:56:21 -04:00
// local thread version
static void ThreadComputeStaticPropLighting ( int iThread , void * pUserData ) ;
void ComputeLightingForProp ( int iThread , int iStaticProp ) ;
// Methods associated with unserializing static props
void UnserializeModelDict ( CUtlBuffer & buf ) ;
void UnserializeModels ( CUtlBuffer & buf ) ;
void UnserializeStaticProps ( ) ;
// Creates a collision model
void CreateCollisionModel ( char const * pModelName ) ;
private :
// Unique static prop models
struct StaticPropDict_t
{
vcollide_t m_loadedModel ;
CPhysCollide * m_pModel ;
Vector m_Mins ; // Bounding box is in local coordinates
Vector m_Maxs ;
studiohdr_t * m_pStudioHdr ;
CUtlBuffer m_VtxBuf ;
CUtlVector < int > m_textureShadowIndex ; // each texture has an index if this model casts texture shadows
CUtlVector < int > m_triangleMaterialIndex ; // each triangle has an index if this model casts texture shadows
2023-05-13 21:11:15 +03:00
Vector m_vReflectivity ;
bool m_bHasBumpmap ;
bool m_bHasPhong ;
2020-04-22 12:56:21 -04:00
} ;
struct MeshData_t
{
2023-05-13 21:11:15 +03:00
CUtlVector < Vector4D > m_VertColorData ; // w has the additional lightmap alpha data
int m_numVerts ;
2020-04-22 12:56:21 -04:00
int m_nLod ;
} ;
// A static prop instance
struct CStaticProp
{
Vector m_Origin ;
QAngle m_Angles ;
Vector m_mins ;
Vector m_maxs ;
Vector m_LightingOrigin ;
int m_ModelIdx ;
BSPTreeDataHandle_t m_Handle ;
CUtlVector < MeshData_t > m_MeshData ;
int m_Flags ;
2023-05-13 21:11:15 +03:00
int m_FlagsEx ;
2020-04-22 12:56:21 -04:00
bool m_bLightingOriginValid ;
2023-05-13 21:11:15 +03:00
Vector m_vReflectivity ;
2020-04-22 12:56:21 -04:00
} ;
// Enumeration context
struct EnumContext_t
{
PropTested_t * m_pPropTested ;
Ray_t const * m_pRay ;
} ;
// The list of all static props
CUtlVector < StaticPropDict_t > m_StaticPropDict ;
CUtlVector < CStaticProp > m_StaticProps ;
bool m_bIgnoreStaticPropTrace ;
void ComputeLighting ( CStaticProp & prop , int iThread , int prop_index , CComputeStaticPropLightingResults * pResults ) ;
2023-05-13 21:11:15 +03:00
void ApplyLightingToStaticProp ( CStaticProp & prop , const CComputeStaticPropLightingResults * pResults ) ;
2020-04-22 12:56:21 -04:00
void SerializeLighting ( ) ;
void AddPolysForRayTrace ( ) ;
void BuildTriList ( CStaticProp & prop ) ;
} ;
//-----------------------------------------------------------------------------
// Expose IVradStaticPropMgr to vrad
//-----------------------------------------------------------------------------
static CVradStaticPropMgr g_StaticPropMgr ;
IVradStaticPropMgr * StaticPropMgr ( )
{
return & g_StaticPropMgr ;
}
//-----------------------------------------------------------------------------
// constructor, destructor
//-----------------------------------------------------------------------------
CVradStaticPropMgr : : CVradStaticPropMgr ( )
{
// set to ignore static prop traces
m_bIgnoreStaticPropTrace = false ;
}
CVradStaticPropMgr : : ~ CVradStaticPropMgr ( )
{
}
//-----------------------------------------------------------------------------
// Makes sure the studio model is a static prop
//-----------------------------------------------------------------------------
bool IsStaticProp ( studiohdr_t * pHdr )
{
if ( ! ( pHdr - > flags & STUDIOHDR_FLAGS_STATIC_PROP ) )
return false ;
return true ;
}
//-----------------------------------------------------------------------------
// Load a file into a Utlbuf
//-----------------------------------------------------------------------------
static bool LoadFile ( char const * pFileName , CUtlBuffer & buf )
{
2023-05-13 21:11:15 +03:00
if ( ReadFileFromPak ( GetPakFile ( ) , pFileName , false , buf ) )
return true ;
2020-04-22 12:56:21 -04:00
if ( ! g_pFullFileSystem )
return false ;
return g_pFullFileSystem - > ReadFile ( pFileName , NULL , buf ) ;
}
//-----------------------------------------------------------------------------
// Constructs the file name from the model name
//-----------------------------------------------------------------------------
static char const * ConstructFileName ( char const * pModelName )
{
static char buf [ 1024 ] ;
sprintf ( buf , " %s%s " , gamedir , pModelName ) ;
return buf ;
}
//-----------------------------------------------------------------------------
// Computes a convex hull from a studio mesh
//-----------------------------------------------------------------------------
static CPhysConvex * ComputeConvexHull ( mstudiomesh_t * pMesh , studiohdr_t * pStudioHdr )
{
const mstudio_meshvertexdata_t * vertData = pMesh - > GetVertexData ( ( void * ) pStudioHdr ) ;
Assert ( vertData ) ; // This can only return NULL on X360 for now
// Generate a list of all verts in the mesh
Vector * * ppVerts = ( Vector * * ) _alloca ( pMesh - > numvertices * sizeof ( Vector * ) ) ;
for ( int i = 0 ; i < pMesh - > numvertices ; + + i )
{
ppVerts [ i ] = vertData - > Position ( i ) ;
}
// Generate a convex hull from the verts
return s_pPhysCollision - > ConvexFromVerts ( ppVerts , pMesh - > numvertices ) ;
}
//-----------------------------------------------------------------------------
// Computes a convex hull from the studio model
//-----------------------------------------------------------------------------
CPhysCollide * ComputeConvexHull ( studiohdr_t * pStudioHdr )
{
CUtlVector < CPhysConvex * > convexHulls ;
for ( int body = 0 ; body < pStudioHdr - > numbodyparts ; + + body )
{
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( body ) ;
for ( int model = 0 ; model < pBodyPart - > nummodels ; + + model )
{
mstudiomodel_t * pStudioModel = pBodyPart - > pModel ( model ) ;
for ( int mesh = 0 ; mesh < pStudioModel - > nummeshes ; + + mesh )
{
// Make a convex hull for each mesh
// NOTE: This won't work unless the model has been compiled
// with $staticprop
mstudiomesh_t * pStudioMesh = pStudioModel - > pMesh ( mesh ) ;
convexHulls . AddToTail ( ComputeConvexHull ( pStudioMesh , pStudioHdr ) ) ;
}
}
}
// Convert an array of convex elements to a compiled collision model
// (this deletes the convex elements)
2023-05-13 21:11:15 +03:00
return s_pPhysCollision - > ConvertConvexToCollide ( convexHulls . Base ( ) , convexHulls . Count ( ) ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Load studio model vertex data from a file...
//-----------------------------------------------------------------------------
bool LoadStudioModel ( char const * pModelName , CUtlBuffer & buf )
{
// No luck, gotta build it
// Construct the file name...
if ( ! LoadFile ( pModelName , buf ) )
{
Warning ( " Error! Unable to load model \" %s \" \n " , pModelName ) ;
return false ;
}
// Check that it's valid
if ( strncmp ( ( const char * ) buf . PeekGet ( ) , " IDST " , 4 ) & &
strncmp ( ( const char * ) buf . PeekGet ( ) , " IDAG " , 4 ) )
{
Warning ( " Error! Invalid model file \" %s \" \n " , pModelName ) ;
return false ;
}
studiohdr_t * pHdr = ( studiohdr_t * ) buf . PeekGet ( ) ;
Studio_ConvertStudioHdrToNewVersion ( pHdr ) ;
if ( pHdr - > version ! = STUDIO_VERSION )
{
Warning ( " Error! Invalid model version \" %s \" \n " , pModelName ) ;
return false ;
}
if ( ! IsStaticProp ( pHdr ) )
{
Warning ( " Error! To use model \" %s \" \n "
" as a static prop, it must be compiled with $staticprop! \n " , pModelName ) ;
return false ;
}
// ensure reset
2023-05-13 21:11:15 +03:00
pHdr - > SetVertexBase ( NULL ) ;
pHdr - > SetIndexBase ( NULL ) ;
2020-04-22 12:56:21 -04:00
return true ;
}
bool LoadStudioCollisionModel ( char const * pModelName , CUtlBuffer & buf )
{
char tmp [ 1024 ] ;
Q_strncpy ( tmp , pModelName , sizeof ( tmp ) ) ;
Q_SetExtension ( tmp , " .phy " , sizeof ( tmp ) ) ;
// No luck, gotta build it
if ( ! LoadFile ( tmp , buf ) )
{
// this is not an error, the model simply has no PHY file
return false ;
}
phyheader_t * header = ( phyheader_t * ) buf . PeekGet ( ) ;
if ( header - > size ! = sizeof ( * header ) | | header - > solidCount < = 0 )
return false ;
return true ;
}
bool LoadVTXFile ( char const * pModelName , const studiohdr_t * pStudioHdr , CUtlBuffer & buf )
{
char filename [ MAX_PATH ] ;
// construct filename
Q_StripExtension ( pModelName , filename , sizeof ( filename ) ) ;
2023-05-13 21:11:15 +03:00
strcat ( filename , " .dx90.vtx " ) ;
2020-04-22 12:56:21 -04:00
if ( ! LoadFile ( filename , buf ) )
{
Warning ( " Error! Unable to load file \" %s \" \n " , filename ) ;
return false ;
}
OptimizedModel : : FileHeader_t * pVtxHdr = ( OptimizedModel : : FileHeader_t * ) buf . Base ( ) ;
// Check that it's valid
if ( pVtxHdr - > version ! = OPTIMIZED_MODEL_FILE_VERSION )
{
Warning ( " Error! Invalid VTX file version: %d, expected %d \" %s \" \n " , pVtxHdr - > version , OPTIMIZED_MODEL_FILE_VERSION , filename ) ;
return false ;
}
if ( pVtxHdr - > checkSum ! = pStudioHdr - > checksum )
{
Warning ( " Error! Invalid VTX file checksum: %d, expected %d \" %s \" \n " , pVtxHdr - > checkSum , pStudioHdr - > checksum , filename ) ;
return false ;
}
return true ;
}
//-----------------------------------------------------------------------------
// Gets a vertex position from a strip index
//-----------------------------------------------------------------------------
inline static Vector * PositionFromIndex ( const mstudio_meshvertexdata_t * vertData , mstudiomesh_t * pMesh , OptimizedModel : : StripGroupHeader_t * pStripGroup , int i )
{
OptimizedModel : : Vertex_t * pVert = pStripGroup - > pVertex ( i ) ;
return vertData - > Position ( pVert - > origMeshVertID ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Writes a glview text file containing the collision surface in question
// Input : *pCollide -
// *pFilename -
//-----------------------------------------------------------------------------
void DumpCollideToGlView ( vcollide_t * pCollide , const char * pFilename )
{
if ( ! pCollide )
return ;
Msg ( " Writing %s... \n " , pFilename ) ;
FILE * fp = fopen ( pFilename , " w " ) ;
for ( int i = 0 ; i < pCollide - > solidCount ; + + i )
{
Vector * outVerts ;
int vertCount = s_pPhysCollision - > CreateDebugMesh ( pCollide - > solids [ i ] , & outVerts ) ;
int triCount = vertCount / 3 ;
int vert = 0 ;
unsigned char r = ( i & 1 ) * 64 + 64 ;
unsigned char g = ( i & 2 ) * 64 + 64 ;
unsigned char b = ( i & 4 ) * 64 + 64 ;
float fr = r / 255.0f ;
float fg = g / 255.0f ;
float fb = b / 255.0f ;
for ( int i = 0 ; i < triCount ; i + + )
{
fprintf ( fp , " 3 \n " ) ;
fprintf ( fp , " %6.3f %6.3f %6.3f %.2f %.3f %.3f \n " ,
outVerts [ vert ] . x , outVerts [ vert ] . y , outVerts [ vert ] . z , fr , fg , fb ) ;
vert + + ;
fprintf ( fp , " %6.3f %6.3f %6.3f %.2f %.3f %.3f \n " ,
outVerts [ vert ] . x , outVerts [ vert ] . y , outVerts [ vert ] . z , fr , fg , fb ) ;
vert + + ;
fprintf ( fp , " %6.3f %6.3f %6.3f %.2f %.3f %.3f \n " ,
outVerts [ vert ] . x , outVerts [ vert ] . y , outVerts [ vert ] . z , fr , fg , fb ) ;
vert + + ;
}
s_pPhysCollision - > DestroyDebugMesh ( vertCount , outVerts ) ;
}
fclose ( fp ) ;
}
static bool PointInTriangle ( const Vector2D & p , const Vector2D & v0 , const Vector2D & v1 , const Vector2D & v2 )
{
float coords [ 3 ] ;
GetBarycentricCoords2D ( v0 , v1 , v2 , p , coords ) ;
for ( int i = 0 ; i < 3 ; i + + )
{
if ( coords [ i ] < 0.0f | | coords [ i ] > 1.0f )
return false ;
}
float sum = coords [ 0 ] + coords [ 1 ] + coords [ 2 ] ;
if ( sum > 1.0f )
return false ;
return true ;
}
bool LoadFileIntoBuffer ( CUtlBuffer & buf , const char * pFilename )
{
FileHandle_t fileHandle = g_pFileSystem - > Open ( pFilename , " rb " ) ;
if ( ! fileHandle )
return false ;
// Get the file size
int texSize = g_pFileSystem - > Size ( fileHandle ) ;
buf . EnsureCapacity ( texSize ) ;
int nBytesRead = g_pFileSystem - > Read ( buf . Base ( ) , texSize , fileHandle ) ;
g_pFileSystem - > Close ( fileHandle ) ;
buf . SeekPut ( CUtlBuffer : : SEEK_HEAD , nBytesRead ) ;
buf . SeekGet ( CUtlBuffer : : SEEK_HEAD , 0 ) ;
return true ;
}
// keeps a list of all textures that cast shadows via alpha channel
class CShadowTextureList
{
public :
// This loads a vtf and converts it to RGB8888 format
unsigned char * LoadVTFRGB8888 ( const char * pName , int * pWidth , int * pHeight , bool * pClampU , bool * pClampV )
{
char szPath [ MAX_PATH ] ;
Q_strncpy ( szPath , " materials/ " , sizeof ( szPath ) ) ;
Q_strncat ( szPath , pName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_strncat ( szPath , " .vtf " , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
CUtlBuffer buf ;
if ( ! LoadFileIntoBuffer ( buf , szPath ) )
return NULL ;
IVTFTexture * pTex = CreateVTFTexture ( ) ;
if ( ! pTex - > Unserialize ( buf ) )
return NULL ;
Msg ( " Loaded alpha texture %s \n " , szPath ) ;
unsigned char * pSrcImage = pTex - > ImageData ( 0 , 0 , 0 , 0 , 0 , 0 ) ;
int iWidth = pTex - > Width ( ) ;
int iHeight = pTex - > Height ( ) ;
ImageFormat dstFormat = IMAGE_FORMAT_RGBA8888 ;
ImageFormat srcFormat = pTex - > Format ( ) ;
* pClampU = ( pTex - > Flags ( ) & TEXTUREFLAGS_CLAMPS ) ? true : false ;
* pClampV = ( pTex - > Flags ( ) & TEXTUREFLAGS_CLAMPT ) ? true : false ;
unsigned char * pDstImage = new unsigned char [ ImageLoader : : GetMemRequired ( iWidth , iHeight , 1 , dstFormat , false ) ] ;
if ( ! ImageLoader : : ConvertImageFormat ( pSrcImage , srcFormat ,
pDstImage , dstFormat , iWidth , iHeight , 0 , 0 ) )
{
delete [ ] pDstImage ;
return NULL ;
}
* pWidth = iWidth ;
* pHeight = iHeight ;
return pDstImage ;
}
// Checks the database for the material and loads if necessary
// returns true if found and pIndex will be the index, -1 if no alpha shadows
bool FindOrLoadIfValid ( const char * pMaterialName , int * pIndex )
{
* pIndex = - 1 ;
int index = m_Textures . Find ( pMaterialName ) ;
bool bFound = false ;
if ( index ! = m_Textures . InvalidIndex ( ) )
{
bFound = true ;
* pIndex = index ;
}
else
{
KeyValues * pVMT = new KeyValues ( " vmt " ) ;
CUtlBuffer buf ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
LoadFileIntoBuffer ( buf , pMaterialName ) ;
if ( pVMT - > LoadFromBuffer ( pMaterialName , buf ) )
{
bFound = true ;
if ( pVMT - > FindKey ( " $translucent " ) | | pVMT - > FindKey ( " $alphatest " ) )
{
KeyValues * pBaseTexture = pVMT - > FindKey ( " $basetexture " ) ;
if ( pBaseTexture )
{
const char * pBaseTextureName = pBaseTexture - > GetString ( ) ;
if ( pBaseTextureName )
{
int w , h ;
bool bClampU = false ;
bool bClampV = false ;
unsigned char * pImageBits = LoadVTFRGB8888 ( pBaseTextureName , & w , & h , & bClampU , & bClampV ) ;
if ( pImageBits )
{
int index = m_Textures . Insert ( pMaterialName ) ;
m_Textures [ index ] . InitFromRGB8888 ( w , h , pImageBits ) ;
* pIndex = index ;
if ( pVMT - > FindKey ( " $nocull " ) )
{
// UNDONE: Support this? Do we need to emit two triangles?
m_Textures [ index ] . allowBackface = true ;
}
m_Textures [ index ] . clampU = bClampU ;
m_Textures [ index ] . clampV = bClampV ;
delete [ ] pImageBits ;
}
}
}
}
}
pVMT - > deleteThis ( ) ;
}
return bFound ;
}
// iterate the textures for the model and load each one into the database
// this is used on models marked to cast texture shadows
void LoadAllTexturesForModel ( studiohdr_t * pHdr , int * pTextureList )
{
for ( int i = 0 ; i < pHdr - > numtextures ; i + + )
{
int textureIndex = - 1 ;
// try to add each texture to the transparent shadow manager
char szPath [ MAX_PATH ] ;
// iterate quietly through all specified directories until a valid material is found
for ( int j = 0 ; j < pHdr - > numcdtextures ; j + + )
{
Q_strncpy ( szPath , " materials/ " , sizeof ( szPath ) ) ;
Q_strncat ( szPath , pHdr - > pCdtexture ( j ) , sizeof ( szPath ) ) ;
const char * textureName = pHdr - > pTexture ( i ) - > pszName ( ) ;
Q_strncat ( szPath , textureName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_strncat ( szPath , " .vmt " , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
if ( FindOrLoadIfValid ( szPath , & textureIndex ) )
break ;
}
pTextureList [ i ] = textureIndex ;
}
}
int AddMaterialEntry ( int shadowTextureIndex , const Vector2D & t0 , const Vector2D & t1 , const Vector2D & t2 )
{
int index = m_MaterialEntries . AddToTail ( ) ;
m_MaterialEntries [ index ] . textureIndex = shadowTextureIndex ;
m_MaterialEntries [ index ] . uv [ 0 ] = t0 ;
m_MaterialEntries [ index ] . uv [ 1 ] = t1 ;
m_MaterialEntries [ index ] . uv [ 2 ] = t2 ;
return index ;
}
// HACKHACK: Compute the average coverage for this triangle by sampling the AABB of its texture space
float ComputeCoverageForTriangle ( int shadowTextureIndex , const Vector2D & t0 , const Vector2D & t1 , const Vector2D & t2 )
{
2023-05-13 21:11:15 +03:00
float umin = min ( t0 . x , t1 . x ) ;
umin = min ( umin , t2 . x ) ;
float umax = max ( t0 . x , t1 . x ) ;
umax = max ( umax , t2 . x ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
float vmin = min ( t0 . y , t1 . y ) ;
vmin = min ( vmin , t2 . y ) ;
float vmax = max ( t0 . y , t1 . y ) ;
vmax = max ( vmax , t2 . y ) ;
2020-04-22 12:56:21 -04:00
// UNDONE: Do something about tiling
umin = clamp ( umin , 0 , 1 ) ;
umax = clamp ( umax , 0 , 1 ) ;
vmin = clamp ( vmin , 0 , 1 ) ;
vmax = clamp ( vmax , 0 , 1 ) ;
Assert ( umin > = 0.0f & & umax < = 1.0f ) ;
Assert ( vmin > = 0.0f & & vmax < = 1.0f ) ;
const alphatexture_t & tex = m_Textures . Element ( shadowTextureIndex ) ;
int u0 = umin * ( tex . width - 1 ) ;
int u1 = umax * ( tex . width - 1 ) ;
int v0 = vmin * ( tex . height - 1 ) ;
int v1 = vmax * ( tex . height - 1 ) ;
int total = 0 ;
int count = 0 ;
for ( int v = v0 ; v < = v1 ; v + + )
{
int row = ( v * tex . width ) ;
for ( int u = u0 ; u < = u1 ; u + + )
{
total + = tex . pAlphaTexels [ row + u ] ;
count + + ;
}
}
if ( count )
{
float coverage = float ( total ) / ( count * 255.0f ) ;
return coverage ;
}
return 1.0f ;
}
int SampleMaterial ( int materialIndex , const Vector & coords , bool bBackface )
{
const materialentry_t & mat = m_MaterialEntries [ materialIndex ] ;
const alphatexture_t & tex = m_Textures . Element ( m_MaterialEntries [ materialIndex ] . textureIndex ) ;
if ( bBackface & & ! tex . allowBackface )
return 0 ;
Vector2D uv = coords . x * mat . uv [ 0 ] + coords . y * mat . uv [ 1 ] + coords . z * mat . uv [ 2 ] ;
2023-05-13 21:11:15 +03:00
// bilinear filtered sample
float ou = uv [ 0 ] * tex . width ;
float ov = uv [ 1 ] * tex . height ;
int u = floor ( ou ) ;
int v = floor ( ov ) ;
int u1 = u + 1 ;
int v1 = v + 1 ;
2020-04-22 12:56:21 -04:00
u & = ( tex . width - 1 ) ;
2023-05-13 21:11:15 +03:00
u1 & = ( tex . width - 1 ) ;
2020-04-22 12:56:21 -04:00
v & = ( tex . height - 1 ) ;
2023-05-13 21:11:15 +03:00
v1 & = ( tex . height - 1 ) ;
float lerpU = ou - u ;
float lerpV = ov - v ;
int x = ( tex . pAlphaTexels [ v * tex . width + u ] * ( 1 - lerpU ) ) + ( lerpU * tex . pAlphaTexels [ v * tex . width + u1 ] ) ;
int y = ( tex . pAlphaTexels [ v1 * tex . width + u ] * ( 1 - lerpU ) ) + ( lerpU * tex . pAlphaTexels [ v1 * tex . width + u1 ] ) ;
return int ( x * ( 1 - lerpV ) + ( y * lerpV ) ) ;
}
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
void GetMapping ( int shadowTextureIndex , int * pWidth , int * pHeight )
{
* pWidth = m_Textures [ shadowTextureIndex ] . width ;
* pHeight = m_Textures [ shadowTextureIndex ] . height ;
2020-04-22 12:56:21 -04:00
}
struct alphatexture_t
{
short width ;
short height ;
bool allowBackface ;
bool clampU ;
bool clampV ;
unsigned char * pAlphaTexels ;
void InitFromRGB8888 ( int w , int h , unsigned char * pTexels )
{
width = w ;
height = h ;
pAlphaTexels = new unsigned char [ w * h ] ;
for ( int i = 0 ; i < h ; i + + )
{
for ( int j = 0 ; j < w ; j + + )
{
int index = ( i * w ) + j ;
pAlphaTexels [ index ] = pTexels [ index * 4 + 3 ] ;
}
}
}
} ;
struct materialentry_t
{
int textureIndex ;
Vector2D uv [ 3 ] ;
} ;
// this is the list of textures we've loaded
// only load each one once
CUtlDict < alphatexture_t , unsigned short > m_Textures ;
CUtlVector < materialentry_t > m_MaterialEntries ;
} ;
// global to keep the shadow-casting texture list and their alpha bits
CShadowTextureList g_ShadowTextureList ;
float ComputeCoverageFromTexture ( float b0 , float b1 , float b2 , int32 hitID )
{
const float alphaScale = 1.0f / 255.0f ;
// UNDONE: Pass ray down to determine backfacing?
//Vector normal( tri.m_flNx, tri.m_flNy, tri.m_flNz );
//bool bBackface = DotProduct(delta, tri.N) > 0 ? true : false;
Vector coords ( b0 , b1 , b2 ) ;
return alphaScale * g_ShadowTextureList . SampleMaterial ( g_RtEnv . GetTriangleMaterial ( hitID ) , coords , false ) ;
}
// this is here to strip models/ or .mdl or whatnot
void CleanModelName ( const char * pModelName , char * pOutput , int outLen )
{
// strip off leading models/ if it exists
const char * pModelDir = " models/ " ;
int modelLen = Q_strlen ( pModelDir ) ;
if ( ! Q_strnicmp ( pModelName , pModelDir , modelLen ) )
{
pModelName + = modelLen ;
}
Q_strncpy ( pOutput , pModelName , outLen ) ;
// truncate any .mdl extension
char * dot = strchr ( pOutput , ' . ' ) ;
if ( dot )
{
* dot = 0 ;
}
2023-05-13 21:11:15 +03:00
}
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
int LoadShadowTexture ( const char * pMaterialName )
{
int textureIndex = - 1 ;
// try to add each texture to the transparent shadow manager
char szPath [ MAX_PATH ] ;
Q_strncpy ( szPath , " materials/ " , sizeof ( szPath ) ) ;
Q_strncat ( szPath , pMaterialName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_strncat ( szPath , " .vmt " , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
g_ShadowTextureList . FindOrLoadIfValid ( szPath , & textureIndex ) ;
return textureIndex ;
}
int AddShadowTextureTriangle ( int shadowTextureIndex , const Vector2D & t0 , const Vector2D & t1 , const Vector2D & t2 )
{
return g_ShadowTextureList . AddMaterialEntry ( shadowTextureIndex , t0 , t1 , t2 ) ;
}
float ComputeCoverageForTriangle ( int shadowTextureIndex , const Vector2D & t0 , const Vector2D & t1 , const Vector2D & t2 )
{
return g_ShadowTextureList . ComputeCoverageForTriangle ( shadowTextureIndex , t0 , t1 , t2 ) ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
void GetShadowTextureMapping ( int shadowTextureIndex , int * pWidth , int * pHeight )
{
g_ShadowTextureList . GetMapping ( shadowTextureIndex , pWidth , pHeight ) ;
}
2020-04-22 12:56:21 -04:00
void ForceTextureShadowsOnModel ( const char * pModelName )
{
char buf [ 1024 ] ;
CleanModelName ( pModelName , buf , sizeof ( buf ) ) ;
if ( ! g_ForcedTextureShadowsModels . Find ( buf ) . IsValid ( ) )
{
g_ForcedTextureShadowsModels . AddString ( buf ) ;
}
}
bool IsModelTextureShadowsForced ( const char * pModelName )
{
char buf [ 1024 ] ;
CleanModelName ( pModelName , buf , sizeof ( buf ) ) ;
return g_ForcedTextureShadowsModels . Find ( buf ) . IsValid ( ) ;
}
2023-05-13 21:11:15 +03:00
bool IsStaticPropBumpmapped ( studiohdr_t * pStudioHdr )
{
if ( g_numVradStaticPropsLightingStreams = = 1 )
{
return false ;
}
// check if prop uses "$bumpmap" in any materials, use this as an indication of valid tangent data (availability of tangentdata does not imply it's valid/used)
for ( int textureIndex = 0 ; textureIndex < pStudioHdr - > numtextures ; textureIndex + + )
{
char szPath [ MAX_PATH ] ;
// iterate quietly through all specified directories until a valid material is found
for ( int i = 0 ; i < pStudioHdr - > numcdtextures ; i + + )
{
Q_strncpy ( szPath , " materials/ " , sizeof ( szPath ) ) ;
Q_strncat ( szPath , pStudioHdr - > pCdtexture ( i ) , sizeof ( szPath ) ) ;
const char * textureName = pStudioHdr - > pTexture ( textureIndex ) - > pszName ( ) ;
Q_strncat ( szPath , textureName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_strncat ( szPath , " .vmt " , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
KeyValues * pVMT = new KeyValues ( " vmt " ) ;
CUtlBuffer buf ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
LoadFileIntoBuffer ( buf , szPath ) ;
if ( pVMT - > LoadFromBuffer ( szPath , buf ) )
{
if ( pVMT - > FindKey ( " $bumpmap " ) )
{
pVMT - > deleteThis ( ) ;
return true ;
}
}
pVMT - > deleteThis ( ) ;
}
}
return false ;
}
void StaticPropHasPhongBump ( studiohdr_t * pStudioHdr , bool * pHasBumpmap , bool * pHasPhong )
{
if ( g_numVradStaticPropsLightingStreams = = 1 )
{
return ;
}
* pHasBumpmap = false ;
* pHasPhong = false ;
// check if prop uses "$bumpmap" in any materials, use this as an indication of valid tangent data (availability of tangentdata does not imply it's valid/used)
for ( int textureIndex = 0 ; textureIndex < pStudioHdr - > numtextures ; textureIndex + + )
{
char szPath [ MAX_PATH ] ;
// iterate quietly through all specified directories until a valid material is found
for ( int i = 0 ; i < pStudioHdr - > numcdtextures ; i + + )
{
Q_strncpy ( szPath , " materials/ " , sizeof ( szPath ) ) ;
Q_strncat ( szPath , pStudioHdr - > pCdtexture ( i ) , sizeof ( szPath ) ) ;
const char * textureName = pStudioHdr - > pTexture ( textureIndex ) - > pszName ( ) ;
Q_strncat ( szPath , textureName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_strncat ( szPath , " .vmt " , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
KeyValues * pVMT = new KeyValues ( " vmt " ) ;
CUtlBuffer buf ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
LoadFileIntoBuffer ( buf , szPath ) ;
if ( pVMT - > LoadFromBuffer ( szPath , buf ) )
{
if ( pVMT - > FindKey ( " $bumpmap " ) )
{
* pHasBumpmap = true ;
// is it also phong
if ( pVMT - > FindKey ( " $phong " ) )
{
* pHasPhong = true ;
pVMT - > deleteThis ( ) ;
return ;
}
}
}
pVMT - > deleteThis ( ) ;
}
}
return ;
}
Vector ReadReflectivityFromVTF ( const char * pName )
{
Vector vRefl ( 0.18f , 0.18f , 0.18f ) ;
char szPath [ MAX_PATH ] ;
Q_strncpy ( szPath , " materials/ " , sizeof ( szPath ) ) ;
Q_strncat ( szPath , pName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_strncat ( szPath , " .vtf " , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
int nHeaderSize = VTFFileHeaderSize ( ) ;
unsigned char * pMem = ( unsigned char * ) stackalloc ( nHeaderSize ) ;
CUtlBuffer buf ( pMem , nHeaderSize ) ;
if ( g_pFullFileSystem - > ReadFile ( szPath , NULL , buf , nHeaderSize ) )
{
IVTFTexture * pTex = CreateVTFTexture ( ) ;
if ( pTex - > Unserialize ( buf , true ) )
{
vRefl = pTex - > Reflectivity ( ) ;
}
DestroyVTFTexture ( pTex ) ;
}
return vRefl ;
}
Vector ComputeStaticPropReflectivity ( studiohdr_t * pStudioHdr )
{
Vector vReflectivity ( 0.18f , 0.18f , 0.18f ) ;
for ( int textureIndex = 0 ; textureIndex < pStudioHdr - > numtextures ; textureIndex + + )
{
char szPath [ MAX_PATH ] ;
// iterate quietly through all specified directories until a valid material is found
for ( int i = 0 ; i < pStudioHdr - > numcdtextures ; i + + )
{
Q_strncpy ( szPath , " materials/ " , sizeof ( szPath ) ) ;
Q_strncat ( szPath , pStudioHdr - > pCdtexture ( i ) , sizeof ( szPath ) ) ;
const char * textureName = pStudioHdr - > pTexture ( textureIndex ) - > pszName ( ) ;
Q_strncat ( szPath , textureName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_strncat ( szPath , " .vmt " , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
Vector vVtfRefl ( 1.0f , 1.0f , 1.0f ) ;
Vector vTint ( 1.0f , 1.0f , 1.0f ) ;
KeyValues * pVMT = new KeyValues ( " vmt " ) ;
CUtlBuffer buf ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
LoadFileIntoBuffer ( buf , szPath ) ;
if ( pVMT - > LoadFromBuffer ( szPath , buf ) )
{
KeyValues * pBaseTexture = pVMT - > FindKey ( " $basetexture " ) ;
if ( pBaseTexture )
{
const char * pBaseTextureName = pBaseTexture - > GetString ( ) ;
if ( pBaseTextureName )
{
vVtfRefl = ReadReflectivityFromVTF ( pBaseTextureName ) ;
}
}
vReflectivity = vVtfRefl ;
KeyValues * pColorTint = pVMT - > FindKey ( " color " ) ;
if ( pColorTint )
{
const char * pColorString = pColorTint - > GetString ( ) ;
if ( pColorString [ 0 ] = = ' { ' )
{
int r = 0 ;
int g = 0 ;
int b = 0 ;
sscanf ( pColorString , " {%d %d %d} " , & r , & g , & b ) ;
vTint . x = SrgbGammaToLinear ( clamp ( float ( r ) / 255.0f , 0.0f , 1.0f ) ) ;
vTint . y = SrgbGammaToLinear ( clamp ( float ( r ) / 255.0f , 0.0f , 1.0f ) ) ;
vTint . z = SrgbGammaToLinear ( clamp ( float ( r ) / 255.0f , 0.0f , 1.0f ) ) ;
}
else if ( pColorString [ 0 ] = = ' [ ' )
{
sscanf ( pColorString , " [%f %f %f] " , & vTint . x , & vTint . y , & vTint . z ) ;
vTint . x = clamp ( vTint . x , 0.0f , 1.0f ) ;
vTint . y = clamp ( vTint . y , 0.0f , 1.0f ) ;
vTint . z = clamp ( vTint . z , 0.0f , 1.0f ) ;
}
}
}
pVMT - > deleteThis ( ) ;
vReflectivity = vVtfRefl * vTint ;
if ( vReflectivity . x = = 1.0f & & vReflectivity . y = = 1.0f & & vReflectivity . z = = 1.0f )
{
vReflectivity . Init ( 0.18f , 0.18f , 0.18f ) ;
}
return vReflectivity ;
}
}
return vReflectivity ;
}
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Creates a collision model (based on the render geometry!)
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : CreateCollisionModel ( char const * pModelName )
{
CUtlBuffer buf ;
CUtlBuffer bufvtx ;
CUtlBuffer bufphy ;
int i = m_StaticPropDict . AddToTail ( ) ;
m_StaticPropDict [ i ] . m_pModel = NULL ;
m_StaticPropDict [ i ] . m_pStudioHdr = NULL ;
if ( ! LoadStudioModel ( pModelName , buf ) )
{
VectorCopy ( vec3_origin , m_StaticPropDict [ i ] . m_Mins ) ;
VectorCopy ( vec3_origin , m_StaticPropDict [ i ] . m_Maxs ) ;
return ;
}
studiohdr_t * pHdr = ( studiohdr_t * ) buf . Base ( ) ;
VectorCopy ( pHdr - > hull_min , m_StaticPropDict [ i ] . m_Mins ) ;
VectorCopy ( pHdr - > hull_max , m_StaticPropDict [ i ] . m_Maxs ) ;
if ( LoadStudioCollisionModel ( pModelName , bufphy ) )
{
phyheader_t header ;
bufphy . Get ( & header , sizeof ( header ) ) ;
vcollide_t * pCollide = & m_StaticPropDict [ i ] . m_loadedModel ;
s_pPhysCollision - > VCollideLoad ( pCollide , header . solidCount , ( const char * ) bufphy . PeekGet ( ) , bufphy . TellPut ( ) - bufphy . TellGet ( ) ) ;
m_StaticPropDict [ i ] . m_pModel = m_StaticPropDict [ i ] . m_loadedModel . solids [ 0 ] ;
/*
static int propNum = 0 ;
char tmp [ 128 ] ;
sprintf ( tmp , " staticprop%03d.txt " , propNum ) ;
DumpCollideToGlView ( pCollide , tmp ) ;
+ + propNum ;
*/
}
else
{
// mark this as unused
m_StaticPropDict [ i ] . m_loadedModel . solidCount = 0 ;
// CPhysCollide* pPhys = CreatePhysCollide( pHdr, pVtxHdr );
m_StaticPropDict [ i ] . m_pModel = ComputeConvexHull ( pHdr ) ;
}
// clone it
m_StaticPropDict [ i ] . m_pStudioHdr = ( studiohdr_t * ) malloc ( buf . Size ( ) ) ;
memcpy ( m_StaticPropDict [ i ] . m_pStudioHdr , ( studiohdr_t * ) buf . Base ( ) , buf . Size ( ) ) ;
if ( ! LoadVTXFile ( pModelName , m_StaticPropDict [ i ] . m_pStudioHdr , m_StaticPropDict [ i ] . m_VtxBuf ) )
{
// failed, leave state identified as disabled
m_StaticPropDict [ i ] . m_VtxBuf . Purge ( ) ;
}
if ( g_bTextureShadows )
{
if ( ( pHdr - > flags & STUDIOHDR_FLAGS_CAST_TEXTURE_SHADOWS ) | | IsModelTextureShadowsForced ( pModelName ) )
{
m_StaticPropDict [ i ] . m_textureShadowIndex . RemoveAll ( ) ;
m_StaticPropDict [ i ] . m_triangleMaterialIndex . RemoveAll ( ) ;
m_StaticPropDict [ i ] . m_textureShadowIndex . AddMultipleToTail ( pHdr - > numtextures ) ;
g_ShadowTextureList . LoadAllTexturesForModel ( pHdr , m_StaticPropDict [ i ] . m_textureShadowIndex . Base ( ) ) ;
}
}
2023-05-13 21:11:15 +03:00
// mark static props that use $bumpmap, $phong materials
StaticPropHasPhongBump ( pHdr , & m_StaticPropDict [ i ] . m_bHasBumpmap , & m_StaticPropDict [ i ] . m_bHasPhong ) ;
m_StaticPropDict [ i ] . m_vReflectivity = ComputeStaticPropReflectivity ( pHdr ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Unserialize static prop model dictionary
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : UnserializeModelDict ( CUtlBuffer & buf )
{
int count = buf . GetInt ( ) ;
while ( - - count > = 0 )
{
StaticPropDictLump_t lump ;
buf . Get ( & lump , sizeof ( StaticPropDictLump_t ) ) ;
CreateCollisionModel ( lump . m_Name ) ;
}
2023-05-13 21:11:15 +03:00
// spew bump static prop info
if ( g_bDumpBumpStaticProps & & ( g_numVradStaticPropsLightingStreams = = 3 ) )
{
char mapName [ MAX_PATH ] ;
Q_FileBase ( source , mapName , sizeof ( mapName ) ) ;
char bumpPropFilename [ MAX_PATH ] ;
sprintf ( bumpPropFilename , " vrad_bumpstaticprops_%s.txt " , mapName ) ;
Msg ( " Writing %s... \n " , bumpPropFilename ) ;
FILE * fp = fopen ( bumpPropFilename , " w " ) ;
if ( ! fp )
{
Msg ( " Writing %s...failed \n " , bumpPropFilename ) ;
return ;
}
fprintf ( fp , " Bumpmap static prop list for %s \n " , mapName ) ;
int numBumpmapStaticProps = 0 ;
int numPhongStaticProps = 0 ;
for ( int i = m_StaticPropDict . Count ( ) ; - - i > = 0 ; )
{
studiohdr_t * pStudioHdr = m_StaticPropDict [ i ] . m_pStudioHdr ;
if ( m_StaticPropDict [ i ] . m_bHasBumpmap )
{
numBumpmapStaticProps + + ;
}
if ( m_StaticPropDict [ i ] . m_bHasPhong )
{
numPhongStaticProps + + ;
}
if ( m_StaticPropDict [ i ] . m_bHasBumpmap | | m_StaticPropDict [ i ] . m_bHasPhong )
{
fprintf ( fp , " \n prop: %s \n vmt's containing $bumpmap, $phong: \n " , pStudioHdr - > pszName ( ) ) ;
for ( int textureIndex = 0 ; textureIndex < pStudioHdr - > numtextures ; textureIndex + + )
{
char szPath [ MAX_PATH ] ;
// iterate quietly through all specified directories until a valid material is found
for ( int i = 0 ; i < pStudioHdr - > numcdtextures ; i + + )
{
Q_strncpy ( szPath , " materials/ " , sizeof ( szPath ) ) ;
Q_strncat ( szPath , pStudioHdr - > pCdtexture ( i ) , sizeof ( szPath ) ) ;
const char * textureName = pStudioHdr - > pTexture ( textureIndex ) - > pszName ( ) ;
Q_strncat ( szPath , textureName , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_strncat ( szPath , " .vmt " , sizeof ( szPath ) , COPY_ALL_CHARACTERS ) ;
Q_FixSlashes ( szPath , CORRECT_PATH_SEPARATOR ) ;
KeyValues * pVMT = new KeyValues ( " vmt " ) ;
CUtlBuffer buf ( 0 , 0 , CUtlBuffer : : TEXT_BUFFER ) ;
LoadFileIntoBuffer ( buf , szPath ) ;
if ( pVMT - > LoadFromBuffer ( szPath , buf ) )
{
if ( pVMT - > FindKey ( " $bumpmap " ) )
{
if ( pVMT - > FindKey ( " $phong " ) )
{
fprintf ( fp , " $bump, $phong: %s \n " , szPath ) ;
}
else
{
fprintf ( fp , " $bump: %s \n " , szPath ) ;
}
}
else if ( pVMT - > FindKey ( " $phong " ) )
{
// not possible/error?
fprintf ( fp , " $phong: %s \n " , szPath ) ;
}
}
pVMT - > deleteThis ( ) ;
}
}
}
}
fprintf ( fp , " \n %d static props, %d bumped static props (%d phong static props) \n " , m_StaticPropDict . Count ( ) , numBumpmapStaticProps , numPhongStaticProps ) ;
fclose ( fp ) ;
}
2020-04-22 12:56:21 -04:00
}
void CVradStaticPropMgr : : UnserializeModels ( CUtlBuffer & buf )
{
int count = buf . GetInt ( ) ;
m_StaticProps . AddMultipleToTail ( count ) ;
for ( int i = 0 ; i < count ; + + i )
{
StaticPropLump_t lump ;
buf . Get ( & lump , sizeof ( StaticPropLump_t ) ) ;
VectorCopy ( lump . m_Origin , m_StaticProps [ i ] . m_Origin ) ;
VectorCopy ( lump . m_Angles , m_StaticProps [ i ] . m_Angles ) ;
VectorCopy ( lump . m_LightingOrigin , m_StaticProps [ i ] . m_LightingOrigin ) ;
m_StaticProps [ i ] . m_bLightingOriginValid = ( lump . m_Flags & STATIC_PROP_USE_LIGHTING_ORIGIN ) > 0 ;
m_StaticProps [ i ] . m_ModelIdx = lump . m_PropType ;
m_StaticProps [ i ] . m_Handle = TREEDATA_INVALID_HANDLE ;
m_StaticProps [ i ] . m_Flags = lump . m_Flags ;
2023-05-13 21:11:15 +03:00
m_StaticProps [ i ] . m_FlagsEx = lump . m_FlagsEx ;
m_StaticProps [ i ] . m_vReflectivity . Init ( SrgbGammaToLinear ( float ( lump . m_DiffuseModulation . r ) / 255.0f ) ,
SrgbGammaToLinear ( float ( lump . m_DiffuseModulation . g ) / 255.0f ) ,
SrgbGammaToLinear ( float ( lump . m_DiffuseModulation . b ) / 255.0f ) ) ;
m_StaticProps [ i ] . m_vReflectivity * = m_StaticPropDict [ m_StaticProps [ i ] . m_ModelIdx ] . m_vReflectivity ;
2020-04-22 12:56:21 -04:00
}
}
//-----------------------------------------------------------------------------
// Unserialize static props
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : UnserializeStaticProps ( )
{
// Unserialize static props, insert them into the appropriate leaves
GameLumpHandle_t handle = g_GameLumps . GetGameLumpHandle ( GAMELUMP_STATIC_PROPS ) ;
int size = g_GameLumps . GameLumpSize ( handle ) ;
if ( ! size )
return ;
if ( g_GameLumps . GetGameLumpVersion ( handle ) ! = GAMELUMP_STATIC_PROPS_VERSION )
{
2023-05-13 21:11:15 +03:00
Error ( " Cannot load the static props... encountered a stale map version. Re-vbsp the map. " ) ;
2020-04-22 12:56:21 -04:00
}
if ( g_GameLumps . GetGameLump ( handle ) )
{
CUtlBuffer buf ( g_GameLumps . GetGameLump ( handle ) , size , CUtlBuffer : : READ_ONLY ) ;
UnserializeModelDict ( buf ) ;
// Skip the leaf list data
int count = buf . GetInt ( ) ;
buf . SeekGet ( CUtlBuffer : : SEEK_CURRENT , count * sizeof ( StaticPropLeafLump_t ) ) ;
UnserializeModels ( buf ) ;
}
}
//-----------------------------------------------------------------------------
// Level init, shutdown
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : Init ( )
{
CreateInterfaceFn physicsFactory = GetPhysicsFactory ( ) ;
if ( ! physicsFactory )
Error ( " Unable to load vphysics DLL. " ) ;
s_pPhysCollision = ( IPhysicsCollision * ) physicsFactory ( VPHYSICS_COLLISION_INTERFACE_VERSION , NULL ) ;
if ( ! s_pPhysCollision )
{
Error ( " Unable to get '%s' for physics interface. " , VPHYSICS_COLLISION_INTERFACE_VERSION ) ;
return ;
}
// Read in static props that have been compiled into the bsp file
UnserializeStaticProps ( ) ;
}
void CVradStaticPropMgr : : Shutdown ( )
{
// Remove all static prop model data
2023-05-13 21:11:15 +03:00
for ( int i = m_StaticPropDict . Count ( ) ; - - i > = 0 ; )
2020-04-22 12:56:21 -04:00
{
studiohdr_t * pStudioHdr = m_StaticPropDict [ i ] . m_pStudioHdr ;
if ( pStudioHdr )
{
2023-05-09 19:57:22 +03:00
if ( pStudioHdr - > VertexBase ( ) )
2020-04-22 12:56:21 -04:00
{
2023-05-09 19:57:22 +03:00
free ( pStudioHdr - > VertexBase ( ) ) ;
2023-05-13 21:11:15 +03:00
pStudioHdr - > SetVertexBase ( nullptr ) ;
2020-04-22 12:56:21 -04:00
}
free ( pStudioHdr ) ;
}
}
m_StaticProps . Purge ( ) ;
m_StaticPropDict . Purge ( ) ;
}
void ComputeLightmapColor ( dface_t * pFace , Vector & color )
{
texinfo_t * pTex = & texinfo [ pFace - > texinfo ] ;
if ( pTex - > flags & SURF_SKY )
{
// sky ambient already accounted for in direct component
return ;
}
}
bool PositionInSolid ( Vector & position )
{
2023-05-13 21:11:15 +03:00
/* Testing enabling/disabling since it erroneously reports verts inside light blockers
and there are a number of position offsets applied elsewhere to avoid surface acne
that might well be enough */
if ( g_bDisableStaticPropVertexInSolidTest )
{
return false ;
}
2020-04-22 12:56:21 -04:00
int ndxLeaf = PointLeafnum ( position ) ;
if ( dleafs [ ndxLeaf ] . contents & CONTENTS_SOLID )
{
// position embedded in solid
return true ;
}
return false ;
}
2023-05-13 21:11:15 +03:00
bool PositionIn3DSkybox ( Vector & position )
{
int iLeaf = PointLeafnum ( position ) ;
int area = dleafs [ iLeaf ] . area ;
return area_sky_cameras [ area ] > = 0 ;
}
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Trace from a vertex to each direct light source, accumulating its contribution.
//-----------------------------------------------------------------------------
2023-05-13 21:11:15 +03:00
void ComputeDirectLightingAtPoint ( Vector & position , Vector * normals , Vector * outColors , float * outSunAmount , int numNormals , bool bSkipSkyLight , int iThread ,
int static_prop_id_to_skip , int nLFlags )
2020-04-22 12:56:21 -04:00
{
SSE_sampleLightOutput_t sampleOutput ;
2023-05-13 21:11:15 +03:00
for ( int k = 0 ; k < numNormals ; + + k )
{
outColors [ k ] . Init ( ) ;
outSunAmount [ k ] = 0.0f ;
}
2020-04-22 12:56:21 -04:00
// Iterate over all direct lights and accumulate their contribution
int cluster = ClusterFromPoint ( position ) ;
for ( directlight_t * dl = activelights ; dl ! = NULL ; dl = dl - > next )
{
if ( dl - > light . style )
{
// skip lights with style
continue ;
}
// is this lights cluster visible?
if ( ! PVSCheck ( dl - > pvs , cluster ) )
continue ;
// push the vertex towards the light to avoid surface acne
Vector adjusted_pos = position ;
float flEpsilon = 0.0 ;
2023-05-13 21:11:15 +03:00
const float flFudgeFactor = 4.0 ;
2020-04-22 12:56:21 -04:00
if ( dl - > light . type ! = emit_skyambient )
{
// push towards the light
Vector fudge ;
if ( dl - > light . type = = emit_skylight )
fudge = - ( dl - > light . normal ) ;
else
{
fudge = dl - > light . origin - position ;
VectorNormalize ( fudge ) ;
}
2023-05-13 21:11:15 +03:00
fudge * = flFudgeFactor ;
2020-04-22 12:56:21 -04:00
adjusted_pos + = fudge ;
}
else
{
// push out along normal
2023-05-13 21:11:15 +03:00
adjusted_pos + = flFudgeFactor * normals [ 0 ] ;
2020-04-22 12:56:21 -04:00
// flEpsilon = 1.0;
}
FourVectors adjusted_pos4 ;
adjusted_pos4 . DuplicateVector ( adjusted_pos ) ;
2023-05-13 21:11:15 +03:00
FourVectors normal4 ;
switch ( numNormals )
{
case 4 :
normal4 . LoadAndSwizzle ( normals [ 0 ] , normals [ 1 ] , normals [ 2 ] , normals [ 3 ] ) ;
break ;
case 3 :
normal4 . LoadAndSwizzle ( normals [ 0 ] , normals [ 1 ] , normals [ 2 ] , normals [ 0 ] ) ;
break ;
default :
normal4 . DuplicateVector ( normals [ 0 ] ) ;
break ;
}
GatherSampleLightSSE ( sampleOutput , dl , - 1 , adjusted_pos4 , & normal4 ,
1 , // really it's number of FourVectors passed
iThread , g_bFastStaticProps ? ( nLFlags | GATHERLFLAGS_FORCE_FAST ) : nLFlags ,
static_prop_id_to_skip , flEpsilon ) ;
2023-05-09 19:57:22 +03:00
2023-05-13 21:11:15 +03:00
for ( int k = 0 ; k < numNormals ; + + k )
{
if ( ! ( ( dl - > light . type = = emit_skylight ) & & bSkipSkyLight ) )
{
VectorMA ( outColors [ k ] ,
sampleOutput . m_flFalloff [ k ] * sampleOutput . m_flDot [ 0 ] [ k ] ,
dl - > light . intensity ,
outColors [ k ] ) ;
}
outSunAmount [ k ] + = SubFloat ( sampleOutput . m_flSunAmount [ 0 ] , k ) * ( sampleOutput . m_flDot [ 0 ] [ 0 ] > 0.0f ? 1.0f : 0.0f ) ;
}
2020-04-22 12:56:21 -04:00
}
}
//-----------------------------------------------------------------------------
2023-05-13 21:11:15 +03:00
// version of above that just computes/returns the sun amount
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
2023-05-13 21:11:15 +03:00
void ComputeSunAmountAtPoint ( Vector & position , Vector * normals , float * outSunAmount , int numNormals , int iThread ,
int static_prop_id_to_skip = - 1 , int nLFlags = 0 )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
SSE_sampleLightOutput_t sampleOutput ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
for ( int k = 0 ; k < numNormals ; + + k )
{
outSunAmount [ k ] = 0.0f ;
}
// Iterate over all direct lights and accumulate their contribution
int cluster = ClusterFromPoint ( position ) ;
for ( directlight_t * dl = activelights ; dl ! = NULL ; dl = dl - > next )
{
if ( dl - > light . style )
{
// skip lights with style
continue ;
}
if ( dl - > light . type ! = emit_skylight )
{
// skip lights that don't contribue to sunamount
continue ;
}
// is this lights cluster visible?
if ( ! PVSCheck ( dl - > pvs , cluster ) )
continue ;
// push the vertex towards the light to avoid surface acne
Vector adjusted_pos = position ;
float flEpsilon = 0.0 ;
const float flFudgeFactor = 4.0 ;
// push towards the light
Vector fudge ;
fudge = - ( dl - > light . normal ) ;
fudge * = flFudgeFactor ;
adjusted_pos + = fudge ;
FourVectors adjusted_pos4 ;
adjusted_pos4 . DuplicateVector ( adjusted_pos ) ;
FourVectors normal4 ;
switch ( numNormals )
{
case 4 :
normal4 . LoadAndSwizzle ( normals [ 0 ] , normals [ 1 ] , normals [ 2 ] , normals [ 3 ] ) ;
break ;
case 3 :
normal4 . LoadAndSwizzle ( normals [ 0 ] , normals [ 1 ] , normals [ 2 ] , normals [ 0 ] ) ;
break ;
default :
normal4 . DuplicateVector ( normals [ 0 ] ) ;
break ;
}
GatherSampleLightSSE ( sampleOutput , dl , - 1 , adjusted_pos4 , & normal4 ,
1 , // really it's number of FourVectors passed
iThread , g_bFastStaticProps ? ( nLFlags | GATHERLFLAGS_FORCE_FAST ) : nLFlags ,
static_prop_id_to_skip , flEpsilon ) ;
for ( int k = 0 ; k < numNormals ; + + k )
{
outSunAmount [ k ] + = SubFloat ( sampleOutput . m_flSunAmount [ 0 ] , k ) * ( sampleOutput . m_flDot [ 0 ] [ 0 ] > 0.0f ? 1.0f : 0.0f ) ;
}
}
}
//-----------------------------------------------------------------------------
// Takes the results from a ComputeLighting call and applies it to the static prop in question.
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : ApplyLightingToStaticProp ( CStaticProp & prop , const CComputeStaticPropLightingResults * pResults )
{
if ( pResults - > m_ColorVertsArrays . Count ( ) = = 0 )
return ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
StaticPropDict_t & dict = m_StaticPropDict [ prop . m_ModelIdx ] ;
studiohdr_t * pStudioHdr = dict . m_pStudioHdr ;
OptimizedModel : : FileHeader_t * pVtxHdr = ( OptimizedModel : : FileHeader_t * ) dict . m_VtxBuf . Base ( ) ;
Assert ( pStudioHdr & & pVtxHdr ) ;
int const numVertexLightComponents = g_numVradStaticPropsLightingStreams ;
int iCurColorVertsArray = 0 ;
2020-04-22 12:56:21 -04:00
for ( int bodyID = 0 ; bodyID < pStudioHdr - > numbodyparts ; + + bodyID )
{
OptimizedModel : : BodyPartHeader_t * pVtxBodyPart = pVtxHdr - > pBodyPart ( bodyID ) ;
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( bodyID ) ;
for ( int modelID = 0 ; modelID < pBodyPart - > nummodels ; + + modelID )
{
OptimizedModel : : ModelHeader_t * pVtxModel = pVtxBodyPart - > pModel ( modelID ) ;
mstudiomodel_t * pStudioModel = pBodyPart - > pModel ( modelID ) ;
2023-05-13 21:11:15 +03:00
const CUtlVector < colorVertex_t > & colorVerts = * pResults - > m_ColorVertsArrays [ iCurColorVertsArray + + ] ;
2020-04-22 12:56:21 -04:00
for ( int nLod = 0 ; nLod < pVtxHdr - > numLODs ; nLod + + )
{
OptimizedModel : : ModelLODHeader_t * pVtxLOD = pVtxModel - > pLOD ( nLod ) ;
for ( int nMesh = 0 ; nMesh < pStudioModel - > nummeshes ; + + nMesh )
{
mstudiomesh_t * pMesh = pStudioModel - > pMesh ( nMesh ) ;
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( nMesh ) ;
for ( int nGroup = 0 ; nGroup < pVtxMesh - > numStripGroups ; + + nGroup )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( nGroup ) ;
int nMeshIdx = prop . m_MeshData . AddToTail ( ) ;
2023-05-13 21:11:15 +03:00
prop . m_MeshData [ nMeshIdx ] . m_VertColorData . AddMultipleToTail ( pStripGroup - > numVerts * numVertexLightComponents ) ;
prop . m_MeshData [ nMeshIdx ] . m_numVerts = pStripGroup - > numVerts ;
prop . m_MeshData [ nMeshIdx ] . m_nLod = nLod ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
for ( int nVertex = 0 ; nVertex < pStripGroup - > numVerts ; + + nVertex )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
int nIndex = pMesh - > vertexoffset + pStripGroup - > pVertex ( nVertex ) - > origMeshVertID ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
Assert ( nIndex < pStudioModel - > numvertices ) ;
if ( numVertexLightComponents < = 1 )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
prop . m_MeshData [ nMeshIdx ] . m_VertColorData [ nVertex ] . AsVector3D ( ) = colorVerts [ nIndex ] . m_Colors [ 0 ] ;
prop . m_MeshData [ nMeshIdx ] . m_VertColorData [ nVertex ] . w = colorVerts [ nIndex ] . m_SunAmount [ 0 ] ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
else for ( int k = 0 ; k < numVertexLightComponents ; + + k )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
prop . m_MeshData [ nMeshIdx ] . m_VertColorData [ nVertex * numVertexLightComponents + k ] . AsVector3D ( ) = colorVerts [ nIndex ] . m_Colors [ k + 1 ] ;
prop . m_MeshData [ nMeshIdx ] . m_VertColorData [ nVertex * numVertexLightComponents + k ] . w = colorVerts [ nIndex ] . m_SunAmount [ k + 1 ] ;
2020-04-22 12:56:21 -04:00
}
}
}
}
}
}
}
}
2023-05-13 21:11:15 +03:00
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
// Trace rays from each unique vertex, accumulating direct and indirect
// sources at each ray termination. Use the winding data to distribute the unique vertexes
// into the rendering layout.
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : ComputeLighting ( CStaticProp & prop , int iThread , int prop_index , CComputeStaticPropLightingResults * pResults )
{
CUtlVector < badVertex_t > badVerts ;
StaticPropDict_t & dict = m_StaticPropDict [ prop . m_ModelIdx ] ;
studiohdr_t * pStudioHdr = dict . m_pStudioHdr ;
OptimizedModel : : FileHeader_t * pVtxHdr = ( OptimizedModel : : FileHeader_t * ) dict . m_VtxBuf . Base ( ) ;
if ( ! pStudioHdr | | ! pVtxHdr )
{
// must have model and its verts for lighting computation
// game will fallback to fullbright
return ;
}
2023-05-13 21:11:15 +03:00
int nGatherFlags = ( prop . m_Flags & STATIC_PROP_IGNORE_NORMALS ) ? GATHERLFLAGS_IGNORE_NORMALS : 0 ;
nGatherFlags | = ( prop . m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING ) ? GATHERLFLAGS_NO_OCCLUSION : 0 ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
if ( dict . m_bHasPhong )
{
nGatherFlags & = ~ GATHERLFLAGS_IGNORE_NORMALS ;
}
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
nGatherFlags | = GATHERLFLAGS_STATICPROP ;
2020-04-22 12:56:21 -04:00
2023-05-09 19:57:22 +03:00
# ifdef MPI
2020-04-22 12:56:21 -04:00
VMPI_SetCurrentStage ( " ComputeLighting " ) ;
2023-05-09 19:57:22 +03:00
# endif
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
int numSampleNormals = ( g_numVradStaticPropsLightingStreams > 1 ) ? ( NUM_BUMP_VECTS + 1 ) : 1 ;
bool bCanUseTangents = dict . m_bHasBumpmap ;
bool bSkipDirectSkylight = true ; // Only computing indirect GI for all static props now. Direct sunlight applied in shader.
if ( PositionIn3DSkybox ( prop . m_Origin ) )
{
bSkipDirectSkylight = false ;
}
2020-04-22 12:56:21 -04:00
for ( int bodyID = 0 ; bodyID < pStudioHdr - > numbodyparts ; + + bodyID )
{
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( bodyID ) ;
for ( int modelID = 0 ; modelID < pBodyPart - > nummodels ; + + modelID )
{
mstudiomodel_t * pStudioModel = pBodyPart - > pModel ( modelID ) ;
// light all unique vertexes
CUtlVector < colorVertex_t > * pColorVertsArray = new CUtlVector < colorVertex_t > ;
pResults - > m_ColorVertsArrays . AddToTail ( pColorVertsArray ) ;
2023-05-13 21:11:15 +03:00
2020-04-22 12:56:21 -04:00
CUtlVector < colorVertex_t > & colorVerts = * pColorVertsArray ;
colorVerts . EnsureCount ( pStudioModel - > numvertices ) ;
memset ( colorVerts . Base ( ) , 0 , colorVerts . Count ( ) * sizeof ( colorVertex_t ) ) ;
int numVertexes = 0 ;
for ( int meshID = 0 ; meshID < pStudioModel - > nummeshes ; + + meshID )
{
mstudiomesh_t * pStudioMesh = pStudioModel - > pMesh ( meshID ) ;
const mstudio_meshvertexdata_t * vertData = pStudioMesh - > GetVertexData ( ( void * ) pStudioHdr ) ;
2023-05-13 21:11:15 +03:00
Assert ( vertData ) ; // This can only return NULL on X360 for now
2020-04-22 12:56:21 -04:00
for ( int vertexID = 0 ; vertexID < pStudioMesh - > numvertices ; + + vertexID )
{
2023-05-13 21:11:15 +03:00
Vector sampleNormals [ NUM_BUMP_VECTS + 1 ] ;
2020-04-22 12:56:21 -04:00
Vector samplePosition ;
// transform position and normal into world coordinate system
2023-05-13 21:11:15 +03:00
matrix3x4_t matrix ;
AngleMatrix ( prop . m_Angles , prop . m_Origin , matrix ) ;
VectorTransform ( * vertData - > Position ( vertexID ) , matrix , samplePosition ) ;
AngleMatrix ( prop . m_Angles , matrix ) ;
VectorTransform ( * vertData - > Normal ( vertexID ) , matrix , sampleNormals [ 0 ] ) ;
if ( numSampleNormals > 1 )
{
Vector * bumpVects = & sampleNormals [ 1 ] ;
Vector4D * vecTangentS = vertData - > HasTangentData ( ) ? vertData - > TangentS ( vertexID ) : NULL ;
if ( vecTangentS & & bCanUseTangents )
{
Vector vecTexS ;
VectorTransform ( vecTangentS - > AsVector3D ( ) , matrix , vecTexS ) ;
Vector vecTexT ;
CrossProduct ( sampleNormals [ 0 ] , vecTexS , vecTexT ) ;
vecTexT . NormalizeInPlace ( ) ;
// recompute S-vector to have S, T, N as an orthonormal basis for hl2 vectors
CrossProduct ( vecTexT , sampleNormals [ 0 ] , vecTexS ) ;
// respect the flip-factor for T-vector
vecTexT * = vecTangentS - > w ;
GetStaticPropBumpNormals (
vecTexS , vecTexT ,
sampleNormals [ 0 ] ,
sampleNormals [ 0 ] ,
bumpVects ) ;
sampleNormals [ 0 ] . NormalizeInPlace ( ) ;
sampleNormals [ 1 ] . NormalizeInPlace ( ) ;
sampleNormals [ 2 ] . NormalizeInPlace ( ) ;
sampleNormals [ 3 ] . NormalizeInPlace ( ) ;
}
else
{
sampleNormals [ 1 ] = sampleNormals [ 0 ] ;
sampleNormals [ 2 ] = sampleNormals [ 0 ] ;
sampleNormals [ 3 ] = sampleNormals [ 0 ] ;
}
}
2020-04-22 12:56:21 -04:00
if ( PositionInSolid ( samplePosition ) )
{
// vertex is in solid, add to the bad list, and recover later
badVertex_t badVertex ;
badVertex . m_ColorVertex = numVertexes ;
badVertex . m_Position = samplePosition ;
2023-05-13 21:11:15 +03:00
memcpy ( badVertex . m_Normals , sampleNormals , sizeof ( badVertex . m_Normals ) ) ;
2020-04-22 12:56:21 -04:00
badVerts . AddToTail ( badVertex ) ;
}
2023-05-13 21:11:15 +03:00
else
2020-04-22 12:56:21 -04:00
{
Vector direct_pos = samplePosition ;
2023-05-13 21:11:15 +03:00
int skip_prop = - 1 ;
if ( g_bDisablePropSelfShadowing | | ( prop . m_Flags & STATIC_PROP_NO_SELF_SHADOWING ) )
{
skip_prop = prop_index ;
}
Vector directColors [ NUM_BUMP_VECTS + 1 ] ;
float sunAmount [ NUM_BUMP_VECTS + 1 ] ;
memset ( directColors , 0 , sizeof ( directColors ) ) ;
memset ( sunAmount , 0 , sizeof ( sunAmount ) ) ;
if ( bCanUseTangents )
{
ComputeDirectLightingAtPoint ( direct_pos ,
sampleNormals , directColors , sunAmount , numSampleNormals , bSkipDirectSkylight ,
iThread ,
skip_prop , nGatherFlags ) ;
}
else
{
ComputeDirectLightingAtPoint ( direct_pos ,
sampleNormals , directColors , sunAmount , 1 , bSkipDirectSkylight ,
iThread ,
skip_prop , nGatherFlags ) ;
directColors [ 1 ] = directColors [ 0 ] ;
directColors [ 2 ] = directColors [ 0 ] ;
directColors [ 3 ] = directColors [ 0 ] ;
sunAmount [ 1 ] = sunAmount [ 0 ] ;
sunAmount [ 2 ] = sunAmount [ 0 ] ;
sunAmount [ 3 ] = sunAmount [ 0 ] ;
}
if ( numSampleNormals > 1 )
{
// doing this for direct and indirect separately helps eliminate errors with CSM blending
NormalizeVertexBumpedLighting ( directColors , directColors + 1 ) ;
}
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
Vector indirectColors [ NUM_BUMP_VECTS + 1 ] ;
memset ( indirectColors , 0 , sizeof ( indirectColors ) ) ;
2020-04-22 12:56:21 -04:00
if ( g_bShowStaticPropNormals )
{
2023-05-13 21:11:15 +03:00
directColors [ 0 ] = sampleNormals [ 0 ] ;
directColors [ 0 ] + = Vector ( 1.0 , 1.0 , 1.0 ) ;
directColors [ 0 ] * = 50.0 ;
directColors [ 1 ] = directColors [ 0 ] ;
directColors [ 2 ] = directColors [ 0 ] ;
directColors [ 3 ] = directColors [ 0 ] ;
2020-04-22 12:56:21 -04:00
}
else
{
if ( numbounce > = 1 )
2023-05-13 21:11:15 +03:00
{
if ( bCanUseTangents )
{
ComputeIndirectLightingAtPoint (
samplePosition , sampleNormals ,
indirectColors , numSampleNormals , iThread , g_bFastStaticProps ,
( prop . m_Flags & STATIC_PROP_IGNORE_NORMALS ) ! = 0 , prop_index ) ;
}
else
{
ComputeIndirectLightingAtPoint (
samplePosition , sampleNormals ,
indirectColors , 1 , iThread , g_bFastStaticProps ,
( prop . m_Flags & STATIC_PROP_IGNORE_NORMALS ) ! = 0 , prop_index ) ;
indirectColors [ 1 ] = indirectColors [ 0 ] ;
indirectColors [ 2 ] = indirectColors [ 0 ] ;
indirectColors [ 3 ] = indirectColors [ 0 ] ;
}
if ( numSampleNormals > 1 )
{
// doing this for direct and indirect separately helps eliminate errors with CSM blending
NormalizeVertexBumpedLighting ( indirectColors , indirectColors + 1 ) ;
}
}
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
2020-04-22 12:56:21 -04:00
colorVerts [ numVertexes ] . m_bValid = true ;
colorVerts [ numVertexes ] . m_Position = samplePosition ;
2023-05-13 21:11:15 +03:00
for ( int k = 0 ; k < numSampleNormals ; + + k )
{
VectorAdd ( directColors [ k ] , indirectColors [ k ] , colorVerts [ numVertexes ] . m_Colors [ k ] ) ;
colorVerts [ numVertexes ] . m_SunAmount [ k ] = sunAmount [ k ] ;
}
if ( numSampleNormals > 1 )
{
float * pSunAmountUnbumped = & colorVerts [ numVertexes ] . m_SunAmount [ 0 ] ;
NormalizeVertexBumpedSunAmount ( pSunAmountUnbumped , pSunAmountUnbumped + 1 , pSunAmountUnbumped + 2 , pSunAmountUnbumped + 3 ) ;
}
2020-04-22 12:56:21 -04:00
}
numVertexes + + ;
}
}
// color in the bad vertexes
// when entire model has no lighting origin and no valid neighbors
// must punt, leave black coloring
if ( badVerts . Count ( ) & & ( prop . m_bLightingOriginValid | | badVerts . Count ( ) ! = numVertexes ) )
{
for ( int nBadVertex = 0 ; nBadVertex < badVerts . Count ( ) ; nBadVertex + + )
{
Vector bestPosition ;
if ( prop . m_bLightingOriginValid )
{
// use the specified lighting origin
VectorCopy ( prop . m_LightingOrigin , bestPosition ) ;
}
else
{
// find the closest valid neighbor
int best = 0 ;
float closest = FLT_MAX ;
for ( int nColorVertex = 0 ; nColorVertex < numVertexes ; nColorVertex + + )
{
if ( ! colorVerts [ nColorVertex ] . m_bValid )
{
// skip invalid neighbors
continue ;
}
Vector delta ;
VectorSubtract ( colorVerts [ nColorVertex ] . m_Position , badVerts [ nBadVertex ] . m_Position , delta ) ;
float distance = VectorLength ( delta ) ;
if ( distance < closest )
{
closest = distance ;
best = nColorVertex ;
}
}
// use the best neighbor as the direction to crawl
VectorCopy ( colorVerts [ best ] . m_Position , bestPosition ) ;
}
// crawl toward best position
2023-05-13 21:11:15 +03:00
// subdivide to determine a closer valid point to the bad vertex, and re-light
2020-04-22 12:56:21 -04:00
Vector midPosition ;
int numIterations = 20 ;
while ( - - numIterations > 0 )
{
VectorAdd ( bestPosition , badVerts [ nBadVertex ] . m_Position , midPosition ) ;
VectorScale ( midPosition , 0.5f , midPosition ) ;
if ( PositionInSolid ( midPosition ) )
break ;
bestPosition = midPosition ;
}
2023-05-13 21:11:15 +03:00
Vector directColors [ NUM_BUMP_VECTS + 1 ] ;
memset ( directColors , 0 , sizeof ( directColors ) ) ;
Vector indirectColors [ NUM_BUMP_VECTS + 1 ] ;
memset ( indirectColors , 0 , sizeof ( indirectColors ) ) ;
float sunAmount [ NUM_BUMP_VECTS + 1 ] ;
memset ( sunAmount , 0 , sizeof ( sunAmount ) ) ;
2020-04-22 12:56:21 -04:00
// re-light from better position
2023-05-13 21:11:15 +03:00
if ( bCanUseTangents )
{
ComputeDirectLightingAtPoint ( bestPosition , badVerts [ nBadVertex ] . m_Normals ,
directColors , sunAmount , numSampleNormals , bSkipDirectSkylight , iThread ) ;
ComputeIndirectLightingAtPoint ( bestPosition , badVerts [ nBadVertex ] . m_Normals ,
indirectColors , numSampleNormals , iThread , true , false , prop_index ) ;
}
else
{
ComputeDirectLightingAtPoint ( bestPosition , badVerts [ nBadVertex ] . m_Normals ,
directColors , sunAmount , 1 , bSkipDirectSkylight , iThread ) ;
// doing this for direct and indirect separately helps eliminate errors with CSM blending
ComputeIndirectLightingAtPoint ( bestPosition , badVerts [ nBadVertex ] . m_Normals ,
indirectColors , 1 , iThread , true , false , prop_index ) ;
for ( int k = 1 ; k < numSampleNormals ; + + k )
{
directColors [ k ] = directColors [ 0 ] ;
indirectColors [ k ] = indirectColors [ 0 ] ;
sunAmount [ k ] = sunAmount [ 0 ] ;
}
}
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
if ( numSampleNormals > 1 )
{
// doing this for direct and indirect separately helps eliminate errors with CSM blending
NormalizeVertexBumpedLighting ( directColors , directColors + 1 ) ;
NormalizeVertexBumpedLighting ( indirectColors , indirectColors + 1 ) ;
}
2020-04-22 12:56:21 -04:00
// save results, not changing valid status
// to ensure this offset position is not considered as a viable candidate
2023-05-13 21:11:15 +03:00
const int idxColorVertex = badVerts [ nBadVertex ] . m_ColorVertex ;
colorVerts [ idxColorVertex ] . m_Position = bestPosition ;
for ( int k = 0 ; k < numSampleNormals ; + + k )
{
VectorAdd ( directColors [ k ] , indirectColors [ k ] , colorVerts [ idxColorVertex ] . m_Colors [ k ] ) ;
colorVerts [ idxColorVertex ] . m_SunAmount [ k ] = sunAmount [ k ] ;
}
if ( numSampleNormals > 1 )
{
float * pSunAmountUnbumped = & colorVerts [ idxColorVertex ] . m_SunAmount [ 0 ] ;
NormalizeVertexBumpedSunAmount ( pSunAmountUnbumped , pSunAmountUnbumped + 1 , pSunAmountUnbumped + 2 , pSunAmountUnbumped + 3 ) ;
}
2020-04-22 12:56:21 -04:00
}
}
// discard bad verts
badVerts . Purge ( ) ;
}
}
}
//-----------------------------------------------------------------------------
2023-05-13 21:11:15 +03:00
// Write the lighting to bsp pak lump
2020-04-22 12:56:21 -04:00
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : SerializeLighting ( )
{
char filename [ MAX_PATH ] ;
CUtlBuffer utlBuf ;
// illuminate them all
int count = m_StaticProps . Count ( ) ;
if ( ! count )
{
// nothing to do
return ;
}
char mapName [ MAX_PATH ] ;
Q_FileBase ( source , mapName , sizeof ( mapName ) ) ;
int size ;
for ( int i = 0 ; i < count ; + + i )
{
if ( g_bHDR )
{
sprintf ( filename , " sp_hdr_%d.vhv " , i ) ;
}
else
{
sprintf ( filename , " sp_%d.vhv " , i ) ;
}
int totalVertexes = 0 ;
for ( int j = 0 ; j < m_StaticProps [ i ] . m_MeshData . Count ( ) ; j + + )
{
2023-05-13 21:11:15 +03:00
totalVertexes + = m_StaticProps [ i ] . m_MeshData [ j ] . m_numVerts ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
int numLightingComponents = g_numVradStaticPropsLightingStreams ;
2020-04-22 12:56:21 -04:00
// allocate a buffer with enough padding for alignment
size = sizeof ( HardwareVerts : : FileHeader_t ) +
m_StaticProps [ i ] . m_MeshData . Count ( ) * sizeof ( HardwareVerts : : MeshHeader_t ) +
2023-05-13 21:11:15 +03:00
totalVertexes * 4 * numLightingComponents + 2 * 512 ;
2020-04-22 12:56:21 -04:00
utlBuf . EnsureCapacity ( size ) ;
Q_memset ( utlBuf . Base ( ) , 0 , size ) ;
HardwareVerts : : FileHeader_t * pVhvHdr = ( HardwareVerts : : FileHeader_t * ) utlBuf . Base ( ) ;
// align to start of vertex data
unsigned char * pVertexData = ( unsigned char * ) ( sizeof ( HardwareVerts : : FileHeader_t ) + m_StaticProps [ i ] . m_MeshData . Count ( ) * sizeof ( HardwareVerts : : MeshHeader_t ) ) ;
2023-05-13 21:11:15 +03:00
pVertexData = ( unsigned char * ) pVhvHdr + ALIGN_TO_POW2 ( ( size_t ) pVertexData , 512 ) ;
2020-04-22 12:56:21 -04:00
// construct header
pVhvHdr - > m_nVersion = VHV_VERSION ;
pVhvHdr - > m_nChecksum = m_StaticPropDict [ m_StaticProps [ i ] . m_ModelIdx ] . m_pStudioHdr - > checksum ;
2023-05-13 21:11:15 +03:00
pVhvHdr - > m_nVertexFlags = ( numLightingComponents > 1 ) ? VERTEX_NORMAL : VERTEX_COLOR ;
pVhvHdr - > m_nVertexSize = 4 * numLightingComponents ;
2020-04-22 12:56:21 -04:00
pVhvHdr - > m_nVertexes = totalVertexes ;
pVhvHdr - > m_nMeshes = m_StaticProps [ i ] . m_MeshData . Count ( ) ;
for ( int n = 0 ; n < pVhvHdr - > m_nMeshes ; n + + )
{
// construct mesh dictionary
HardwareVerts : : MeshHeader_t * pMesh = pVhvHdr - > pMesh ( n ) ;
pMesh - > m_nLod = m_StaticProps [ i ] . m_MeshData [ n ] . m_nLod ;
2023-05-13 21:11:15 +03:00
pMesh - > m_nVertexes = m_StaticProps [ i ] . m_MeshData [ n ] . m_numVerts ;
pMesh - > m_nOffset = ( size_t ) pVertexData - ( size_t ) pVhvHdr ;
2020-04-22 12:56:21 -04:00
// construct vertexes
2023-05-13 21:11:15 +03:00
for ( int k = 0 ; k < m_StaticProps [ i ] . m_MeshData [ n ] . m_VertColorData . Count ( ) ; k + + )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
Vector & vector = m_StaticProps [ i ] . m_MeshData [ n ] . m_VertColorData [ k ] . AsVector3D ( ) ;
//if ( (vector.x > 1024.0f) || (vector.y > 1024.0f) || (vector.z > 1024.0f) )s
// Msg(" *** out of range prop lighting *** \n");
2020-04-22 12:56:21 -04:00
ColorRGBExp32 rgbColor ;
2023-05-13 21:11:15 +03:00
VectorToColorRGBExp32 ( vector , rgbColor ) ;
2020-04-22 12:56:21 -04:00
unsigned char dstColor [ 4 ] ;
ConvertRGBExp32ToRGBA8888 ( & rgbColor , dstColor ) ;
// b,g,r,a order
pVertexData [ 0 ] = dstColor [ 2 ] ;
pVertexData [ 1 ] = dstColor [ 1 ] ;
pVertexData [ 2 ] = dstColor [ 0 ] ;
2023-05-13 21:11:15 +03:00
// Use the unmodified lighting data to generate the sun percentage, not the output of the RGBE conversions above!
float flSunAmount = m_StaticProps [ i ] . m_MeshData [ n ] . m_VertColorData [ k ] . w ;
pVertexData [ 3 ] = uint8 ( clamp ( flSunAmount , 0.0f , 1.0f ) * 255.0f + 0.5f ) ;
2020-04-22 12:56:21 -04:00
pVertexData + = 4 ;
}
}
// align to end of file
2023-05-13 21:11:15 +03:00
pVertexData = ( unsigned char * ) ( ( size_t ) pVertexData - ( size_t ) pVhvHdr ) ;
pVertexData = ( unsigned char * ) pVhvHdr + ALIGN_TO_POW2 ( ( size_t ) pVertexData , 512 ) ;
2020-04-22 12:56:21 -04:00
AddBufferToPak ( GetPakFile ( ) , filename , ( void * ) pVhvHdr , pVertexData - ( unsigned char * ) pVhvHdr , false ) ;
}
}
2023-05-09 19:57:22 +03:00
# ifdef MPI
2020-04-22 12:56:21 -04:00
void CVradStaticPropMgr : : VMPI_ProcessStaticProp_Static ( int iThread , uint64 iStaticProp , MessageBuffer * pBuf )
{
g_StaticPropMgr . VMPI_ProcessStaticProp ( iThread , iStaticProp , pBuf ) ;
}
void CVradStaticPropMgr : : VMPI_ReceiveStaticPropResults_Static ( uint64 iStaticProp , MessageBuffer * pBuf , int iWorker )
{
g_StaticPropMgr . VMPI_ReceiveStaticPropResults ( iStaticProp , pBuf , iWorker ) ;
}
//-----------------------------------------------------------------------------
// Called on workers to do the computation for a static prop and send
// it to the master.
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : VMPI_ProcessStaticProp ( int iThread , int iStaticProp , MessageBuffer * pBuf )
{
// Compute the lighting.
CComputeStaticPropLightingResults results ;
ComputeLighting ( m_StaticProps [ iStaticProp ] , iThread , iStaticProp , & results ) ;
VMPI_SetCurrentStage ( " EncodeLightingResults " ) ;
// Encode the results.
int nLists = results . m_ColorVertsArrays . Count ( ) ;
pBuf - > write ( & nLists , sizeof ( nLists ) ) ;
for ( int i = 0 ; i < nLists ; i + + )
{
CUtlVector < colorVertex_t > & curList = * results . m_ColorVertsArrays [ i ] ;
int count = curList . Count ( ) ;
pBuf - > write ( & count , sizeof ( count ) ) ;
pBuf - > write ( curList . Base ( ) , curList . Count ( ) * sizeof ( colorVertex_t ) ) ;
}
}
//-----------------------------------------------------------------------------
// Called on the master when a worker finishes processing a static prop.
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : VMPI_ReceiveStaticPropResults ( int iStaticProp , MessageBuffer * pBuf , int iWorker )
{
// Read in the results.
CComputeStaticPropLightingResults results ;
int nLists ;
pBuf - > read ( & nLists , sizeof ( nLists ) ) ;
for ( int i = 0 ; i < nLists ; i + + )
{
CUtlVector < colorVertex_t > * pList = new CUtlVector < colorVertex_t > ;
results . m_ColorVertsArrays . AddToTail ( pList ) ;
int count ;
pBuf - > read ( & count , sizeof ( count ) ) ;
pList - > SetSize ( count ) ;
pBuf - > read ( pList - > Base ( ) , count * sizeof ( colorVertex_t ) ) ;
}
// Apply the results.
2023-05-13 21:11:15 +03:00
ApplyLightingToStaticProp ( m_StaticProps [ iStaticProp ] , & results ) ;
2020-04-22 12:56:21 -04:00
}
2023-05-09 19:57:22 +03:00
# endif
2020-04-22 12:56:21 -04:00
void CVradStaticPropMgr : : ComputeLightingForProp ( int iThread , int iStaticProp )
{
// Compute the lighting.
CComputeStaticPropLightingResults results ;
ComputeLighting ( m_StaticProps [ iStaticProp ] , iThread , iStaticProp , & results ) ;
2023-05-13 21:11:15 +03:00
ApplyLightingToStaticProp ( m_StaticProps [ iStaticProp ] , & results ) ;
2020-04-22 12:56:21 -04:00
}
void CVradStaticPropMgr : : ThreadComputeStaticPropLighting ( int iThread , void * pUserData )
{
while ( 1 )
{
int j = GetThreadWork ( ) ;
if ( j = = - 1 )
break ;
CComputeStaticPropLightingResults results ;
g_StaticPropMgr . ComputeLightingForProp ( iThread , j ) ;
}
}
//-----------------------------------------------------------------------------
// Computes lighting for the static props.
// Must be after all other surface lighting has been computed for the indirect sampling.
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : ComputeLighting ( int iThread )
{
// illuminate them all
int count = m_StaticProps . Count ( ) ;
if ( ! count )
{
// nothing to do
return ;
}
2023-05-13 21:11:15 +03:00
double start = Plat_FloatTime ( ) ;
2020-04-22 12:56:21 -04:00
StartPacifier ( " Computing static prop lighting : " ) ;
2023-05-13 21:11:15 +03:00
#if 0
CGlViewBuffer glViewBuf ;
glViewBuf . WriteKDTree ( & g_RtEnv ) ;
g_pFullFileSystem - > WriteFile ( " maps/rtenv.gl " , " GAME " , glViewBuf ) ;
# endif
2020-04-22 12:56:21 -04:00
// ensure any traces against us are ignored because we have no inherit lighting contribution
m_bIgnoreStaticPropTrace = true ;
2023-05-09 19:57:22 +03:00
# ifdef MPI
2020-04-22 12:56:21 -04:00
if ( g_bUseMPI )
{
// Distribute the work among the workers.
VMPI_SetCurrentStage ( " CVradStaticPropMgr::ComputeLighting " ) ;
DistributeWork (
count ,
& CVradStaticPropMgr : : VMPI_ProcessStaticProp_Static ,
& CVradStaticPropMgr : : VMPI_ReceiveStaticPropResults_Static ) ;
}
else
2023-05-09 19:57:22 +03:00
# endif
2020-04-22 12:56:21 -04:00
{
RunThreadsOn ( count , true , ThreadComputeStaticPropLighting ) ;
}
// restore default
m_bIgnoreStaticPropTrace = false ;
2023-05-13 21:11:15 +03:00
2020-04-22 12:56:21 -04:00
// save data to bsp
SerializeLighting ( ) ;
EndPacifier ( true ) ;
2023-05-13 21:11:15 +03:00
double end = Plat_FloatTime ( ) ;
DumpElapsedTime ( ( int ) ( end - start ) ) ;
2020-04-22 12:56:21 -04:00
}
//-----------------------------------------------------------------------------
// Adds all static prop polys to the ray trace store.
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : AddPolysForRayTrace ( void )
{
int count = m_StaticProps . Count ( ) ;
if ( ! count )
{
// nothing to do
return ;
}
// Triangle coverage of 1 (full coverage)
Vector fullCoverage ;
fullCoverage . x = 1.0f ;
for ( int nProp = 0 ; nProp < count ; + + nProp )
{
CStaticProp & prop = m_StaticProps [ nProp ] ;
StaticPropDict_t & dict = m_StaticPropDict [ prop . m_ModelIdx ] ;
if ( prop . m_Flags & STATIC_PROP_NO_SHADOW )
continue ;
// If not using static prop polys, use AABB
if ( ! g_bStaticPropPolys )
{
if ( dict . m_pModel )
{
VMatrix xform ;
xform . SetupMatrixOrgAngles ( prop . m_Origin , prop . m_Angles ) ;
ICollisionQuery * queryModel = s_pPhysCollision - > CreateQueryModel ( dict . m_pModel ) ;
for ( int nConvex = 0 ; nConvex < queryModel - > ConvexCount ( ) ; + + nConvex )
{
for ( int nTri = 0 ; nTri < queryModel - > TriangleCount ( nConvex ) ; + + nTri )
{
Vector verts [ 3 ] ;
queryModel - > GetTriangleVerts ( nConvex , nTri , verts ) ;
for ( int nVert = 0 ; nVert < 3 ; + + nVert )
verts [ nVert ] = xform . VMul4x3 ( verts [ nVert ] ) ;
g_RtEnv . AddTriangle ( TRACE_ID_STATICPROP | nProp , verts [ 0 ] , verts [ 1 ] , verts [ 2 ] , fullCoverage ) ;
}
}
s_pPhysCollision - > DestroyQueryModel ( queryModel ) ;
}
else
{
VectorAdd ( dict . m_Mins , prop . m_Origin , prop . m_mins ) ;
VectorAdd ( dict . m_Maxs , prop . m_Origin , prop . m_maxs ) ;
g_RtEnv . AddAxisAlignedRectangularSolid ( TRACE_ID_STATICPROP | nProp , prop . m_mins , prop . m_maxs , fullCoverage ) ;
}
continue ;
}
studiohdr_t * pStudioHdr = dict . m_pStudioHdr ;
OptimizedModel : : FileHeader_t * pVtxHdr = ( OptimizedModel : : FileHeader_t * ) dict . m_VtxBuf . Base ( ) ;
if ( ! pStudioHdr | | ! pVtxHdr )
{
// must have model and its verts for decoding triangles
2023-05-13 21:11:15 +03:00
// must have model and its verts for decoding triangles
printf ( " Can't get studio header (%p) and vertex data (%p) for %s \n " , pStudioHdr , pVtxHdr ,
pStudioHdr ? pStudioHdr - > name : " ***unknown*** " ) ;
continue ;
2020-04-22 12:56:21 -04:00
}
// only init the triangle table the first time
bool bInitTriangles = dict . m_triangleMaterialIndex . Count ( ) ? false : true ;
int triangleIndex = 0 ;
2023-05-13 21:11:15 +03:00
// transform position into world coordinate system
matrix3x4_t matrix ;
AngleMatrix ( prop . m_Angles , prop . m_Origin , matrix ) ;
2020-04-22 12:56:21 -04:00
// meshes are deeply hierarchial, divided between three stores, follow the white rabbit
// body parts -> models -> lod meshes -> strip groups -> strips
// the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
for ( int bodyID = 0 ; bodyID < pStudioHdr - > numbodyparts ; + + bodyID )
{
OptimizedModel : : BodyPartHeader_t * pVtxBodyPart = pVtxHdr - > pBodyPart ( bodyID ) ;
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( bodyID ) ;
for ( int modelID = 0 ; modelID < pBodyPart - > nummodels ; + + modelID )
{
OptimizedModel : : ModelHeader_t * pVtxModel = pVtxBodyPart - > pModel ( modelID ) ;
mstudiomodel_t * pStudioModel = pBodyPart - > pModel ( modelID ) ;
// assuming lod 0, could iterate if required
int nLod = 0 ;
OptimizedModel : : ModelLODHeader_t * pVtxLOD = pVtxModel - > pLOD ( nLod ) ;
for ( int nMesh = 0 ; nMesh < pStudioModel - > nummeshes ; + + nMesh )
{
// check if this mesh's material is in the no shadow material name list
mstudiomesh_t * pMesh = pStudioModel - > pMesh ( nMesh ) ;
mstudiotexture_t * pTxtr = pStudioHdr - > pTexture ( pMesh - > material ) ;
//printf("mat idx=%d mat name=%s\n",pMesh->material,pTxtr->pszName());
bool bSkipThisMesh = false ;
for ( int check = 0 ; check < g_NonShadowCastingMaterialStrings . Count ( ) ; check + + )
{
if ( Q_stristr ( pTxtr - > pszName ( ) ,
g_NonShadowCastingMaterialStrings [ check ] ) )
{
//printf("skip mat name=%s\n",pTxtr->pszName());
bSkipThisMesh = true ;
break ;
}
}
if ( bSkipThisMesh )
continue ;
int shadowTextureIndex = - 1 ;
if ( dict . m_textureShadowIndex . Count ( ) )
{
shadowTextureIndex = dict . m_textureShadowIndex [ pMesh - > material ] ;
}
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( nMesh ) ;
const mstudio_meshvertexdata_t * vertData = pMesh - > GetVertexData ( ( void * ) pStudioHdr ) ;
Assert ( vertData ) ; // This can only return NULL on X360 for now
for ( int nGroup = 0 ; nGroup < pVtxMesh - > numStripGroups ; + + nGroup )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( nGroup ) ;
int nStrip ;
for ( nStrip = 0 ; nStrip < pStripGroup - > numStrips ; nStrip + + )
{
OptimizedModel : : StripHeader_t * pStrip = pStripGroup - > pStrip ( nStrip ) ;
2023-05-13 21:11:15 +03:00
for ( int i = 0 ; i < pStrip - > numIndices ; i + = 3 )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
int idx = pStrip - > indexOffset + i ;
unsigned short i1 = * pStripGroup - > pIndex ( idx ) ;
unsigned short i2 = * pStripGroup - > pIndex ( idx + 1 ) ;
unsigned short i3 = * pStripGroup - > pIndex ( idx + 2 ) ;
int vertex1 = pStripGroup - > pVertex ( i1 ) - > origMeshVertID ;
int vertex2 = pStripGroup - > pVertex ( i2 ) - > origMeshVertID ;
int vertex3 = pStripGroup - > pVertex ( i3 ) - > origMeshVertID ;
// transform position into world coordinate system
matrix3x4_t matrix ;
AngleMatrix ( prop . m_Angles , prop . m_Origin , matrix ) ;
Vector position1 ;
Vector position2 ;
Vector position3 ;
VectorTransform ( * vertData - > Position ( vertex1 ) , matrix , position1 ) ;
VectorTransform ( * vertData - > Position ( vertex2 ) , matrix , position2 ) ;
VectorTransform ( * vertData - > Position ( vertex3 ) , matrix , position3 ) ;
unsigned short flags = 0 ;
int materialIndex = - 1 ;
Vector color = vec3_origin ;
if ( shadowTextureIndex > = 0 )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
if ( bInitTriangles )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
// add texture space and texture index to material database
// now
float coverage = g_ShadowTextureList . ComputeCoverageForTriangle ( shadowTextureIndex , * vertData - > Texcoord ( vertex1 ) , * vertData - > Texcoord ( vertex2 ) , * vertData - > Texcoord ( vertex3 ) ) ;
if ( coverage < 1.0f )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
materialIndex = g_ShadowTextureList . AddMaterialEntry ( shadowTextureIndex , * vertData - > Texcoord ( vertex1 ) , * vertData - > Texcoord ( vertex2 ) , * vertData - > Texcoord ( vertex3 ) ) ;
color . x = coverage ;
2020-04-22 12:56:21 -04:00
}
else
{
2023-05-13 21:11:15 +03:00
materialIndex = - 1 ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
dict . m_triangleMaterialIndex . AddToTail ( materialIndex ) ;
}
else
{
materialIndex = dict . m_triangleMaterialIndex [ triangleIndex ] ;
triangleIndex + + ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
if ( materialIndex > = 0 )
{
flags = FCACHETRI_TRANSPARENT ;
}
}
2020-04-22 12:56:21 -04:00
// printf( "\ngl 3\n" );
// printf( "gl %6.3f %6.3f %6.3f 1 0 0\n", XYZ(position1));
// printf( "gl %6.3f %6.3f %6.3f 0 1 0\n", XYZ(position2));
// printf( "gl %6.3f %6.3f %6.3f 0 0 1\n", XYZ(position3));
2023-05-13 21:11:15 +03:00
g_RtEnv . AddTriangle ( TRACE_ID_STATICPROP | nProp ,
position1 , position2 , position3 ,
color , flags , materialIndex ) ;
2020-04-22 12:56:21 -04:00
}
}
}
}
}
}
}
}
struct tl_tri_t
{
Vector p0 ;
Vector p1 ;
Vector p2 ;
Vector n0 ;
Vector n1 ;
Vector n2 ;
bool operator = = ( const tl_tri_t & t ) const
{
return ( p0 = = t . p0 & &
p1 = = t . p1 & &
p2 = = t . p2 & &
n0 = = t . n0 & &
n1 = = t . n1 & &
n2 = = t . n2 ) ;
}
} ;
struct tl_vert_t
{
Vector m_position ;
CUtlLinkedList < tl_tri_t , int > m_triList ;
} ;
void AddTriVertsToList ( CUtlVector < tl_vert_t > & triListVerts , int vertIndex , Vector vertPosition , Vector p0 , Vector p1 , Vector p2 , Vector n0 , Vector n1 , Vector n2 )
{
tl_tri_t tlTri ;
tlTri . p0 = p0 ;
tlTri . p1 = p1 ;
tlTri . p2 = p2 ;
tlTri . n0 = n0 ;
tlTri . n1 = n1 ;
tlTri . n2 = n2 ;
triListVerts . EnsureCapacity ( vertIndex + 1 ) ;
triListVerts [ vertIndex ] . m_position = vertPosition ;
int index = triListVerts [ vertIndex ] . m_triList . Find ( tlTri ) ;
if ( ! triListVerts [ vertIndex ] . m_triList . IsValidIndex ( index ) )
{
// not in list, add to list of triangles
triListVerts [ vertIndex ] . m_triList . AddToTail ( tlTri ) ;
}
}
//-----------------------------------------------------------------------------
// Builds a list of tris for every vertex
//-----------------------------------------------------------------------------
void CVradStaticPropMgr : : BuildTriList ( CStaticProp & prop )
{
// the generated list will consist of a list of verts
// each vert will have a linked list of triangles that it belongs to
CUtlVector < tl_vert_t > triListVerts ;
StaticPropDict_t & dict = m_StaticPropDict [ prop . m_ModelIdx ] ;
studiohdr_t * pStudioHdr = dict . m_pStudioHdr ;
OptimizedModel : : FileHeader_t * pVtxHdr = ( OptimizedModel : : FileHeader_t * ) dict . m_VtxBuf . Base ( ) ;
if ( ! pStudioHdr | | ! pVtxHdr )
{
// must have model and its verts for decoding triangles
return ;
}
// meshes are deeply hierarchial, divided between three stores, follow the white rabbit
// body parts -> models -> lod meshes -> strip groups -> strips
// the vertices and indices are pooled, the trick is knowing the offset to determine your indexed base
for ( int bodyID = 0 ; bodyID < pStudioHdr - > numbodyparts ; + + bodyID )
{
OptimizedModel : : BodyPartHeader_t * pVtxBodyPart = pVtxHdr - > pBodyPart ( bodyID ) ;
mstudiobodyparts_t * pBodyPart = pStudioHdr - > pBodypart ( bodyID ) ;
for ( int modelID = 0 ; modelID < pBodyPart - > nummodels ; + + modelID )
{
OptimizedModel : : ModelHeader_t * pVtxModel = pVtxBodyPart - > pModel ( modelID ) ;
mstudiomodel_t * pStudioModel = pBodyPart - > pModel ( modelID ) ;
// get the specified lod, assuming lod 0
int nLod = 0 ;
OptimizedModel : : ModelLODHeader_t * pVtxLOD = pVtxModel - > pLOD ( nLod ) ;
// must reset because each model has their own vertexes [0..n]
// in order for this to be monolithic for the entire prop the list must be segmented
triListVerts . Purge ( ) ;
for ( int nMesh = 0 ; nMesh < pStudioModel - > nummeshes ; + + nMesh )
{
mstudiomesh_t * pMesh = pStudioModel - > pMesh ( nMesh ) ;
OptimizedModel : : MeshHeader_t * pVtxMesh = pVtxLOD - > pMesh ( nMesh ) ;
const mstudio_meshvertexdata_t * vertData = pMesh - > GetVertexData ( ( void * ) pStudioHdr ) ;
Assert ( vertData ) ; // This can only return NULL on X360 for now
for ( int nGroup = 0 ; nGroup < pVtxMesh - > numStripGroups ; + + nGroup )
{
OptimizedModel : : StripGroupHeader_t * pStripGroup = pVtxMesh - > pStripGroup ( nGroup ) ;
int nStrip ;
for ( nStrip = 0 ; nStrip < pStripGroup - > numStrips ; nStrip + + )
{
OptimizedModel : : StripHeader_t * pStrip = pStripGroup - > pStrip ( nStrip ) ;
2023-05-13 21:11:15 +03:00
for ( int i = 0 ; i < pStrip - > numIndices ; i + = 3 )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
int idx = pStrip - > indexOffset + i ;
unsigned short i1 = * pStripGroup - > pIndex ( idx ) ;
unsigned short i2 = * pStripGroup - > pIndex ( idx + 1 ) ;
unsigned short i3 = * pStripGroup - > pIndex ( idx + 2 ) ;
int vertex1 = pStripGroup - > pVertex ( i1 ) - > origMeshVertID ;
int vertex2 = pStripGroup - > pVertex ( i2 ) - > origMeshVertID ;
int vertex3 = pStripGroup - > pVertex ( i3 ) - > origMeshVertID ;
// transform position into world coordinate system
matrix3x4_t matrix ;
AngleMatrix ( prop . m_Angles , prop . m_Origin , matrix ) ;
Vector position1 ;
Vector position2 ;
Vector position3 ;
VectorTransform ( * vertData - > Position ( vertex1 ) , matrix , position1 ) ;
VectorTransform ( * vertData - > Position ( vertex2 ) , matrix , position2 ) ;
VectorTransform ( * vertData - > Position ( vertex3 ) , matrix , position3 ) ;
Vector normal1 ;
Vector normal2 ;
Vector normal3 ;
VectorTransform ( * vertData - > Normal ( vertex1 ) , matrix , normal1 ) ;
VectorTransform ( * vertData - > Normal ( vertex2 ) , matrix , normal2 ) ;
VectorTransform ( * vertData - > Normal ( vertex3 ) , matrix , normal3 ) ;
AddTriVertsToList ( triListVerts , pMesh - > vertexoffset + vertex1 , position1 , position1 , position2 , position3 , normal1 , normal2 , normal3 ) ;
AddTriVertsToList ( triListVerts , pMesh - > vertexoffset + vertex2 , position2 , position1 , position2 , position3 , normal1 , normal2 , normal3 ) ;
AddTriVertsToList ( triListVerts , pMesh - > vertexoffset + vertex3 , position3 , position1 , position2 , position3 , normal1 , normal2 , normal3 ) ;
2020-04-22 12:56:21 -04:00
}
}
}
}
}
}
}
const vertexFileHeader_t * mstudiomodel_t : : CacheVertexData ( void * pModelData )
{
studiohdr_t * pActiveStudioHdr = static_cast < studiohdr_t * > ( pModelData ) ;
Assert ( pActiveStudioHdr ) ;
2023-05-09 19:57:22 +03:00
if ( pActiveStudioHdr - > VertexBase ( ) )
2020-04-22 12:56:21 -04:00
{
2023-05-09 19:57:22 +03:00
return ( vertexFileHeader_t * ) pActiveStudioHdr - > VertexBase ( ) ;
2020-04-22 12:56:21 -04:00
}
// mandatory callback to make requested data resident
// load and persist the vertex file
char fileName [ MAX_PATH ] ;
strcpy ( fileName , " models/ " ) ;
strcat ( fileName , pActiveStudioHdr - > pszName ( ) ) ;
Q_StripExtension ( fileName , fileName , sizeof ( fileName ) ) ;
strcat ( fileName , " .vvd " ) ;
// load the model
2023-05-13 21:11:15 +03:00
CUtlBuffer bufData ;
if ( ! LoadFile ( fileName , bufData ) )
2020-04-22 12:56:21 -04:00
{
Error ( " Unable to load vertex data \" %s \" \n " , fileName ) ;
}
// Get the file size
2023-05-13 21:11:15 +03:00
int vvdSize = bufData . TellPut ( ) ;
2020-04-22 12:56:21 -04:00
if ( vvdSize = = 0 )
{
Error ( " Bad size for vertex data \" %s \" \n " , fileName ) ;
}
2023-05-13 21:11:15 +03:00
vertexFileHeader_t * pVvdHdr = ( vertexFileHeader_t * ) bufData . Base ( ) ;
2020-04-22 12:56:21 -04:00
// check header
if ( pVvdHdr - > id ! = MODEL_VERTEX_FILE_ID )
{
Error ( " Error Vertex File %s id %d should be %d \n " , fileName , pVvdHdr - > id , MODEL_VERTEX_FILE_ID ) ;
}
if ( pVvdHdr - > version ! = MODEL_VERTEX_FILE_VERSION )
{
Error ( " Error Vertex File %s version %d should be %d \n " , fileName , pVvdHdr - > version , MODEL_VERTEX_FILE_VERSION ) ;
}
if ( pVvdHdr - > checksum ! = pActiveStudioHdr - > checksum )
{
Error ( " Error Vertex File %s checksum %d should be %d \n " , fileName , pVvdHdr - > checksum , pActiveStudioHdr - > checksum ) ;
}
// need to perform mesh relocation fixups
// allocate a new copy
vertexFileHeader_t * pNewVvdHdr = ( vertexFileHeader_t * ) malloc ( vvdSize ) ;
if ( ! pNewVvdHdr )
{
Error ( " Error allocating %d bytes for Vertex File '%s' \n " , vvdSize , fileName ) ;
}
// load vertexes and run fixups
2023-05-13 21:11:15 +03:00
Studio_LoadVertexes ( pVvdHdr , pNewVvdHdr , 0 , true ) ;
2020-04-22 12:56:21 -04:00
// discard original
pVvdHdr = pNewVvdHdr ;
2023-05-13 21:11:15 +03:00
pActiveStudioHdr - > SetVertexBase ( ( void * ) pVvdHdr ) ;
2020-04-22 12:56:21 -04:00
return pVvdHdr ;
}
2023-05-13 21:11:15 +03:00
extern float totalarea ;
extern unsigned num_degenerate_faces ;
extern int fakeplanes ;
extern int PlaneTypeForNormal ( Vector & normal ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
void MakePatchForTriangle ( winding_t * w , Vector vRefl , int nStaticPropIdx )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
float area ;
CPatch * patch ;
Vector centroid ( 0 , 0 , 0 ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
area = WindingArea ( w ) ;
if ( area < = 0 )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
num_degenerate_faces + + ;
return ;
}
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
totalarea + = area ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
// get a patch
int ndxPatch = g_Patches . AddToTail ( ) ;
patch = & g_Patches [ ndxPatch ] ;
memset ( patch , 0 , sizeof ( CPatch ) ) ;
patch - > ndxNext = g_Patches . InvalidIndex ( ) ;
patch - > ndxNextParent = g_Patches . InvalidIndex ( ) ;
patch - > ndxNextClusterChild = g_Patches . InvalidIndex ( ) ;
patch - > child1 = g_Patches . InvalidIndex ( ) ;
patch - > child2 = g_Patches . InvalidIndex ( ) ;
patch - > parent = g_Patches . InvalidIndex ( ) ;
patch - > needsBumpmap = false ;
patch - > staticPropIdx = nStaticPropIdx ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
patch - > scale [ 0 ] = patch - > scale [ 1 ] = 1.0f ;
patch - > area = area ;
patch - > sky = false ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
// chop scaled up lightmaps coarser
patch - > luxscale = 16.0f ;
patch - > chop = maxchop ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
patch - > winding = w ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
patch - > plane = new dplane_t ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
Vector vecNormal ;
CrossProduct ( w - > p [ 2 ] - w - > p [ 0 ] , w - > p [ 1 ] - w - > p [ 0 ] , vecNormal ) ;
VectorNormalize ( vecNormal ) ;
VectorCopy ( vecNormal , patch - > plane - > normal ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
patch - > plane - > dist = vecNormal . Dot ( w - > p [ 0 ] ) ;
patch - > plane - > type = PlaneTypeForNormal ( patch - > plane - > normal ) ;
patch - > planeDist = patch - > plane - > dist ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
patch - > faceNumber = - 1 ; // This is a bit hacky and is used to identify static prop patches in other parts of the code
WindingCenter ( w , patch - > origin ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
VectorCopy ( patch - > plane - > normal , patch - > normal ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
WindingBounds ( w , patch - > face_mins , patch - > face_maxs ) ;
VectorCopy ( patch - > face_mins , patch - > mins ) ;
VectorCopy ( patch - > face_maxs , patch - > maxs ) ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
patch - > baselight . Init ( 0.0f , 0.0f , 0.0f ) ;
patch - > basearea = 1 ;
patch - > reflectivity = vRefl ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
void CVradStaticPropMgr : : MakePatches ( )
{
int count = m_StaticProps . Count ( ) ;
if ( ! count )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
// nothing to do
return ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
// Triangle coverage of 1 (full coverage)
Vector fullCoverage ;
fullCoverage . x = 1.0f ;
int nPatchCount = 0 ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
//IScratchPad3D *pPad = ScratchPad3D_Create();
//pPad->SetAutoFlush( false );
for ( int nProp = 0 ; nProp < count ; + + nProp )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
CStaticProp & prop = m_StaticProps [ nProp ] ;
StaticPropDict_t & dict = m_StaticPropDict [ prop . m_ModelIdx ] ;
2020-04-22 12:56:21 -04:00
2023-05-13 21:11:15 +03:00
if ( dict . m_pModel )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
// Get material, get reflectivity
VMatrix xform ;
xform . SetupMatrixOrgAngles ( prop . m_Origin , prop . m_Angles ) ;
ICollisionQuery * queryModel = s_pPhysCollision - > CreateQueryModel ( dict . m_pModel ) ;
for ( int nConvex = 0 ; nConvex < queryModel - > ConvexCount ( ) ; + + nConvex )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
for ( int nTri = 0 ; nTri < queryModel - > TriangleCount ( nConvex ) ; + + nTri )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
Vector verts [ 3 ] ;
queryModel - > GetTriangleVerts ( nConvex , nTri , verts ) ;
for ( int nVert = 0 ; nVert < 3 ; + + nVert )
verts [ nVert ] = xform . VMul4x3 ( verts [ nVert ] ) ;
//pPad->DrawPolygon( CSPVertList( verts, 3, CSPColor( prop.m_vReflectivity ) ) );
//pPad->DrawLine( CSPVert( g_Patches.Tail().origin ), CSPVert( g_Patches.Tail().origin + 5.0f * g_Patches.Tail().normal) );
winding_t * w = AllocWinding ( 3 ) ;
for ( int i = 0 ; i < 3 ; i + + )
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
w - > p [ i ] = verts [ i ] ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
w - > numpoints = 3 ;
MakePatchForTriangle ( w , prop . m_vReflectivity , nProp ) ;
//pPad->DrawPolygon( CSPVertList( verts, 3 ) );
//pPad->DrawLine( CSPVert( g_Patches.Tail().origin ), CSPVert( g_Patches.Tail().origin + 5.0f * g_Patches.Tail().normal) );
g_RtEnv_RadiosityPatches . AddTriangle ( TRACE_ID_PATCH | ( g_Patches . Count ( ) - 1 ) , verts [ 0 ] , verts [ 1 ] , verts [ 2 ] , Vector ( 1.0f , 1.0f , 1.0f ) ) ;
nPatchCount + + ;
2020-04-22 12:56:21 -04:00
}
}
2023-05-13 21:11:15 +03:00
s_pPhysCollision - > DestroyQueryModel ( queryModel ) ;
2020-04-22 12:56:21 -04:00
}
2023-05-13 21:11:15 +03:00
else
2020-04-22 12:56:21 -04:00
{
2023-05-13 21:11:15 +03:00
// FIXME
#if 0
VectorAdd ( dict . m_Mins , prop . m_Origin , prop . m_mins ) ;
VectorAdd ( dict . m_Maxs , prop . m_Origin , prop . m_maxs ) ;
g_RtEnv . AddAxisAlignedRectangularSolid ( TRACE_ID_STATICPROP | nProp , prop . m_mins , prop . m_maxs , fullCoverage ) ;
# endif
2020-04-22 12:56:21 -04:00
}
}
2023-05-13 21:11:15 +03:00
//pPad->Release();
g_RtEnv_RadiosityPatches . SetupAccelerationStructure ( ) ;
qprintf ( " %i static prop patches \n " , nPatchCount ) ;
2020-04-22 12:56:21 -04:00
}