//========= 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 dispFaces; }; static CUtlVector 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; jnumclusters; 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; knumleaffaces; 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) { }