You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
971 lines
25 KiB
971 lines
25 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
//=============================================================================// |
|
|
|
#include "stdafx.h" |
|
#include "materialsystem/imaterialsystem.h" |
|
#include "istudiorender.h" |
|
#include "material.h" |
|
#include "materialsystem/imesh.h" |
|
#include "disp_common.h" |
|
#include "bsplighting.h" |
|
#include "interface.h" |
|
#include "filesystem.h" |
|
#include "hammer.h" |
|
#include "tier0/dbg.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
|
|
bool SurfHasBumpedLightmaps( int flags ) |
|
{ |
|
return ( flags & SURF_BUMPLIGHT ) && |
|
( !( flags & SURF_NOLIGHT ) ); |
|
} |
|
|
|
|
|
void InitLMSamples( Vector4D *pSamples, int nSamples, float value ) |
|
{ |
|
for( int i=0; i < nSamples; i++ ) |
|
{ |
|
pSamples[i][0] = pSamples[i][1] = pSamples[i][2] = value; |
|
pSamples[i][3] = 1.0f; |
|
} |
|
} |
|
|
|
|
|
void InitLMSamplesRed( Vector4D *pSamples, int nSamples ) |
|
{ |
|
for( int i=0; i < nSamples; i++ ) |
|
{ |
|
pSamples[i][0] = 1; |
|
pSamples[i][1] = pSamples[i][2] = 0; |
|
pSamples[i][3] = 1.0f; |
|
} |
|
} |
|
|
|
|
|
CBSPLighting::CMaterialBuf::CMaterialBuf() |
|
{ |
|
m_nVerts = m_nIndices = 0; |
|
m_pMesh = NULL; |
|
} |
|
|
|
|
|
CBSPLighting::CMaterialBuf::~CMaterialBuf() |
|
{ |
|
if( m_pMesh ) |
|
{ |
|
CMatRenderContextPtr pRenderContext( MaterialSystemInterface() ); |
|
pRenderContext->DestroyStaticMesh( m_pMesh ); |
|
} |
|
|
|
m_DrawCommands.PurgeAndDeleteElements(); |
|
} |
|
|
|
|
|
CBSPLighting::CFaceMaterial::~CFaceMaterial() |
|
{ |
|
m_MaterialBufs.PurgeAndDeleteElements(); |
|
m_Faces.PurgeAndDeleteElements(); |
|
} |
|
|
|
|
|
CBSPLighting::CBSPLighting() |
|
{ |
|
m_nTotalTris = 0; |
|
m_hVRadDLL = 0; |
|
m_pVRadDLL = 0; |
|
m_pBSPLightingThread = 0; |
|
m_bLightingInProgress = false; |
|
} |
|
|
|
|
|
CBSPLighting::~CBSPLighting() |
|
{ |
|
Term(); |
|
} |
|
|
|
|
|
void CBSPLighting::Release() |
|
{ |
|
delete this; |
|
} |
|
|
|
|
|
bool CBSPLighting::Load( char const *pFilename ) |
|
{ |
|
// Free everything. |
|
Term(); |
|
|
|
|
|
// Load VRAD's DLL (and load the BSP file). |
|
if( !LoadVRADDLL( pFilename ) ) |
|
return false; |
|
|
|
|
|
// Create the lighting thread. |
|
m_pBSPLightingThread = CreateBSPLightingThread( m_pVRadDLL ); |
|
if( !m_pBSPLightingThread ) |
|
return false; |
|
|
|
|
|
// Get the BSP file information from VRAD. |
|
CBSPInfo file; |
|
m_pVRadDLL->GetBSPInfo( &file ); |
|
|
|
|
|
// Allocate faces and verts. |
|
CUtlVector<char> usedFaces; |
|
usedFaces.SetSize( file.numfaces ); |
|
|
|
int nFaces = 0; |
|
int nVerts = 0; |
|
for( int iCountFace=0; iCountFace < file.numfaces; iCountFace++ ) |
|
{ |
|
usedFaces[iCountFace] = 0; |
|
|
|
// Was checking m_LightmapTextureSizeInLuxels[0] twice. Fixing but then |
|
// commenting out the second check to avoid changing the behavior. |
|
if( file.dfaces[iCountFace].m_LightmapTextureSizeInLuxels[0] != 0 /*|| |
|
file.dfaces[iCountFace].m_LightmapTextureSizeInLuxels[1] != 0*/ ) |
|
{ |
|
texinfo_t *pTexInfo = &file.texinfo[ file.dfaces[iCountFace].texinfo ]; |
|
|
|
if( !(pTexInfo->flags & SURF_NODRAW) ) |
|
{ |
|
++nFaces; |
|
nVerts += file.dfaces[iCountFace].numedges; |
|
usedFaces[iCountFace] = 1; |
|
} |
|
} |
|
} |
|
|
|
|
|
CUtlVector<CFace> faces; |
|
faces.SetSize( nFaces ); |
|
|
|
CUtlVector<CVert> verts; |
|
verts.SetSize( nVerts ); |
|
|
|
m_StoredFaces.SetSize( nFaces ); |
|
|
|
|
|
InitMaterialLUT( file ); |
|
|
|
|
|
// Make lightmaps and translate the map faces over.. |
|
IMaterialSystem *pMatSys = MaterialSystemInterface(); |
|
|
|
// Add the BSP file as a search path so our FindMaterial calls will get |
|
// VMFs embedded in the BSP file. |
|
g_pFullFileSystem->AddSearchPath( pFilename, "GAME" ); |
|
|
|
m_nTotalTris = 0; |
|
int iOutVert = 0; |
|
int iOutFace = 0; |
|
for( int iFace=0; iFace < file.numfaces; iFace++ ) |
|
{ |
|
dface_t *pIn = &file.dfaces[iFace]; |
|
|
|
if( !usedFaces[iFace] ) |
|
{ |
|
continue; |
|
} |
|
|
|
CFace *pOut = &faces[iOutFace]; |
|
CStoredFace *pStoredFace = &m_StoredFaces[iOutFace]; |
|
|
|
++iOutFace; |
|
|
|
pStoredFace->m_iMapFace = iFace; |
|
pStoredFace->m_pFace = pOut; |
|
|
|
pOut->m_pDFace = pIn; |
|
pOut->m_pStoredFace = pStoredFace; |
|
|
|
// Get its material. |
|
texinfo_t *pTexInfo = &file.texinfo[pIn->texinfo]; |
|
dtexdata_t *pTexData = &file.dtexdata[pTexInfo->texdata]; |
|
pStoredFace->m_pMaterial = FindOrAddMaterial( file, pTexData->nameStringTableID ); |
|
if( pStoredFace->m_pMaterial ) |
|
pStoredFace->m_pMaterial->m_Faces.AddToTail( pStoredFace ); |
|
|
|
// Setup its lightmap. |
|
memcpy( pOut->m_LightmapVecs, file.texinfo[pIn->texinfo].lightmapVecsLuxelsPerWorldUnits, sizeof(pOut->m_LightmapVecs) ); |
|
memcpy( pOut->m_LightmapTextureMinsInLuxels, pIn->m_LightmapTextureMinsInLuxels, sizeof(pOut->m_LightmapTextureMinsInLuxels) ); |
|
|
|
pStoredFace->m_LightmapSize[0] = pIn->m_LightmapTextureSizeInLuxels[0]+1; |
|
pStoredFace->m_LightmapSize[1] = pIn->m_LightmapTextureSizeInLuxels[1]+1; |
|
|
|
// Setup the verts. |
|
pOut->m_iVertStart = iOutVert; |
|
pOut->m_nVerts = pIn->numedges; |
|
for( int iEdge=0; iEdge < pIn->numedges; iEdge++ ) |
|
{ |
|
int edgeVal = file.dsurfedges[ pIn->firstedge + iEdge ]; |
|
if( edgeVal < 0 ) |
|
verts[pOut->m_iVertStart+iEdge].m_vPos = file.dvertexes[ file.dedges[-edgeVal].v[1] ].point; |
|
else |
|
verts[pOut->m_iVertStart+iEdge].m_vPos = file.dvertexes[ file.dedges[edgeVal].v[0] ].point; |
|
} |
|
m_nTotalTris += pOut->m_nVerts - 2; |
|
|
|
iOutVert += pOut->m_nVerts; |
|
pOut->m_iDispInfo = pIn->dispinfo; |
|
} |
|
|
|
g_pFullFileSystem->RemoveSearchPath( pFilename, "GAME" ); |
|
|
|
|
|
// Allocate lightmaps.. must be grouped by material. |
|
pMatSys->ResetMaterialLightmapPageInfo(); |
|
|
|
pMatSys->BeginLightmapAllocation(); |
|
|
|
FOR_EACH_LL( m_FaceMaterials, iMat ) |
|
{ |
|
CFaceMaterial *pMat = m_FaceMaterials[iMat]; |
|
bool bNeedsBumpmap = pMat->m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ); |
|
|
|
FOR_EACH_LL( pMat->m_Faces, iFace ) |
|
{ |
|
CStoredFace *pStoredFace = pMat->m_Faces[iFace]; |
|
CFace *pOut = pStoredFace->m_pFace; |
|
|
|
int bumpedSize = pStoredFace->m_LightmapSize[0]; |
|
if( bNeedsBumpmap ) |
|
bumpedSize *= 4; |
|
|
|
pOut->m_LightmapSortID = pMatSys->AllocateLightmap( |
|
bumpedSize, |
|
pStoredFace->m_LightmapSize[1], |
|
pStoredFace->m_OffsetIntoLightmapPage, |
|
pMat->m_pMaterial ); |
|
} |
|
} |
|
|
|
pMatSys->EndLightmapAllocation(); |
|
|
|
|
|
// Get sort IDs from the material system. |
|
CUtlVector<MaterialSystem_SortInfo_t> sortInfos; |
|
sortInfos.SetSize( pMatSys->GetNumSortIDs() ); |
|
pMatSys->GetSortInfo( sortInfos.Base() ); |
|
|
|
for( int iFace=0; iFace < faces.Count(); iFace++ ) |
|
{ |
|
m_StoredFaces[iFace].m_LightmapPageID = sortInfos[faces[iFace].m_LightmapSortID].lightmapPageID; |
|
} |
|
|
|
// Setup the gamma table. |
|
BuildGammaTable( 2.2f, 2.2f, 0, 1 ); |
|
|
|
|
|
// Set lightmap texture coordinates. |
|
for( int iFace=0; iFace < faces.Size(); iFace++ ) |
|
{ |
|
CFace *pFace = &faces[iFace]; |
|
CStoredFace *pStoredFace = &m_StoredFaces[iFace]; |
|
texinfo_t *pTexInfo = &file.texinfo[pFace->m_pDFace->texinfo]; |
|
|
|
int lightmapPageSize[2]; |
|
pMatSys->GetLightmapPageSize( pFace->m_pStoredFace->m_LightmapPageID, &lightmapPageSize[0], &lightmapPageSize[1] ); |
|
|
|
pStoredFace->m_BumpSTexCoordOffset = (float)pStoredFace->m_LightmapSize[0] / lightmapPageSize[0]; |
|
|
|
// Set its texture coordinates. |
|
for( int iVert=0; iVert < pFace->m_nVerts; iVert++ ) |
|
{ |
|
CVert *pVert = &verts[ pFace->m_iVertStart + iVert ]; |
|
Vector &vPos = pVert->m_vPos; |
|
|
|
for( int iCoord=0; iCoord < 2; iCoord++ ) |
|
{ |
|
float *lmVec = pFace->m_LightmapVecs[iCoord]; |
|
float flVal = lmVec[0]*vPos[0] + lmVec[1]*vPos[1] + lmVec[2]*vPos[2] + lmVec[3] - pFace->m_LightmapTextureMinsInLuxels[iCoord]; |
|
|
|
flVal += pFace->m_pStoredFace->m_OffsetIntoLightmapPage[iCoord]; |
|
flVal += 0.5f; // bilinear... |
|
flVal /= lightmapPageSize[iCoord]; |
|
Assert( _finite(flVal) ); |
|
pVert->m_vLightCoords[iCoord] = flVal; |
|
|
|
pVert->m_vTexCoords[iCoord] = |
|
DotProduct( vPos, *((Vector*)pTexInfo->textureVecsTexelsPerWorldUnits[iCoord]) ) + |
|
pTexInfo->textureVecsTexelsPerWorldUnits[iCoord][3]; |
|
|
|
if( pStoredFace->m_pMaterial ) |
|
{ |
|
if( iCoord == 0 ) |
|
pVert->m_vTexCoords[iCoord] /= pStoredFace->m_pMaterial->m_pMaterial->GetMappingWidth(); |
|
else |
|
pVert->m_vTexCoords[iCoord] /= pStoredFace->m_pMaterial->m_pMaterial->GetMappingHeight(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
// Create displacements. |
|
CUtlVector<CDispInfoFaces> dispInfos; |
|
CreateDisplacements( file, faces, dispInfos ); |
|
|
|
BuildLMGroups( file, faces, verts, dispInfos ); |
|
BuildDrawCommands(); |
|
|
|
ReloadLightmaps(); |
|
return true; |
|
} |
|
|
|
|
|
void CBSPLighting::Term() |
|
{ |
|
if( m_pBSPLightingThread ) |
|
{ |
|
m_pBSPLightingThread->Release(); |
|
m_pBSPLightingThread = 0; |
|
} |
|
|
|
m_nTotalTris = 0; |
|
|
|
if( m_hVRadDLL ) |
|
{ |
|
if( m_pVRadDLL ) |
|
{ |
|
// Save the .r0 and .bsp files. |
|
m_pVRadDLL->Serialize(); |
|
|
|
m_pVRadDLL->Release(); |
|
m_pVRadDLL = 0; |
|
} |
|
|
|
Sys_UnloadModule( m_hVRadDLL ); |
|
m_hVRadDLL = 0; |
|
} |
|
|
|
m_StoredFaces.Purge(); |
|
} |
|
|
|
|
|
bool CBSPLighting::Serialize() |
|
{ |
|
if( m_pBSPLightingThread ) |
|
{ |
|
// Only serialize if we're not currently in the middle of lighting. |
|
if( m_pBSPLightingThread->GetCurrentState() == IBSPLightingThread::STATE_FINISHED ) |
|
return m_pVRadDLL->Serialize(); |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
void CBSPLighting::StartLighting( char const *pVMFFileWithEnts ) |
|
{ |
|
if( m_pBSPLightingThread ) |
|
{ |
|
m_pBSPLightingThread->StartLighting( pVMFFileWithEnts ); |
|
m_bLightingInProgress = true; |
|
} |
|
} |
|
|
|
|
|
float CBSPLighting::GetPercentComplete() |
|
{ |
|
if( m_bLightingInProgress && m_pBSPLightingThread ) |
|
return m_pBSPLightingThread->GetPercentComplete(); |
|
else |
|
return -1; |
|
} |
|
|
|
|
|
void CBSPLighting::Interrupt() |
|
{ |
|
if( m_pBSPLightingThread ) |
|
m_pBSPLightingThread->Interrupt(); |
|
} |
|
|
|
|
|
bool CBSPLighting::CheckForNewLightmaps() |
|
{ |
|
if( !m_pBSPLightingThread ) |
|
return false; |
|
|
|
// Has it finished lighting? |
|
int curState = m_pBSPLightingThread->GetCurrentState(); |
|
if( m_bLightingInProgress ) |
|
{ |
|
if( curState == IBSPLightingThread::STATE_FINISHED ) |
|
{ |
|
m_bLightingInProgress = false; |
|
ReloadLightmaps(); |
|
return true; |
|
} |
|
else if( curState == IBSPLightingThread::STATE_IDLE ) |
|
{ |
|
m_bLightingInProgress = false; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
#define DRAWLIGHTMAPPAGE |
|
#if defined( DRAWLIGHTMAPPAGE ) |
|
void DrawLightmapPage( IMaterialSystem *materialSystemInterface, int lightmapPageID ) |
|
{ |
|
IMaterial *g_materialDebugLightmap = materialSystemInterface->FindMaterial( "debug/debuglightmap", TEXTURE_GROUP_OTHER ); |
|
|
|
// assumes that we are already in ortho mode. |
|
int lightmapPageWidth, lightmapPageHeight; |
|
|
|
CMatRenderContextPtr pRenderContext( materialSystemInterface ); |
|
IMesh* pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, g_materialDebugLightmap ); |
|
|
|
materialSystemInterface->GetLightmapPageSize( lightmapPageID, &lightmapPageWidth, &lightmapPageHeight ); |
|
pRenderContext->BindLightmapPage( lightmapPageID ); |
|
|
|
CMeshBuilder meshBuilder; |
|
meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); |
|
|
|
// texcoord 1 is lightmaptexcoord for fixed function. |
|
static int yOffset = 30; |
|
|
|
meshBuilder.TexCoord2f( 1, 0.0f, 0.0f ); |
|
meshBuilder.Position3f( 0.0f, yOffset, 0.0f ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.TexCoord2f( 1, 1.0f, 0.0f ); |
|
meshBuilder.Position3f( lightmapPageWidth, yOffset, 0.0f ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.TexCoord2f( 1, 1.0f, 1.0f ); |
|
meshBuilder.Position3f( lightmapPageWidth, yOffset+lightmapPageHeight, 0.0f ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
meshBuilder.TexCoord2f( 1, 0.0f, 1.0f ); |
|
meshBuilder.Position3f( 0.0f, yOffset+lightmapPageHeight, 0.0f ); |
|
meshBuilder.AdvanceVertex(); |
|
|
|
|
|
meshBuilder.End(); |
|
pMesh->Draw(); |
|
} |
|
#endif |
|
|
|
|
|
void CBSPLighting::Draw() |
|
{ |
|
if( m_FaceMaterials.Count() == 0 ) |
|
return; |
|
|
|
IMaterialSystem *pMatSys = MaterialSystemInterface(); |
|
if( !pMatSys ) |
|
return; |
|
|
|
CMatRenderContextPtr pRenderContext( pMatSys ); |
|
|
|
CheckForNewLightmaps(); |
|
|
|
pRenderContext->Flush(); |
|
|
|
#if defined( DRAWLIGHTMAPPAGE ) |
|
static bool bDrawIt = false; |
|
if( bDrawIt ) |
|
{ |
|
pRenderContext->MatrixMode( MATERIAL_VIEW ); |
|
pRenderContext->PushMatrix(); |
|
pRenderContext->LoadIdentity(); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_MODEL ); |
|
pRenderContext->PushMatrix(); |
|
pRenderContext->LoadIdentity(); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); |
|
pRenderContext->PushMatrix(); |
|
pRenderContext->LoadIdentity(); |
|
pRenderContext->Ortho( 0, 0, 300, 300, -99999, 99999 ); |
|
|
|
static int iPageToDraw = 0; |
|
DrawLightmapPage( MaterialSystemInterface(), iPageToDraw ); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_VIEW ); |
|
pRenderContext->PopMatrix(); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_MODEL ); |
|
pRenderContext->PopMatrix(); |
|
|
|
pRenderContext->MatrixMode( MATERIAL_PROJECTION ); |
|
pRenderContext->PopMatrix(); |
|
} |
|
#endif |
|
|
|
// Draw everything from each material. |
|
FOR_EACH_LL( m_FaceMaterials, iMat ) |
|
{ |
|
CFaceMaterial *pMat = m_FaceMaterials[iMat]; |
|
|
|
pRenderContext->Bind( pMat->m_pMaterial ); |
|
|
|
FOR_EACH_LL( pMat->m_MaterialBufs, iBuf ) |
|
{ |
|
CMaterialBuf *pBuf = pMat->m_MaterialBufs[iBuf]; |
|
|
|
for( int iCmd=0; iCmd < pBuf->m_DrawCommands.Count(); iCmd++ ) |
|
{ |
|
CDrawCommand *pCmd = pBuf->m_DrawCommands[iCmd]; |
|
|
|
pRenderContext->BindLightmapPage( pCmd->m_LightmapPageID ); |
|
pBuf->m_pMesh->Draw( pCmd->m_PrimLists.Base(), pCmd->m_PrimLists.Count() ); |
|
} |
|
} |
|
} |
|
|
|
pRenderContext->Flush(); |
|
} |
|
|
|
|
|
void CBSPLighting::AssignFaceMaterialCounts( |
|
CBSPInfo &file, |
|
CUtlVector<CFace> &faces ) |
|
{ |
|
FOR_EACH_LL( m_FaceMaterials, i ) |
|
{ |
|
CFaceMaterial *pMat = m_FaceMaterials[i]; |
|
|
|
// Start off an initial CMaterialBuf to dump the faces in. |
|
CMaterialBuf *pBuf = new CMaterialBuf; |
|
pMat->m_MaterialBufs.AddToTail( pBuf ); |
|
|
|
FOR_EACH_LL( pMat->m_Faces, iFace ) |
|
{ |
|
CStoredFace *pStoredFace = pMat->m_Faces[iFace]; |
|
CFace *pFace = pStoredFace->m_pFace; |
|
|
|
pStoredFace->m_iFirstIndex = pBuf->m_nIndices; |
|
|
|
if( pFace->m_iDispInfo == -1 ) |
|
{ |
|
pStoredFace->m_nIndices = (pFace->m_nVerts - 2) * 3; |
|
|
|
pBuf->m_nIndices += (pFace->m_nVerts - 2) * 3; |
|
pBuf->m_nVerts += pFace->m_nVerts; |
|
} |
|
else |
|
{ |
|
ddispinfo_t *pDisp = &file.g_dispinfo[pFace->m_iDispInfo]; |
|
|
|
int nTris = Square( 1 << pDisp->power ) * 2; |
|
int nVerts = Square( (1 << pDisp->power) + 1 ); |
|
|
|
pStoredFace->m_nIndices = nTris * 3; |
|
|
|
pBuf->m_nIndices += nTris * 3; |
|
pBuf->m_nVerts += nVerts; |
|
} |
|
|
|
pBuf->m_Faces.AddToTail( pStoredFace ); |
|
|
|
// Don't make the buffers too big.. |
|
if( pBuf->m_nIndices > (16*1024) || pBuf->m_nVerts > (16*1024) ) |
|
{ |
|
pBuf = new CMaterialBuf; |
|
pMat->m_MaterialBufs.AddToTail( pBuf ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Determines the appropriate vertex format for LMGroup meshes |
|
//----------------------------------------------------------------------------- |
|
VertexFormat_t CBSPLighting::ComputeLMGroupVertexFormat( IMaterial * pMaterial ) |
|
{ |
|
VertexFormat_t vertexFormat = pMaterial->GetVertexFormat(); |
|
|
|
// FIXME: set VERTEX_FORMAT_COMPRESSED if there are no artifacts and if it saves enough memory (use 'mem_dumpvballocs') |
|
vertexFormat &= ~VERTEX_FORMAT_COMPRESSED; |
|
// FIXME: check for and strip unused vertex elements (bone weights+indices, TANGENT_S/T?) - requires reliable material vertex formats first |
|
|
|
return vertexFormat; |
|
} |
|
|
|
void CBSPLighting::BuildLMGroups( |
|
CBSPInfo &file, |
|
CUtlVector<CFace> &faces, |
|
CUtlVector<CVert> &verts, |
|
CUtlVector<CDispInfoFaces> &dispInfos |
|
) |
|
{ |
|
// Count everything in each CFaceMaterial. |
|
AssignFaceMaterialCounts( file, faces ); |
|
|
|
|
|
IMaterialSystem *pMatSys = MaterialSystemInterface(); |
|
if( !pMatSys ) |
|
return; |
|
|
|
CMatRenderContextPtr pRenderContext( pMatSys ); |
|
|
|
// Now create the static buffers. |
|
FOR_EACH_LL( m_FaceMaterials, iMat ) |
|
{ |
|
CFaceMaterial *pMat = m_FaceMaterials[iMat]; |
|
|
|
FOR_EACH_LL( pMat->m_MaterialBufs, iBuf ) |
|
{ |
|
CMaterialBuf *pBuf = pMat->m_MaterialBufs[iBuf]; |
|
|
|
VertexFormat_t vertexFormat = ComputeLMGroupVertexFormat( pMat->m_pMaterial ); |
|
pBuf->m_pMesh = pRenderContext->CreateStaticMesh( vertexFormat, "terd", pMat->m_pMaterial ); |
|
if( !pBuf->m_pMesh ) |
|
continue; |
|
|
|
bool bNeedsBumpmap = pMat->m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ); |
|
|
|
CMeshBuilder mb; |
|
mb.Begin( pBuf->m_pMesh, MATERIAL_TRIANGLES, pBuf->m_nVerts, pBuf->m_nIndices ); |
|
|
|
// Write all the faces in. |
|
int iCurBaseVert = 0; |
|
|
|
FOR_EACH_LL( pBuf->m_Faces, iFace ) |
|
{ |
|
CStoredFace *pStoredFace = pBuf->m_Faces[iFace]; |
|
CFace *pFace = pStoredFace->m_pFace; |
|
|
|
if( pFace->m_iDispInfo == -1 ) |
|
{ |
|
// It's a regular face. |
|
CVert *pVerts = &verts[pFace->m_iVertStart]; |
|
|
|
for( int iVert=0; iVert < pFace->m_nVerts; iVert++ ) |
|
{ |
|
mb.Position3fv( (float*)&pVerts[iVert].m_vPos ); |
|
|
|
mb.TexCoord2fv( 0, pVerts[iVert].m_vTexCoords.Base() ); |
|
mb.TexCoord2fv( 1, pVerts[iVert].m_vLightCoords.Base() ); |
|
if( bNeedsBumpmap ) |
|
mb.TexCoord2f ( 2, pStoredFace->m_BumpSTexCoordOffset, 0 ); |
|
|
|
mb.Color3f( 1,1,1 ); |
|
mb.AdvanceVertex(); |
|
} |
|
|
|
// Write the indices. |
|
for( int iTri=0; iTri < pFace->m_nVerts-2; iTri++ ) |
|
{ |
|
mb.Index( iCurBaseVert ); mb.AdvanceIndex(); |
|
mb.Index( iCurBaseVert+iTri+1 ); mb.AdvanceIndex(); |
|
mb.Index( iCurBaseVert+iTri+2 ); mb.AdvanceIndex(); |
|
} |
|
|
|
iCurBaseVert += pFace->m_nVerts; |
|
} |
|
else |
|
{ |
|
// It's a displacement. |
|
CDispInfoFaces *pDisp = &dispInfos[pFace->m_iDispInfo]; |
|
|
|
// Generate the index list. |
|
unsigned short indices[ (1<<MAX_MAP_DISP_POWER) * (1<<MAX_MAP_DISP_POWER) * 6 ]; |
|
|
|
int nRequired = DispCommon_GetNumTriIndices( pDisp->m_Power ); |
|
Assert( nRequired <= sizeof(indices)/sizeof(indices[0]) ); |
|
|
|
DispCommon_GenerateTriIndices( pDisp->m_Power, indices ); |
|
|
|
for( int iIndex=0; iIndex < nRequired; iIndex++ ) |
|
{ |
|
mb.Index( indices[iIndex] + iCurBaseVert ); |
|
mb.AdvanceIndex(); |
|
} |
|
|
|
// Generate the vert list. |
|
for( int iVert=0; iVert < pDisp->m_Verts.Count(); iVert++ ) |
|
{ |
|
mb.Position3fv( (float*)&pDisp->m_Verts[iVert].m_vPos ); |
|
mb.TexCoord2fv( 0, (float*)&pDisp->m_Verts[iVert].m_vTexCoords ); |
|
mb.TexCoord2fv( 1, (float*)&pDisp->m_Verts[iVert].m_vLightCoords ); |
|
|
|
if( bNeedsBumpmap ) |
|
mb.TexCoord2f ( 2, pStoredFace->m_BumpSTexCoordOffset, 0 ); |
|
|
|
mb.AdvanceVertex(); |
|
} |
|
|
|
iCurBaseVert += pDisp->m_Verts.Count();; |
|
} |
|
} |
|
|
|
mb.End(); |
|
} |
|
} |
|
} |
|
|
|
|
|
bool FindDrawCommand( CUtlVector<CBSPLighting::CDrawCommand*> &drawCommands, int lmPageID, int &index ) |
|
{ |
|
for( int i=0; i < drawCommands.Count(); i++ ) |
|
{ |
|
if( drawCommands[i]->m_LightmapPageID == lmPageID ) |
|
{ |
|
index = i; |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
void CBSPLighting::BuildDrawCommands() |
|
{ |
|
FOR_EACH_LL( m_FaceMaterials, iMat ) |
|
{ |
|
CFaceMaterial *pMat = m_FaceMaterials[iMat]; |
|
|
|
FOR_EACH_LL( pMat->m_MaterialBufs, iBuf ) |
|
{ |
|
CMaterialBuf *pBuf = pMat->m_MaterialBufs[iBuf]; |
|
|
|
// Group by lightmap page IDs. |
|
FOR_EACH_LL( pBuf->m_Faces, iFace ) |
|
{ |
|
CStoredFace *pFace = pBuf->m_Faces[iFace]; |
|
|
|
int index; |
|
if( !FindDrawCommand( pBuf->m_DrawCommands, pFace->m_LightmapPageID, index ) ) |
|
{ |
|
index = pBuf->m_DrawCommands.AddToTail( new CDrawCommand ); |
|
pBuf->m_DrawCommands[index]->m_LightmapPageID = pFace->m_LightmapPageID; |
|
} |
|
|
|
CPrimList primList; |
|
primList.m_FirstIndex = pFace->m_iFirstIndex; |
|
primList.m_NumIndices = pFace->m_nIndices; |
|
pBuf->m_DrawCommands[index]->m_PrimLists.AddToTail( primList ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
void CBSPLighting::ReloadLightmaps() |
|
{ |
|
if( !m_pVRadDLL ) |
|
return; |
|
|
|
IMaterialSystem *pMatSys = MaterialSystemInterface(); |
|
if( !pMatSys ) |
|
return; |
|
|
|
CBSPInfo bspInfo; |
|
m_pVRadDLL->GetBSPInfo( &bspInfo ); |
|
|
|
if( !bspInfo.lightdatasize ) |
|
return; |
|
|
|
Vector4D blocklights[4][MAX_LIGHTMAP_DIM_INCLUDING_BORDER * MAX_LIGHTMAP_DIM_INCLUDING_BORDER]; |
|
|
|
for( int iFace=0; iFace < m_StoredFaces.Count(); iFace++ ) |
|
{ |
|
CStoredFace *pFace = &m_StoredFaces[iFace]; |
|
|
|
// Avoid updating lightmaps in faces that weren't touched. |
|
if( bspInfo.m_pFacesTouched && !bspInfo.m_pFacesTouched[pFace->m_iMapFace] ) |
|
continue; |
|
|
|
dface_t *pIn = &bspInfo.dfaces[ pFace->m_iMapFace ]; |
|
int nLuxels = pFace->m_LightmapSize[0] * pFace->m_LightmapSize[1]; |
|
|
|
bool bNeedsBumpmap = pFace->m_pMaterial->m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ); |
|
|
|
texinfo_t *pTexInfo = &bspInfo.texinfo[ bspInfo.dfaces[pFace->m_iMapFace].texinfo ]; |
|
bool bHasBumpmap = SurfHasBumpedLightmaps( pTexInfo->flags ); |
|
|
|
int nLightmaps = 1; |
|
if( bNeedsBumpmap && bHasBumpmap ) |
|
nLightmaps = 4; |
|
|
|
ColorRGBExp32 *pLightmap = (ColorRGBExp32 *)&bspInfo.dlightdata[pIn->lightofs]; |
|
int iLightmap; |
|
for( iLightmap=0; iLightmap < nLightmaps; iLightmap++ ) |
|
{ |
|
for( int iLuxel=0; iLuxel < nLuxels; iLuxel++ ) |
|
{ |
|
blocklights[iLightmap][iLuxel][0] = TexLightToLinear( pLightmap->r, pLightmap->exponent ); |
|
blocklights[iLightmap][iLuxel][1] = TexLightToLinear( pLightmap->g, pLightmap->exponent ); |
|
blocklights[iLightmap][iLuxel][2] = TexLightToLinear( pLightmap->b, pLightmap->exponent ); |
|
blocklights[iLightmap][iLuxel][3] = 1; |
|
++pLightmap; |
|
} |
|
} |
|
|
|
// If it needs bumpmaps but doesn't have them in the file, then just copy |
|
// the lightmap data into the other lightmaps like the engine does. |
|
if( bNeedsBumpmap && !bHasBumpmap ) |
|
{ |
|
for( iLightmap=1; iLightmap < 4; iLightmap++ ) |
|
{ |
|
memcpy( blocklights[iLightmap], blocklights[0], nLuxels * sizeof( blocklights[0][0] ) ); |
|
} |
|
} |
|
|
|
if( bNeedsBumpmap ) |
|
{ |
|
pMatSys->UpdateLightmap( |
|
pFace->m_LightmapPageID, |
|
pFace->m_LightmapSize, |
|
pFace->m_OffsetIntoLightmapPage, |
|
(float*)blocklights[0], (float*)blocklights[1], (float*)blocklights[2], (float*)blocklights[3] ); |
|
} |
|
else |
|
{ |
|
pMatSys->UpdateLightmap( |
|
pFace->m_LightmapPageID, |
|
pFace->m_LightmapSize, |
|
pFace->m_OffsetIntoLightmapPage, |
|
(float*)blocklights[0], NULL, NULL, NULL ); |
|
} |
|
} |
|
} |
|
|
|
|
|
bool CBSPLighting::LoadVRADDLL( char const *pFilename ) |
|
{ |
|
// Load VRAD's DLL. |
|
m_hVRadDLL = Sys_LoadModule( "vrad_dll.dll" ); |
|
if( !m_hVRadDLL ) |
|
return false; |
|
|
|
CreateInterfaceFn fn = Sys_GetFactory( m_hVRadDLL ); |
|
if( !fn ) |
|
return false; |
|
|
|
int retCode = 0; |
|
m_pVRadDLL = (IVRadDLL*)fn( VRAD_INTERFACE_VERSION, &retCode ); |
|
if( !m_pVRadDLL ) |
|
return false; |
|
|
|
// Tell VRAD to load the BSP file. |
|
if( !m_pVRadDLL->Init( pFilename ) ) |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
|
|
void CBSPLighting::CreateDisplacements( CBSPInfo &file, CUtlVector<CFace> &faces, CUtlVector<CDispInfoFaces> &dispInfos ) |
|
{ |
|
/* |
|
IMaterialSystem *pMatSys = MaterialSystemInterface(); |
|
|
|
dispInfos.SetSize( file.g_numdispinfo ); |
|
for( int iFace=0; iFace < faces.Size(); iFace++ ) |
|
{ |
|
CFace *pFace = &faces[iFace]; |
|
CStoredFace *pStoredFace = &m_StoredFaces[iFace]; |
|
dface_t *pInFace = pFace->m_pDFace; |
|
|
|
if( pInFace->dispinfo == -1 ) |
|
continue; |
|
|
|
ddispinfo_t *pInDisp = &file.g_dispinfo[pInFace->dispinfo]; |
|
CDispInfoFaces *pOutDisp = &dispInfos[pInFace->dispinfo]; |
|
|
|
pOutDisp->m_Power = pInDisp->power; |
|
int nVertsPerSide = (1 << pInDisp->power) + 1; |
|
|
|
pOutDisp->m_Verts.SetSize( pInDisp->m_LODs[0].m_nVerts ); |
|
|
|
int lightmapPageSize[2]; |
|
pMatSys->GetLightmapPageSize( pFace->m_pStoredFace->m_LightmapPageID, &lightmapPageSize[0], &lightmapPageSize[1] ); |
|
|
|
for( int iVert=0; iVert < pInDisp->m_LODs[0].m_nVerts; iVert++ ) |
|
{ |
|
ddisp_lod_vert_t *pInVert = &file.ddispverts[ pInDisp->m_LODs[0].m_iVertStart + iVert ]; |
|
CVert *pOutVert = &pOutDisp->m_Verts[iVert]; |
|
|
|
pOutVert->m_vPos = pInVert->m_vPos; |
|
for( int iCoord=0; iCoord < 2; iCoord++ ) |
|
{ |
|
float flVal = pInVert->m_LightCoords[iCoord]; |
|
|
|
flVal += pFace->m_pStoredFace->m_OffsetIntoLightmapPage[iCoord]; |
|
flVal += 0.5f; |
|
flVal /= lightmapPageSize[iCoord]; |
|
Assert( _finite(flVal) ); |
|
pOutVert->m_vLightCoords[iCoord] = flVal; |
|
|
|
pOutVert->m_vTexCoords[iCoord] = pInVert->m_TexCoords[iCoord]; |
|
|
|
if( iCoord == 0 ) |
|
pOutVert->m_vTexCoords[iCoord] /= pStoredFace->m_pMaterial->m_pMaterial->GetMappingWidth(); |
|
else |
|
pOutVert->m_vTexCoords[iCoord] /= pStoredFace->m_pMaterial->m_pMaterial->GetMappingHeight(); |
|
} |
|
} |
|
} |
|
*/ |
|
} |
|
|
|
|
|
void CBSPLighting::InitMaterialLUT( CBSPInfo &file ) |
|
{ |
|
m_StringTableIDToMaterial.SetSize( file.nTexDataStringTable ); |
|
for( int i=0; i < m_StringTableIDToMaterial.Count(); i++ ) |
|
m_StringTableIDToMaterial[i] = 0; |
|
} |
|
|
|
|
|
CBSPLighting::CFaceMaterial* CBSPLighting::FindOrAddMaterial( CBSPInfo &file, int stringTableID ) |
|
{ |
|
if( stringTableID >= m_StringTableIDToMaterial.Count() ) |
|
{ |
|
Assert( false ); |
|
return 0; |
|
} |
|
|
|
if( m_StringTableIDToMaterial[stringTableID] ) |
|
{ |
|
return m_StringTableIDToMaterial[stringTableID]; |
|
} |
|
else |
|
{ |
|
IMaterial *pMaterial = 0; |
|
char *pMaterialName = &file.texDataStringData[ file.texDataStringTable[ stringTableID ] ]; |
|
if( pMaterialName ) |
|
pMaterial = MaterialSystemInterface()->FindMaterial( pMaterialName, TEXTURE_GROUP_OTHER ); |
|
|
|
// Don't add CFaceMaterials without a material. |
|
if( !pMaterial ) |
|
return 0; |
|
|
|
// This is lovely. We have to call this stuff to get it to precalculate the data it needs. |
|
pMaterial->GetMappingHeight(); |
|
pMaterial->RecomputeStateSnapshots(); |
|
|
|
CFaceMaterial *pMat = new CFaceMaterial; |
|
if( pMaterial->IsTranslucent() ) |
|
m_FaceMaterials.AddToTail( pMat ); |
|
else |
|
m_FaceMaterials.AddToHead( pMat ); |
|
|
|
pMat->m_pMaterial = pMaterial; |
|
|
|
m_StringTableIDToMaterial[stringTableID] = pMat; |
|
return pMat; |
|
} |
|
} |
|
|
|
|
|
IBSPLighting* CreateBSPLighting() |
|
{ |
|
return new CBSPLighting; |
|
}
|
|
|