summaryrefslogtreecommitdiff
path: root/hammer/bsplighting.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/bsplighting.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'hammer/bsplighting.cpp')
-rw-r--r--hammer/bsplighting.cpp971
1 files changed, 971 insertions, 0 deletions
diff --git a/hammer/bsplighting.cpp b/hammer/bsplighting.cpp
new file mode 100644
index 0000000..73597dc
--- /dev/null
+++ b/hammer/bsplighting.cpp
@@ -0,0 +1,971 @@
+//========= 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;
+}