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.
483 lines
11 KiB
483 lines
11 KiB
//========= 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) |
|
{ |
|
|
|
}
|
|
|