source-engine/utils/vrad/vismat.cpp

484 lines
11 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//
//=============================================================================//
#include "vrad.h"
#include "vmpi.h"
#ifdef MPI
#include "messbuf.h"
static MessageBuffer mb;
#endif
#define HALFBIT
extern char source[MAX_PATH];
extern char vismatfile[_MAX_PATH];
extern char incrementfile[_MAX_PATH];
extern qboolean incremental;
/*
===================================================================
VISIBILITY MATRIX
Determine which patches can see each other
Use the PVS to accelerate if available
===================================================================
*/
#define TEST_EPSILON 0.1
#define PLANE_TEST_EPSILON 0.01 // patch must be this much in front of the plane to be considered "in front"
#define PATCH_FACE_OFFSET 0.1 // push patch origins off from the face by this amount to avoid self collisions
#define STREAM_SIZE 512
class CTransferMaker
{
public:
CTransferMaker( transfer_t *all_transfers );
~CTransferMaker();
FORCEINLINE void TestMakeTransfer( Vector start, Vector stop, int ndxShooter, int ndxReciever )
{
g_RtEnv.AddToRayStream( m_RayStream, start, stop, &m_pResults[m_nTests] );
m_pShooterPatches[m_nTests] = ndxShooter;
m_pRecieverPatches[m_nTests] = ndxReciever;
++m_nTests;
}
void Finish();
private:
int m_nTests;
RayTracingSingleResult *m_pResults;
int *m_pShooterPatches;
int *m_pRecieverPatches;
RayStream m_RayStream;
transfer_t *m_AllTransfers;
};
CTransferMaker::CTransferMaker( transfer_t *all_transfers ) :
m_AllTransfers( all_transfers ), m_nTests( 0 )
{
m_pResults = (RayTracingSingleResult *)calloc( 1, MAX_PATCHES * sizeof ( RayTracingSingleResult ) );
m_pShooterPatches = (int *)calloc( 1, MAX_PATCHES * sizeof( int ) );
m_pRecieverPatches = (int *)calloc( 1, MAX_PATCHES * sizeof( int ) );
}
CTransferMaker::~CTransferMaker()
{
free ( m_pResults );
free ( m_pShooterPatches );
free (m_pRecieverPatches );
}
void CTransferMaker::Finish()
{
g_RtEnv.FinishRayStream( m_RayStream );
for ( int i = 0; i < m_nTests; ++i )
{
if ( m_pResults[i].HitID == -1 || m_pResults[i].HitDistance >= m_pResults[i].ray_length )
{
MakeTransfer( m_pShooterPatches[i], m_pRecieverPatches[i], m_AllTransfers );
}
}
m_nTests = 0;
}
dleaf_t* PointInLeaf (int iNode, Vector const& point)
{
if ( iNode < 0 )
return &dleafs[ (-1-iNode) ];
dnode_t *node = &dnodes[iNode];
dplane_t *plane = &dplanes[ node->planenum ];
float dist = DotProduct (point, plane->normal) - plane->dist;
if ( dist > TEST_EPSILON )
{
return PointInLeaf( node->children[0], point );
}
else if ( dist < -TEST_EPSILON )
{
return PointInLeaf( node->children[1], point );
}
else
{
dleaf_t *pTest = PointInLeaf( node->children[0], point );
if ( pTest->cluster != -1 )
return pTest;
return PointInLeaf( node->children[1], point );
}
}
int ClusterFromPoint( Vector const& point )
{
dleaf_t *leaf = PointInLeaf( 0, point );
return leaf->cluster;
}
void PvsForOrigin (Vector& org, byte *pvs)
{
int visofs;
int cluster;
if (!visdatasize)
{
memset (pvs, 255, (dvis->numclusters+7)/8 );
return;
}
cluster = ClusterFromPoint( org );
if ( cluster < 0 )
{
visofs = -1;
}
else
{
visofs = dvis->bitofs[ cluster ][DVIS_PVS];
}
if (visofs == -1)
Error ("visofs == -1");
DecompressVis (&dvisdata[visofs], pvs);
}
void TestPatchToPatch( int ndxPatch1, int ndxPatch2, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
{
Vector tmp;
//
// get patches
//
if( ndxPatch1 == g_Patches.InvalidIndex() || ndxPatch2 == g_Patches.InvalidIndex() )
return;
CPatch *patch = &g_Patches.Element( ndxPatch1 );
CPatch *patch2 = &g_Patches.Element( ndxPatch2 );
if (patch2->child1 != g_Patches.InvalidIndex() )
{
// check to see if we should use a child node instead
VectorSubtract( patch->origin, patch2->origin, tmp );
// SQRT( 1/4 )
// FIXME: should be based on form-factor (ie. include visible angle, etc)
if ( DotProduct(tmp, tmp) * 0.0625 < patch2->area )
{
TestPatchToPatch( ndxPatch1, patch2->child1, head, transfers, transferMaker, iThread );
TestPatchToPatch( ndxPatch1, patch2->child2, head, transfers, transferMaker, iThread );
return;
}
}
// check vis between patch and patch2
// if bit has not already been set
// && v2 is not behind light plane
// && v2 is visible from v1
if ( DotProduct( patch2->origin, patch->normal ) > patch->planeDist + PLANE_TEST_EPSILON )
{
// push out origins from face so that don't intersect their owners
Vector p1, p2;
VectorAdd( patch->origin, patch->normal, p1 );
VectorAdd( patch2->origin, patch2->normal, p2 );
transferMaker.TestMakeTransfer( p1, p2, ndxPatch1, ndxPatch2 );
}
}
/*
==============
TestPatchToFace
Sets vis bits for all patches in the face
==============
*/
void TestPatchToFace (unsigned patchnum, int facenum, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
{
if( faceParents.Element( facenum ) == g_Patches.InvalidIndex() || patchnum == g_Patches.InvalidIndex() )
return;
CPatch *patch = &g_Patches.Element( patchnum );
CPatch *patch2 = &g_Patches.Element( faceParents.Element( facenum ) );
// if emitter is behind that face plane, skip all patches
CPatch *pNextPatch;
if ( patch2 && DotProduct(patch->origin, patch2->normal) > patch2->planeDist + PLANE_TEST_EPSILON )
{
// we need to do a real test
for( ; patch2; patch2 = pNextPatch )
{
// next patch
pNextPatch = NULL;
if( patch2->ndxNextParent != g_Patches.InvalidIndex() )
{
pNextPatch = &g_Patches.Element( patch2->ndxNextParent );
}
/*
// skip patches too far away
VectorSubtract( patch->origin, patch2->origin, tmp );
if (DotProduct( tmp, tmp ) > 512 * 512)
continue;
*/
int ndxPatch2 = patch2 - g_Patches.Base();
TestPatchToPatch( patchnum, ndxPatch2, head, transfers, transferMaker, iThread );
}
}
}
struct ClusterDispList_t
{
CUtlVector<int> dispFaces;
};
static CUtlVector<ClusterDispList_t> g_ClusterDispFaces;
//-----------------------------------------------------------------------------
// Helps us find all displacements associated with a particular cluster
//-----------------------------------------------------------------------------
void AddDispsToClusterTable( void )
{
g_ClusterDispFaces.SetCount( g_ClusterLeaves.Count() );
//
// add displacement faces to the cluster table
//
for( int ndxFace = 0; ndxFace < numfaces; ndxFace++ )
{
// search for displacement faces
if( g_pFaces[ndxFace].dispinfo == -1 )
continue;
//
// get the clusters associated with the face
//
if( g_FacePatches.Element( ndxFace ) != g_FacePatches.InvalidIndex() )
{
CPatch *pNextPatch = NULL;
for( CPatch *pPatch = &g_Patches.Element( g_FacePatches.Element( ndxFace ) ); pPatch; pPatch = pNextPatch )
{
// next patch
pNextPatch = NULL;
if( pPatch->ndxNext != g_Patches.InvalidIndex() )
{
pNextPatch = &g_Patches.Element( pPatch->ndxNext );
}
if( pPatch->clusterNumber != g_Patches.InvalidIndex() )
{
int ndxDisp = g_ClusterDispFaces[pPatch->clusterNumber].dispFaces.Find( ndxFace );
if( ndxDisp == -1 )
{
ndxDisp = g_ClusterDispFaces[pPatch->clusterNumber].dispFaces.AddToTail();
g_ClusterDispFaces[pPatch->clusterNumber].dispFaces[ndxDisp] = ndxFace;
}
}
}
}
}
}
/*
==============
BuildVisRow
Calc vis bits from a single patch
==============
*/
void BuildVisRow (int patchnum, byte *pvs, int head, transfer_t *transfers, CTransferMaker &transferMaker, int iThread )
{
int j, k, l, leafIndex;
CPatch *patch;
dleaf_t *leaf;
byte face_tested[MAX_MAP_FACES];
byte disp_tested[MAX_MAP_FACES];
patch = &g_Patches.Element( patchnum );
memset( face_tested, 0, numfaces ) ;
memset( disp_tested, 0, numfaces );
for (j=0; j<dvis->numclusters; j++)
{
if ( ! ( pvs[(j)>>3] & (1<<((j)&7)) ) )
{
continue; // not in pvs
}
for ( leafIndex = 0; leafIndex < g_ClusterLeaves[j].leafCount; leafIndex++ )
{
leaf = dleafs + g_ClusterLeaves[j].leafs[leafIndex];
for (k=0 ; k<leaf->numleaffaces; k++)
{
l = dleaffaces[leaf->firstleafface + k];
// faces can be marksurfed by multiple leaves, but
// don't bother testing again
if (face_tested[l])
{
continue;
}
face_tested[l] = 1;
// don't check patches on the same face
if (patch->faceNumber == l)
continue;
TestPatchToFace (patchnum, l, head, transfers, transferMaker, iThread );
}
}
int dispCount = g_ClusterDispFaces[j].dispFaces.Size();
for( int ndxDisp = 0; ndxDisp < dispCount; ndxDisp++ )
{
int ndxFace = g_ClusterDispFaces[j].dispFaces[ndxDisp];
if( disp_tested[ndxFace] )
continue;
disp_tested[ndxFace] = 1;
// don't check patches on the same face
if( patch->faceNumber == ndxFace )
continue;
TestPatchToFace( patchnum, ndxFace, head, transfers, transferMaker, iThread );
}
}
// Msg("%d) Transfers: %5d\n", patchnum, patch->numtransfers);
}
/*
===========
BuildVisLeafs
This is run by multiple threads
===========
*/
transfer_t* BuildVisLeafs_Start()
{
return (transfer_t *)calloc( 1, MAX_PATCHES * sizeof( transfer_t ) );
}
// If PatchCB is non-null, it is called after each row is generated (used by MPI).
void BuildVisLeafs_Cluster(
int threadnum,
transfer_t *transfers,
int iCluster,
void (*PatchCB)(int iThread, int patchnum, CPatch *patch)
)
{
byte pvs[(MAX_MAP_CLUSTERS+7)/8];
CPatch *patch;
int head;
unsigned patchnum;
DecompressVis( &dvisdata[ dvis->bitofs[ iCluster ][DVIS_PVS] ], pvs);
head = 0;
CTransferMaker transferMaker( transfers );
// light every patch in the cluster
if( clusterChildren.Element( iCluster ) != clusterChildren.InvalidIndex() )
{
CPatch *pNextPatch;
for( patch = &g_Patches.Element( clusterChildren.Element( iCluster ) ); patch; patch = pNextPatch )
{
//
// next patch
//
pNextPatch = NULL;
if( patch->ndxNextClusterChild != g_Patches.InvalidIndex() )
{
pNextPatch = &g_Patches.Element( patch->ndxNextClusterChild );
}
patchnum = patch - g_Patches.Base();
// build to all other world clusters
BuildVisRow (patchnum, pvs, head, transfers, transferMaker, threadnum );
transferMaker.Finish();
// do the transfers
MakeScales( patchnum, transfers );
// Let MPI aggregate the data if it's being used.
if ( PatchCB )
PatchCB( threadnum, patchnum, patch );
}
}
}
void BuildVisLeafs_End( transfer_t *transfers )
{
free( transfers );
}
void BuildVisLeafs( int threadnum, void *pUserData )
{
transfer_t *transfers = BuildVisLeafs_Start();
while ( 1 )
{
//
// build a minimal BSP tree that only
// covers areas relevent to the PVS
//
// JAY: Now this returns a cluster index
int iCluster = GetThreadWork();
if ( iCluster == -1 )
break;
BuildVisLeafs_Cluster( threadnum, transfers, iCluster, NULL );
}
BuildVisLeafs_End( transfers );
}
/*
==============
BuildVisMatrix
==============
*/
void BuildVisMatrix (void)
{
if ( g_bUseMPI )
{
RunMPIBuildVisLeafs();
}
else
{
RunThreadsOn (dvis->numclusters, true, BuildVisLeafs);
}
}
void FreeVisMatrix (void)
{
}