diff options
| author | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:31:46 -0800 |
|---|---|---|
| committer | Jørgen P. Tjernø <[email protected]> | 2013-12-02 19:46:31 -0800 |
| commit | f56bb35301836e56582a575a75864392a0177875 (patch) | |
| tree | de61ddd39de3e7df52759711950b4c288592f0dc /mp/src/utils/vrad/vradstaticprops.cpp | |
| parent | Mark some more files as text. (diff) | |
| download | source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.tar.xz source-sdk-2013-f56bb35301836e56582a575a75864392a0177875.zip | |
Fix line endings. WHAMMY.
Diffstat (limited to 'mp/src/utils/vrad/vradstaticprops.cpp')
| -rw-r--r-- | mp/src/utils/vrad/vradstaticprops.cpp | 3830 |
1 files changed, 1915 insertions, 1915 deletions
diff --git a/mp/src/utils/vrad/vradstaticprops.cpp b/mp/src/utils/vrad/vradstaticprops.cpp index f240d94f..f4f04a69 100644 --- a/mp/src/utils/vrad/vradstaticprops.cpp +++ b/mp/src/utils/vrad/vradstaticprops.cpp @@ -1,1915 +1,1915 @@ -//========= 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"
-#include "UtlBuffer.h"
-#include "utlvector.h"
-#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"
-#include "phyfile.h"
-#include "collisionutils.h"
-#include "tier1/KeyValues.h"
-#include "pacifier.h"
-#include "materialsystem/imaterial.h"
-#include "materialsystem/hardwareverts.h"
-#include "byteswap.h"
-#include "mpivrad.h"
-#include "vtf/vtf.h"
-#include "tier1/utldict.h"
-#include "tier1/utlsymbol.h"
-
-#include "messbuf.h"
-#include "vmpi.h"
-#include "vmpi_distribute_work.h"
-
-
-#define ALIGN_TO_POW2(x,y) (((x)+(y-1))&~(y-1))
-
-// identifies a vertex embedded in solid
-// lighting will be copied from nearest valid neighbor
-struct badVertex_t
-{
- int m_ColorVertex;
- Vector m_Position;
- Vector m_Normal;
-};
-
-// a final colored vertex
-struct colorVertex_t
-{
- Vector m_Color;
- Vector m_Position;
- bool m_bValid;
-};
-
-class CComputeStaticPropLightingResults
-{
-public:
- ~CComputeStaticPropLightingResults()
- {
- m_ColorVertsArrays.PurgeAndDeleteElements();
- }
-
- CUtlVector< CUtlVector<colorVertex_t>* > m_ColorVertsArrays;
-};
-
-//-----------------------------------------------------------------------------
-// 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 );
-
-private:
- // 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 );
-
- // 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
- };
-
- struct MeshData_t
- {
- CUtlVector<Vector> m_Verts;
- 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;
- bool m_bLightingOriginValid;
- };
-
- // 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 );
- void ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults );
-
- 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 )
-{
- 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)
- return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() );
-}
-
-
-//-----------------------------------------------------------------------------
-// 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
- pHdr->pVertexBase = NULL;
- pHdr->pIndexBase = NULL;
-
- 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 ) );
- strcat( filename, ".dx80.vtx" );
-
- 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 )
- {
- float umin = min(t0.x, t1.x);
- umin = min(umin, t2.x);
- float umax = max(t0.x, t1.x);
- umax = max(umax, t2.x);
-
- float vmin = min(t0.y, t1.y);
- vmin = min(vmin, t2.y);
- float vmax = max(t0.y, t1.y);
- vmax = max(vmax, t2.y);
-
- // 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];
- int u = RoundFloatToInt( uv[0] * tex.width );
- int v = RoundFloatToInt( uv[1] * tex.height );
-
- // asume power of 2, clamp or wrap
- // UNDONE: Support clamp? This code should work
-#if 0
- u = tex.clampU ? clamp(u,0,(tex.width-1)) : (u & (tex.width-1));
- v = tex.clampV ? clamp(v,0,(tex.height-1)) : (v & (tex.height-1));
-#else
- // for now always wrap
- u &= (tex.width-1);
- v &= (tex.height-1);
-#endif
-
- return tex.pAlphaTexels[v * tex.width + u];
- }
-
- 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;
- }
-
-}
-
-
-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();
-}
-
-
-//-----------------------------------------------------------------------------
-// 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() );
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// 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 );
- }
-}
-
-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;
- }
-}
-
-//-----------------------------------------------------------------------------
-// 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 )
- {
- Error( "Cannot load the static props... encountered a stale map version. Re-vbsp the map." );
- }
-
- 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
- for (int i = m_StaticPropDict.Size(); --i >= 0; )
- {
- studiohdr_t *pStudioHdr = m_StaticPropDict[i].m_pStudioHdr;
- if ( pStudioHdr )
- {
- if ( pStudioHdr->pVertexBase )
- {
- free( pStudioHdr->pVertexBase );
- }
- 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 )
-{
- int ndxLeaf = PointLeafnum( position );
- if ( dleafs[ndxLeaf].contents & CONTENTS_SOLID )
- {
- // position embedded in solid
- return true;
- }
-
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// Trace from a vertex to each direct light source, accumulating its contribution.
-//-----------------------------------------------------------------------------
-void ComputeDirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor, int iThread,
- int static_prop_id_to_skip=-1, int nLFlags = 0)
-{
- SSE_sampleLightOutput_t sampleOutput;
-
- outColor.Init();
-
- // 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;
-
- 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 );
- }
- fudge *= 4.0;
- adjusted_pos += fudge;
- }
- else
- {
- // push out along normal
- adjusted_pos += 4.0 * normal;
-// flEpsilon = 1.0;
- }
-
- FourVectors adjusted_pos4;
- FourVectors normal4;
- adjusted_pos4.DuplicateVector( adjusted_pos );
- normal4.DuplicateVector( normal );
-
- GatherSampleLightSSE( sampleOutput, dl, -1, adjusted_pos4, &normal4, 1, iThread, nLFlags | GATHERLFLAGS_FORCE_FAST,
- static_prop_id_to_skip, flEpsilon );
-
- VectorMA( outColor, sampleOutput.m_flFalloff.m128_f32[0] * sampleOutput.m_flDot[0].m128_f32[0], dl->light.intensity, outColor );
- }
-}
-
-//-----------------------------------------------------------------------------
-// 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;
-
- 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 iCurColorVertsArray = 0;
- 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 );
-
- const CUtlVector<colorVertex_t> &colorVerts = *pResults->m_ColorVertsArrays[iCurColorVertsArray++];
-
- 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();
- prop.m_MeshData[nMeshIdx].m_Verts.AddMultipleToTail( pStripGroup->numVerts );
- prop.m_MeshData[nMeshIdx].m_nLod = nLod;
-
- for ( int nVertex = 0; nVertex < pStripGroup->numVerts; ++nVertex )
- {
- int nIndex = pMesh->vertexoffset + pStripGroup->pVertex( nVertex )->origMeshVertID;
-
- Assert( nIndex < pStudioModel->numvertices );
- prop.m_MeshData[nMeshIdx].m_Verts[nVertex] = colorVerts[nIndex].m_Color;
- }
- }
- }
- }
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// 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;
- }
-
- if (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
- return;
-
- VMPI_SetCurrentStage( "ComputeLighting" );
-
- 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 );
-
- 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);
- Assert( vertData ); // This can only return NULL on X360 for now
- for ( int vertexID = 0; vertexID < pStudioMesh->numvertices; ++vertexID )
- {
- Vector sampleNormal;
- Vector samplePosition;
- // transform position and normal into world coordinate system
- 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, sampleNormal );
-
- 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;
- badVertex.m_Normal = sampleNormal;
- badVerts.AddToTail( badVertex );
- }
- else
- {
- Vector direct_pos=samplePosition;
- int skip_prop = -1;
- if ( g_bDisablePropSelfShadowing || ( prop.m_Flags & STATIC_PROP_NO_SELF_SHADOWING ) )
- {
- skip_prop = prop_index;
- }
-
- int nFlags = ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS ) ? GATHERLFLAGS_IGNORE_NORMALS : 0;
-
- Vector directColor(0,0,0);
- ComputeDirectLightingAtPoint( direct_pos,
- sampleNormal, directColor, iThread,
- skip_prop, nFlags );
- Vector indirectColor(0,0,0);
-
- if (g_bShowStaticPropNormals)
- {
- directColor= sampleNormal;
- directColor += Vector(1.0,1.0,1.0);
- directColor *= 50.0;
- }
- else
- {
- if (numbounce >= 1)
- ComputeIndirectLightingAtPoint(
- samplePosition, sampleNormal,
- indirectColor, iThread, true,
- ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS) != 0 );
- }
-
- colorVerts[numVertexes].m_bValid = true;
- colorVerts[numVertexes].m_Position = samplePosition;
- VectorAdd( directColor, indirectColor, colorVerts[numVertexes].m_Color );
- }
-
- 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
- // sudivide to determine a closer valid point to the bad vertex, and re-light
- 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;
- }
-
- // re-light from better position
- Vector directColor;
- ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, directColor, iThread );
-
- Vector indirectColor;
- ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal,
- indirectColor, iThread, true );
-
- // save results, not changing valid status
- // to ensure this offset position is not considered as a viable candidate
- colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Position = bestPosition;
- VectorAdd( directColor, indirectColor, colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Color );
- }
- }
-
- // discard bad verts
- badVerts.Purge();
- }
- }
-}
-
-//-----------------------------------------------------------------------------
-// Write the lighitng to bsp pak lump
-//-----------------------------------------------------------------------------
-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)
- {
- // no need to write this file if we didn't compute the data
- // props marked this way will not load the info anyway
- if ( m_StaticProps[i].m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING )
- continue;
-
- 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++ )
- {
- totalVertexes += m_StaticProps[i].m_MeshData[j].m_Verts.Count();
- }
-
- // allocate a buffer with enough padding for alignment
- size = sizeof( HardwareVerts::FileHeader_t ) +
- m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t) +
- totalVertexes*4 + 2*512;
- 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));
- pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
-
- // construct header
- pVhvHdr->m_nVersion = VHV_VERSION;
- pVhvHdr->m_nChecksum = m_StaticPropDict[m_StaticProps[i].m_ModelIdx].m_pStudioHdr->checksum;
- pVhvHdr->m_nVertexFlags = VERTEX_COLOR;
- pVhvHdr->m_nVertexSize = 4;
- 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;
- pMesh->m_nVertexes = m_StaticProps[i].m_MeshData[n].m_Verts.Count();
- pMesh->m_nOffset = (unsigned int)pVertexData - (unsigned int)pVhvHdr;
-
- // construct vertexes
- for (int k=0; k<pMesh->m_nVertexes; k++)
- {
- Vector &vector = m_StaticProps[i].m_MeshData[n].m_Verts[k];
-
- ColorRGBExp32 rgbColor;
- VectorToColorRGBExp32( vector, rgbColor );
- unsigned char dstColor[4];
- ConvertRGBExp32ToRGBA8888( &rgbColor, dstColor );
-
- // b,g,r,a order
- pVertexData[0] = dstColor[2];
- pVertexData[1] = dstColor[1];
- pVertexData[2] = dstColor[0];
- pVertexData[3] = dstColor[3];
- pVertexData += 4;
- }
- }
-
- // align to end of file
- pVertexData = (unsigned char *)((unsigned int)pVertexData - (unsigned int)pVhvHdr);
- pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 );
-
- AddBufferToPak( GetPakFile(), filename, (void*)pVhvHdr, pVertexData - (unsigned char*)pVhvHdr, false );
- }
-}
-
-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.
- ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
-}
-
-
-void CVradStaticPropMgr::ComputeLightingForProp( int iThread, int iStaticProp )
-{
- // Compute the lighting.
- CComputeStaticPropLightingResults results;
- ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results );
- ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results );
-}
-
-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;
- }
-
- StartPacifier( "Computing static prop lighting : " );
-
- // ensure any traces against us are ignored because we have no inherit lighting contribution
- m_bIgnoreStaticPropTrace = true;
-
- if ( g_bUseMPI )
- {
- // Distribute the work among the workers.
- VMPI_SetCurrentStage( "CVradStaticPropMgr::ComputeLighting" );
-
- DistributeWork(
- count,
- VMPI_DISTRIBUTEWORK_PACKETID,
- &CVradStaticPropMgr::VMPI_ProcessStaticProp_Static,
- &CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static );
- }
- else
- {
- RunThreadsOn(count, true, ThreadComputeStaticPropLighting);
- }
-
- // restore default
- m_bIgnoreStaticPropTrace = false;
-
- // save data to bsp
- SerializeLighting();
-
- EndPacifier( true );
-}
-
-//-----------------------------------------------------------------------------
-// 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
- return;
- }
- // only init the triangle table the first time
- bool bInitTriangles = dict.m_triangleMaterialIndex.Count() ? false : true;
- int triangleIndex = 0;
-
- // 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 );
-
- if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
- {
- for ( int i = 0; i < pStrip->numIndices; i += 3 )
- {
- 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 )
- {
- if ( bInitTriangles )
- {
- // 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 )
- {
- materialIndex = g_ShadowTextureList.AddMaterialEntry( shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) );
- color.x = coverage;
- }
- else
- {
- materialIndex = -1;
- }
- dict.m_triangleMaterialIndex.AddToTail(materialIndex);
- }
- else
- {
- materialIndex = dict.m_triangleMaterialIndex[triangleIndex];
- triangleIndex++;
- }
- if ( materialIndex >= 0 )
- {
- flags = FCACHETRI_TRANSPARENT;
- }
- }
-// 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));
- g_RtEnv.AddTriangle( TRACE_ID_STATICPROP | nProp,
- position1, position2, position3,
- color, flags, materialIndex);
- }
- }
- else
- {
- // all tris expected to be discrete tri lists
- // must fixme if stripping ever occurs
- printf( "unexpected strips found\n" );
- Assert( 0 );
- return;
- }
- }
- }
- }
- }
- }
- }
-}
-
-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 );
-
- if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST )
- {
- for ( int i = 0; i < pStrip->numIndices; i += 3 )
- {
- 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 );
- }
- }
- else
- {
- // all tris expected to be discrete tri lists
- // must fixme if stripping ever occurs
- printf( "unexpected strips found\n" );
- Assert( 0 );
- return;
- }
- }
- }
- }
- }
- }
-}
-
-const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void *pModelData )
-{
- studiohdr_t *pActiveStudioHdr = static_cast<studiohdr_t *>(pModelData);
- Assert( pActiveStudioHdr );
-
- if ( pActiveStudioHdr->pVertexBase )
- {
- return (vertexFileHeader_t *)pActiveStudioHdr->pVertexBase;
- }
-
- // 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
- FileHandle_t fileHandle = g_pFileSystem->Open( fileName, "rb" );
- if ( !fileHandle )
- {
- Error( "Unable to load vertex data \"%s\"\n", fileName );
- }
-
- // Get the file size
- int vvdSize = g_pFileSystem->Size( fileHandle );
- if ( vvdSize == 0 )
- {
- g_pFileSystem->Close( fileHandle );
- Error( "Bad size for vertex data \"%s\"\n", fileName );
- }
-
- vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)malloc( vvdSize );
- g_pFileSystem->Read( pVvdHdr, vvdSize, fileHandle );
- g_pFileSystem->Close( fileHandle );
-
- // 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
- Studio_LoadVertexes( pVvdHdr, pNewVvdHdr, 0, true );
-
- // discard original
- free( pVvdHdr );
- pVvdHdr = pNewVvdHdr;
-
- pActiveStudioHdr->pVertexBase = (void*)pVvdHdr;
- return pVvdHdr;
-}
+//========= 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" +#include "UtlBuffer.h" +#include "utlvector.h" +#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" +#include "phyfile.h" +#include "collisionutils.h" +#include "tier1/KeyValues.h" +#include "pacifier.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/hardwareverts.h" +#include "byteswap.h" +#include "mpivrad.h" +#include "vtf/vtf.h" +#include "tier1/utldict.h" +#include "tier1/utlsymbol.h" + +#include "messbuf.h" +#include "vmpi.h" +#include "vmpi_distribute_work.h" + + +#define ALIGN_TO_POW2(x,y) (((x)+(y-1))&~(y-1)) + +// identifies a vertex embedded in solid +// lighting will be copied from nearest valid neighbor +struct badVertex_t +{ + int m_ColorVertex; + Vector m_Position; + Vector m_Normal; +}; + +// a final colored vertex +struct colorVertex_t +{ + Vector m_Color; + Vector m_Position; + bool m_bValid; +}; + +class CComputeStaticPropLightingResults +{ +public: + ~CComputeStaticPropLightingResults() + { + m_ColorVertsArrays.PurgeAndDeleteElements(); + } + + CUtlVector< CUtlVector<colorVertex_t>* > m_ColorVertsArrays; +}; + +//----------------------------------------------------------------------------- +// 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 ); + +private: + // 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 ); + + // 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 + }; + + struct MeshData_t + { + CUtlVector<Vector> m_Verts; + 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; + bool m_bLightingOriginValid; + }; + + // 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 ); + void ApplyLightingToStaticProp( CStaticProp &prop, const CComputeStaticPropLightingResults *pResults ); + + 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 ) +{ + 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) + return s_pPhysCollision->ConvertConvexToCollide( convexHulls.Base(), convexHulls.Size() ); +} + + +//----------------------------------------------------------------------------- +// 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 + pHdr->pVertexBase = NULL; + pHdr->pIndexBase = NULL; + + 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 ) ); + strcat( filename, ".dx80.vtx" ); + + 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 ) + { + float umin = min(t0.x, t1.x); + umin = min(umin, t2.x); + float umax = max(t0.x, t1.x); + umax = max(umax, t2.x); + + float vmin = min(t0.y, t1.y); + vmin = min(vmin, t2.y); + float vmax = max(t0.y, t1.y); + vmax = max(vmax, t2.y); + + // 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]; + int u = RoundFloatToInt( uv[0] * tex.width ); + int v = RoundFloatToInt( uv[1] * tex.height ); + + // asume power of 2, clamp or wrap + // UNDONE: Support clamp? This code should work +#if 0 + u = tex.clampU ? clamp(u,0,(tex.width-1)) : (u & (tex.width-1)); + v = tex.clampV ? clamp(v,0,(tex.height-1)) : (v & (tex.height-1)); +#else + // for now always wrap + u &= (tex.width-1); + v &= (tex.height-1); +#endif + + return tex.pAlphaTexels[v * tex.width + u]; + } + + 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; + } + +} + + +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(); +} + + +//----------------------------------------------------------------------------- +// 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() ); + } + } +} + + +//----------------------------------------------------------------------------- +// 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 ); + } +} + +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; + } +} + +//----------------------------------------------------------------------------- +// 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 ) + { + Error( "Cannot load the static props... encountered a stale map version. Re-vbsp the map." ); + } + + 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 + for (int i = m_StaticPropDict.Size(); --i >= 0; ) + { + studiohdr_t *pStudioHdr = m_StaticPropDict[i].m_pStudioHdr; + if ( pStudioHdr ) + { + if ( pStudioHdr->pVertexBase ) + { + free( pStudioHdr->pVertexBase ); + } + 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 ) +{ + int ndxLeaf = PointLeafnum( position ); + if ( dleafs[ndxLeaf].contents & CONTENTS_SOLID ) + { + // position embedded in solid + return true; + } + + return false; +} + +//----------------------------------------------------------------------------- +// Trace from a vertex to each direct light source, accumulating its contribution. +//----------------------------------------------------------------------------- +void ComputeDirectLightingAtPoint( Vector &position, Vector &normal, Vector &outColor, int iThread, + int static_prop_id_to_skip=-1, int nLFlags = 0) +{ + SSE_sampleLightOutput_t sampleOutput; + + outColor.Init(); + + // 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; + + 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 ); + } + fudge *= 4.0; + adjusted_pos += fudge; + } + else + { + // push out along normal + adjusted_pos += 4.0 * normal; +// flEpsilon = 1.0; + } + + FourVectors adjusted_pos4; + FourVectors normal4; + adjusted_pos4.DuplicateVector( adjusted_pos ); + normal4.DuplicateVector( normal ); + + GatherSampleLightSSE( sampleOutput, dl, -1, adjusted_pos4, &normal4, 1, iThread, nLFlags | GATHERLFLAGS_FORCE_FAST, + static_prop_id_to_skip, flEpsilon ); + + VectorMA( outColor, sampleOutput.m_flFalloff.m128_f32[0] * sampleOutput.m_flDot[0].m128_f32[0], dl->light.intensity, outColor ); + } +} + +//----------------------------------------------------------------------------- +// 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; + + 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 iCurColorVertsArray = 0; + 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 ); + + const CUtlVector<colorVertex_t> &colorVerts = *pResults->m_ColorVertsArrays[iCurColorVertsArray++]; + + 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(); + prop.m_MeshData[nMeshIdx].m_Verts.AddMultipleToTail( pStripGroup->numVerts ); + prop.m_MeshData[nMeshIdx].m_nLod = nLod; + + for ( int nVertex = 0; nVertex < pStripGroup->numVerts; ++nVertex ) + { + int nIndex = pMesh->vertexoffset + pStripGroup->pVertex( nVertex )->origMeshVertID; + + Assert( nIndex < pStudioModel->numvertices ); + prop.m_MeshData[nMeshIdx].m_Verts[nVertex] = colorVerts[nIndex].m_Color; + } + } + } + } + } + } +} + +//----------------------------------------------------------------------------- +// 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; + } + + if (prop.m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING ) + return; + + VMPI_SetCurrentStage( "ComputeLighting" ); + + 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 ); + + 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); + Assert( vertData ); // This can only return NULL on X360 for now + for ( int vertexID = 0; vertexID < pStudioMesh->numvertices; ++vertexID ) + { + Vector sampleNormal; + Vector samplePosition; + // transform position and normal into world coordinate system + 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, sampleNormal ); + + 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; + badVertex.m_Normal = sampleNormal; + badVerts.AddToTail( badVertex ); + } + else + { + Vector direct_pos=samplePosition; + int skip_prop = -1; + if ( g_bDisablePropSelfShadowing || ( prop.m_Flags & STATIC_PROP_NO_SELF_SHADOWING ) ) + { + skip_prop = prop_index; + } + + int nFlags = ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS ) ? GATHERLFLAGS_IGNORE_NORMALS : 0; + + Vector directColor(0,0,0); + ComputeDirectLightingAtPoint( direct_pos, + sampleNormal, directColor, iThread, + skip_prop, nFlags ); + Vector indirectColor(0,0,0); + + if (g_bShowStaticPropNormals) + { + directColor= sampleNormal; + directColor += Vector(1.0,1.0,1.0); + directColor *= 50.0; + } + else + { + if (numbounce >= 1) + ComputeIndirectLightingAtPoint( + samplePosition, sampleNormal, + indirectColor, iThread, true, + ( prop.m_Flags & STATIC_PROP_IGNORE_NORMALS) != 0 ); + } + + colorVerts[numVertexes].m_bValid = true; + colorVerts[numVertexes].m_Position = samplePosition; + VectorAdd( directColor, indirectColor, colorVerts[numVertexes].m_Color ); + } + + 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 + // sudivide to determine a closer valid point to the bad vertex, and re-light + 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; + } + + // re-light from better position + Vector directColor; + ComputeDirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, directColor, iThread ); + + Vector indirectColor; + ComputeIndirectLightingAtPoint( bestPosition, badVerts[nBadVertex].m_Normal, + indirectColor, iThread, true ); + + // save results, not changing valid status + // to ensure this offset position is not considered as a viable candidate + colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Position = bestPosition; + VectorAdd( directColor, indirectColor, colorVerts[badVerts[nBadVertex].m_ColorVertex].m_Color ); + } + } + + // discard bad verts + badVerts.Purge(); + } + } +} + +//----------------------------------------------------------------------------- +// Write the lighitng to bsp pak lump +//----------------------------------------------------------------------------- +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) + { + // no need to write this file if we didn't compute the data + // props marked this way will not load the info anyway + if ( m_StaticProps[i].m_Flags & STATIC_PROP_NO_PER_VERTEX_LIGHTING ) + continue; + + 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++ ) + { + totalVertexes += m_StaticProps[i].m_MeshData[j].m_Verts.Count(); + } + + // allocate a buffer with enough padding for alignment + size = sizeof( HardwareVerts::FileHeader_t ) + + m_StaticProps[i].m_MeshData.Count()*sizeof(HardwareVerts::MeshHeader_t) + + totalVertexes*4 + 2*512; + 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)); + pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 ); + + // construct header + pVhvHdr->m_nVersion = VHV_VERSION; + pVhvHdr->m_nChecksum = m_StaticPropDict[m_StaticProps[i].m_ModelIdx].m_pStudioHdr->checksum; + pVhvHdr->m_nVertexFlags = VERTEX_COLOR; + pVhvHdr->m_nVertexSize = 4; + 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; + pMesh->m_nVertexes = m_StaticProps[i].m_MeshData[n].m_Verts.Count(); + pMesh->m_nOffset = (unsigned int)pVertexData - (unsigned int)pVhvHdr; + + // construct vertexes + for (int k=0; k<pMesh->m_nVertexes; k++) + { + Vector &vector = m_StaticProps[i].m_MeshData[n].m_Verts[k]; + + ColorRGBExp32 rgbColor; + VectorToColorRGBExp32( vector, rgbColor ); + unsigned char dstColor[4]; + ConvertRGBExp32ToRGBA8888( &rgbColor, dstColor ); + + // b,g,r,a order + pVertexData[0] = dstColor[2]; + pVertexData[1] = dstColor[1]; + pVertexData[2] = dstColor[0]; + pVertexData[3] = dstColor[3]; + pVertexData += 4; + } + } + + // align to end of file + pVertexData = (unsigned char *)((unsigned int)pVertexData - (unsigned int)pVhvHdr); + pVertexData = (unsigned char*)pVhvHdr + ALIGN_TO_POW2( (unsigned int)pVertexData, 512 ); + + AddBufferToPak( GetPakFile(), filename, (void*)pVhvHdr, pVertexData - (unsigned char*)pVhvHdr, false ); + } +} + +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. + ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results ); +} + + +void CVradStaticPropMgr::ComputeLightingForProp( int iThread, int iStaticProp ) +{ + // Compute the lighting. + CComputeStaticPropLightingResults results; + ComputeLighting( m_StaticProps[iStaticProp], iThread, iStaticProp, &results ); + ApplyLightingToStaticProp( m_StaticProps[iStaticProp], &results ); +} + +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; + } + + StartPacifier( "Computing static prop lighting : " ); + + // ensure any traces against us are ignored because we have no inherit lighting contribution + m_bIgnoreStaticPropTrace = true; + + if ( g_bUseMPI ) + { + // Distribute the work among the workers. + VMPI_SetCurrentStage( "CVradStaticPropMgr::ComputeLighting" ); + + DistributeWork( + count, + VMPI_DISTRIBUTEWORK_PACKETID, + &CVradStaticPropMgr::VMPI_ProcessStaticProp_Static, + &CVradStaticPropMgr::VMPI_ReceiveStaticPropResults_Static ); + } + else + { + RunThreadsOn(count, true, ThreadComputeStaticPropLighting); + } + + // restore default + m_bIgnoreStaticPropTrace = false; + + // save data to bsp + SerializeLighting(); + + EndPacifier( true ); +} + +//----------------------------------------------------------------------------- +// 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 + return; + } + // only init the triangle table the first time + bool bInitTriangles = dict.m_triangleMaterialIndex.Count() ? false : true; + int triangleIndex = 0; + + // 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 ); + + if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST ) + { + for ( int i = 0; i < pStrip->numIndices; i += 3 ) + { + 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 ) + { + if ( bInitTriangles ) + { + // 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 ) + { + materialIndex = g_ShadowTextureList.AddMaterialEntry( shadowTextureIndex, *vertData->Texcoord(vertex1), *vertData->Texcoord(vertex2), *vertData->Texcoord(vertex3) ); + color.x = coverage; + } + else + { + materialIndex = -1; + } + dict.m_triangleMaterialIndex.AddToTail(materialIndex); + } + else + { + materialIndex = dict.m_triangleMaterialIndex[triangleIndex]; + triangleIndex++; + } + if ( materialIndex >= 0 ) + { + flags = FCACHETRI_TRANSPARENT; + } + } +// 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)); + g_RtEnv.AddTriangle( TRACE_ID_STATICPROP | nProp, + position1, position2, position3, + color, flags, materialIndex); + } + } + else + { + // all tris expected to be discrete tri lists + // must fixme if stripping ever occurs + printf( "unexpected strips found\n" ); + Assert( 0 ); + return; + } + } + } + } + } + } + } +} + +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 ); + + if ( pStrip->flags & OptimizedModel::STRIP_IS_TRILIST ) + { + for ( int i = 0; i < pStrip->numIndices; i += 3 ) + { + 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 ); + } + } + else + { + // all tris expected to be discrete tri lists + // must fixme if stripping ever occurs + printf( "unexpected strips found\n" ); + Assert( 0 ); + return; + } + } + } + } + } + } +} + +const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void *pModelData ) +{ + studiohdr_t *pActiveStudioHdr = static_cast<studiohdr_t *>(pModelData); + Assert( pActiveStudioHdr ); + + if ( pActiveStudioHdr->pVertexBase ) + { + return (vertexFileHeader_t *)pActiveStudioHdr->pVertexBase; + } + + // 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 + FileHandle_t fileHandle = g_pFileSystem->Open( fileName, "rb" ); + if ( !fileHandle ) + { + Error( "Unable to load vertex data \"%s\"\n", fileName ); + } + + // Get the file size + int vvdSize = g_pFileSystem->Size( fileHandle ); + if ( vvdSize == 0 ) + { + g_pFileSystem->Close( fileHandle ); + Error( "Bad size for vertex data \"%s\"\n", fileName ); + } + + vertexFileHeader_t *pVvdHdr = (vertexFileHeader_t *)malloc( vvdSize ); + g_pFileSystem->Read( pVvdHdr, vvdSize, fileHandle ); + g_pFileSystem->Close( fileHandle ); + + // 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 + Studio_LoadVertexes( pVvdHdr, pNewVvdHdr, 0, true ); + + // discard original + free( pVvdHdr ); + pVvdHdr = pNewVvdHdr; + + pActiveStudioHdr->pVertexBase = (void*)pVvdHdr; + return pVvdHdr; +} |