Files
HL2Overcharged/utils/vrad/vismat.cpp
2025-05-21 21:20:08 +03:00

484 lines
11 KiB
C++

//========= 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)
{
}