aboutsummaryrefslogtreecommitdiff
path: root/mp/src/utils/vvis/vvis.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mp/src/utils/vvis/vvis.cpp')
-rw-r--r--mp/src/utils/vvis/vvis.cpp2480
1 files changed, 1240 insertions, 1240 deletions
diff --git a/mp/src/utils/vvis/vvis.cpp b/mp/src/utils/vvis/vvis.cpp
index 577c1cc3..0fb61388 100644
--- a/mp/src/utils/vvis/vvis.cpp
+++ b/mp/src/utils/vvis/vvis.cpp
@@ -1,1240 +1,1240 @@
-//========= Copyright Valve Corporation, All rights reserved. ============//
-//
-// Purpose:
-//
-// $NoKeywords: $
-//
-//=============================================================================//
-// vis.c
-
-#include <windows.h>
-#include "vis.h"
-#include "threads.h"
-#include "stdlib.h"
-#include "pacifier.h"
-#include "vmpi.h"
-#include "mpivis.h"
-#include "tier1/strtools.h"
-#include "collisionutils.h"
-#include "tier0/icommandline.h"
-#include "vmpi_tools_shared.h"
-#include "ilaunchabledll.h"
-#include "tools_minidump.h"
-#include "loadcmdline.h"
-#include "byteswap.h"
-
-
-int g_numportals;
-int portalclusters;
-
-char inbase[32];
-
-portal_t *portals;
-leaf_t *leafs;
-
-int c_portaltest, c_portalpass, c_portalcheck;
-
-byte *uncompressedvis;
-
-byte *vismap, *vismap_p, *vismap_end; // past visfile
-int originalvismapsize;
-
-int leafbytes; // (portalclusters+63)>>3
-int leaflongs;
-
-int portalbytes, portallongs;
-
-bool fastvis;
-bool nosort;
-
-int totalvis;
-
-portal_t *sorted_portals[MAX_MAP_PORTALS*2];
-
-bool g_bUseRadius = false;
-double g_VisRadius = 4096.0f * 4096.0f;
-
-bool g_bLowPriority = false;
-
-//=============================================================================
-
-void PlaneFromWinding (winding_t *w, plane_t *plane)
-{
- Vector v1, v2;
-
-// calc plane
- VectorSubtract (w->points[2], w->points[1], v1);
- VectorSubtract (w->points[0], w->points[1], v2);
- CrossProduct (v2, v1, plane->normal);
- VectorNormalize (plane->normal);
- plane->dist = DotProduct (w->points[0], plane->normal);
-}
-
-
-/*
-==================
-NewWinding
-==================
-*/
-winding_t *NewWinding (int points)
-{
- winding_t *w;
- int size;
-
- if (points > MAX_POINTS_ON_WINDING)
- Error ("NewWinding: %i points, max %d", points, MAX_POINTS_ON_WINDING);
-
- size = (int)(&((winding_t *)0)->points[points]);
- w = (winding_t*)malloc (size);
- memset (w, 0, size);
-
- return w;
-}
-
-void pw(winding_t *w)
-{
- int i;
- for (i=0 ; i<w->numpoints ; i++)
- Msg ("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]);
-}
-
-void prl(leaf_t *l)
-{
- int i;
- portal_t *p;
- plane_t pl;
-
- int count = l->portals.Count();
- for (i=0 ; i<count ; i++)
- {
- p = l->portals[i];
- pl = p->plane;
- Msg ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
- }
-}
-
-
-//=============================================================================
-
-/*
-=============
-SortPortals
-
-Sorts the portals from the least complex, so the later ones can reuse
-the earlier information.
-=============
-*/
-int PComp (const void *a, const void *b)
-{
- if ( (*(portal_t **)a)->nummightsee == (*(portal_t **)b)->nummightsee)
- return 0;
- if ( (*(portal_t **)a)->nummightsee < (*(portal_t **)b)->nummightsee)
- return -1;
-
- return 1;
-}
-
-void BuildTracePortals( int clusterStart )
-{
- leaf_t *leaf = &leafs[g_TraceClusterStart];
- g_numportals = leaf->portals.Count();
- for ( int i = 0; i < g_numportals; i++ )
- {
- sorted_portals[i] = leaf->portals[i];
- }
-}
-
-void SortPortals (void)
-{
- int i;
-
- for (i=0 ; i<g_numportals*2 ; i++)
- sorted_portals[i] = &portals[i];
-
- if (nosort)
- return;
- qsort (sorted_portals, g_numportals*2, sizeof(sorted_portals[0]), PComp);
-}
-
-
-/*
-==============
-LeafVectorFromPortalVector
-==============
-*/
-int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
-{
- int i;
- portal_t *p;
- int c_leafs;
-
-
- memset (leafbits, 0, leafbytes);
-
- for (i=0 ; i<g_numportals*2 ; i++)
- {
- if ( CheckBit( portalbits, i ) )
- {
- p = portals+i;
- SetBit( leafbits, p->leaf );
- }
- }
-
- c_leafs = CountBits (leafbits, portalclusters);
-
- return c_leafs;
-}
-
-
-/*
-===============
-ClusterMerge
-
-Merges the portal visibility for a leaf
-===============
-*/
-void ClusterMerge (int clusternum)
-{
- leaf_t *leaf;
-// byte portalvector[MAX_PORTALS/8];
- byte portalvector[MAX_PORTALS/4]; // 4 because portal bytes is * 2
- byte uncompressed[MAX_MAP_LEAFS/8];
- int i, j;
- int numvis;
- portal_t *p;
- int pnum;
-
- // OR together all the portalvis bits
-
- memset (portalvector, 0, portalbytes);
- leaf = &leafs[clusternum];
- for (i=0 ; i < leaf->portals.Count(); i++)
- {
- p = leaf->portals[i];
- if (p->status != stat_done)
- Error ("portal not done %d %p %p\n", i, p, portals);
- for (j=0 ; j<portallongs ; j++)
- ((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
- pnum = p - portals;
- SetBit( portalvector, pnum );
- }
-
- // convert portal bits to leaf bits
- numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
-
-#if 0
- // func_viscluster makes this happen all the time because it allows a non-convex set of portals
- // My analysis says this is ok, but it does make this check for errors in vis kind of useless
- if ( CheckBit( uncompressed, clusternum ) )
- Warning("WARNING: Cluster portals saw into cluster\n");
-#endif
-
- SetBit( uncompressed, clusternum );
- numvis++; // count the leaf itself
-
- // save uncompressed for PHS calculation
- memcpy (uncompressedvis + clusternum*leafbytes, uncompressed, leafbytes);
-
- qprintf ("cluster %4i : %4i visible\n", clusternum, numvis);
- totalvis += numvis;
-}
-
-static int CompressAndCrosscheckClusterVis( int clusternum )
-{
- int optimized = 0;
- byte compressed[MAX_MAP_LEAFS/8];
-//
-// compress the bit string
-//
- byte *uncompressed = uncompressedvis + clusternum*leafbytes;
- for ( int i = 0; i < portalclusters; i++ )
- {
- if ( i == clusternum )
- continue;
-
- if ( CheckBit( uncompressed, i ) )
- {
- byte *other = uncompressedvis + i*leafbytes;
- if ( !CheckBit( other, clusternum ) )
- {
- ClearBit( uncompressed, i );
- optimized++;
- }
- }
- }
- int numbytes = CompressVis( uncompressed, compressed );
-
- byte *dest = vismap_p;
- vismap_p += numbytes;
-
- if (vismap_p > vismap_end)
- Error ("Vismap expansion overflow");
-
- dvis->bitofs[clusternum][DVIS_PVS] = dest-vismap;
-
- memcpy( dest, compressed, numbytes );
-
- // check vis data
- DecompressVis( vismap + dvis->bitofs[clusternum][DVIS_PVS], compressed );
-
- return optimized;
-}
-
-
-/*
-==================
-CalcPortalVis
-==================
-*/
-void CalcPortalVis (void)
-{
- int i;
-
- // fastvis just uses mightsee for a very loose bound
- if( fastvis )
- {
- for (i=0 ; i<g_numportals*2 ; i++)
- {
- portals[i].portalvis = portals[i].portalflood;
- portals[i].status = stat_done;
- }
- return;
- }
-
-
- if (g_bUseMPI)
- {
- RunMPIPortalFlow();
- }
- else
- {
- RunThreadsOnIndividual (g_numportals*2, true, PortalFlow);
- }
-}
-
-
-void CalcVisTrace (void)
-{
- RunThreadsOnIndividual (g_numportals*2, true, BasePortalVis);
- BuildTracePortals( g_TraceClusterStart );
- // NOTE: We only schedule the one-way portals out of the start cluster here
- // so don't run g_numportals*2 in this case
- RunThreadsOnIndividual (g_numportals, true, PortalFlow);
-}
-
-/*
-==================
-CalcVis
-==================
-*/
-void CalcVis (void)
-{
- int i;
-
- if (g_bUseMPI)
- {
- RunMPIBasePortalVis();
- }
- else
- {
- RunThreadsOnIndividual (g_numportals*2, true, BasePortalVis);
- }
-
- SortPortals ();
-
- CalcPortalVis ();
-
- //
- // assemble the leaf vis lists by oring the portal lists
- //
- for ( i = 0; i < portalclusters; i++ )
- {
- ClusterMerge( i );
- }
-
- int count = 0;
- // Now crosscheck each leaf's vis and compress
- for ( i = 0; i < portalclusters; i++ )
- {
- count += CompressAndCrosscheckClusterVis( i );
- }
-
-
- Msg ("Optimized: %d visible clusters (%.2f%%)\n", count, count*100.0/totalvis);
- Msg ("Total clusters visible: %i\n", totalvis);
- Msg ("Average clusters visible: %i\n", totalvis / portalclusters);
-}
-
-
-void SetPortalSphere (portal_t *p)
-{
- int i;
- Vector total, dist;
- winding_t *w;
- float r, bestr;
-
- w = p->winding;
- VectorCopy (vec3_origin, total);
- for (i=0 ; i<w->numpoints ; i++)
- {
- VectorAdd (total, w->points[i], total);
- }
-
- for (i=0 ; i<3 ; i++)
- total[i] /= w->numpoints;
-
- bestr = 0;
- for (i=0 ; i<w->numpoints ; i++)
- {
- VectorSubtract (w->points[i], total, dist);
- r = VectorLength (dist);
- if (r > bestr)
- bestr = r;
- }
- VectorCopy (total, p->origin);
- p->radius = bestr;
-}
-
-/*
-============
-LoadPortals
-============
-*/
-void LoadPortals (char *name)
-{
- int i, j;
- portal_t *p;
- leaf_t *l;
- char magic[80];
- int numpoints;
- winding_t *w;
- int leafnums[2];
- plane_t plane;
-
- FILE *f;
-
- // Open the portal file.
- if ( g_bUseMPI )
- {
- // If we're using MPI, copy off the file to a temporary first. This will download the file
- // from the MPI master, then we get to use nice functions like fscanf on it.
- char tempPath[MAX_PATH], tempFile[MAX_PATH];
- if ( GetTempPath( sizeof( tempPath ), tempPath ) == 0 )
- {
- Error( "LoadPortals: GetTempPath failed.\n" );
- }
-
- if ( GetTempFileName( tempPath, "vvis_portal_", 0, tempFile ) == 0 )
- {
- Error( "LoadPortals: GetTempFileName failed.\n" );
- }
-
- // Read all the data from the network file into memory.
- FileHandle_t hFile = g_pFileSystem->Open(name, "r");
- if ( hFile == FILESYSTEM_INVALID_HANDLE )
- Error( "LoadPortals( %s ): couldn't get file from master.\n", name );
-
- CUtlVector<char> data;
- data.SetSize( g_pFileSystem->Size( hFile ) );
- g_pFileSystem->Read( data.Base(), data.Count(), hFile );
- g_pFileSystem->Close( hFile );
-
- // Dump it into a temp file.
- f = fopen( tempFile, "wt" );
- fwrite( data.Base(), 1, data.Count(), f );
- fclose( f );
-
- // Open the temp file up.
- f = fopen( tempFile, "rSTD" ); // read only, sequential, temporary, delete on close
- }
- else
- {
- f = fopen( name, "r" );
- }
-
- if ( !f )
- Error ("LoadPortals: couldn't read %s\n",name);
-
- if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalclusters, &g_numportals) != 3)
- Error ("LoadPortals %s: failed to read header", name);
- if (stricmp(magic,PORTALFILE))
- Error ("LoadPortals %s: not a portal file", name);
-
- Msg ("%4i portalclusters\n", portalclusters);
- Msg ("%4i numportals\n", g_numportals);
-
- if (g_numportals * 2 >= MAX_PORTALS)
- {
- Error("The map overflows the max portal count (%d of max %d)!\n", g_numportals, MAX_PORTALS / 2 );
- }
-
- // these counts should take advantage of 64 bit systems automatically
- leafbytes = ((portalclusters+63)&~63)>>3;
- leaflongs = leafbytes/sizeof(long);
-
- portalbytes = ((g_numportals*2+63)&~63)>>3;
- portallongs = portalbytes/sizeof(long);
-
-// each file portal is split into two memory portals
- portals = (portal_t*)malloc(2*g_numportals*sizeof(portal_t));
- memset (portals, 0, 2*g_numportals*sizeof(portal_t));
-
- leafs = (leaf_t*)malloc(portalclusters*sizeof(leaf_t));
- memset (leafs, 0, portalclusters*sizeof(leaf_t));
-
- originalvismapsize = portalclusters*leafbytes;
- uncompressedvis = (byte*)malloc(originalvismapsize);
-
- vismap = vismap_p = dvisdata;
- dvis->numclusters = portalclusters;
- vismap_p = (byte *)&dvis->bitofs[portalclusters];
-
- vismap_end = vismap + MAX_MAP_VISIBILITY;
-
- for (i=0, p=portals ; i<g_numportals ; i++)
- {
- if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1])
- != 3)
- Error ("LoadPortals: reading portal %i", i);
- if (numpoints > MAX_POINTS_ON_WINDING)
- Error ("LoadPortals: portal %i has too many points", i);
- if ( (unsigned)leafnums[0] > portalclusters
- || (unsigned)leafnums[1] > portalclusters)
- Error ("LoadPortals: reading portal %i", i);
-
- w = p->winding = NewWinding (numpoints);
- w->original = true;
- w->numpoints = numpoints;
-
- for (j=0 ; j<numpoints ; j++)
- {
- double v[3];
- int k;
-
- // scanf into double, then assign to vec_t
- // so we don't care what size vec_t is
- if (fscanf (f, "(%lf %lf %lf ) "
- , &v[0], &v[1], &v[2]) != 3)
- Error ("LoadPortals: reading portal %i", i);
- for (k=0 ; k<3 ; k++)
- w->points[j][k] = v[k];
- }
- fscanf (f, "\n");
-
- // calc plane
- PlaneFromWinding (w, &plane);
-
- // create forward portal
- l = &leafs[leafnums[0]];
- l->portals.AddToTail(p);
-
- p->winding = w;
- VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
- p->plane.dist = -plane.dist;
- p->leaf = leafnums[1];
- SetPortalSphere (p);
- p++;
-
- // create backwards portal
- l = &leafs[leafnums[1]];
- l->portals.AddToTail(p);
-
- p->winding = NewWinding(w->numpoints);
- p->winding->numpoints = w->numpoints;
- for (j=0 ; j<w->numpoints ; j++)
- {
- VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
- }
-
- p->plane = plane;
- p->leaf = leafnums[0];
- SetPortalSphere (p);
- p++;
-
- }
-
- fclose (f);
-}
-
-
-/*
-================
-CalcPAS
-
-Calculate the PAS (Potentially Audible Set)
-by ORing together all the PVS visible from a leaf
-================
-*/
-void CalcPAS (void)
-{
- int i, j, k, l, index;
- int bitbyte;
- long *dest, *src;
- byte *scan;
- int count;
- byte uncompressed[MAX_MAP_LEAFS/8];
- byte compressed[MAX_MAP_LEAFS/8];
-
- Msg ("Building PAS...\n");
-
- count = 0;
- for (i=0 ; i<portalclusters ; i++)
- {
- scan = uncompressedvis + i*leafbytes;
- memcpy (uncompressed, scan, leafbytes);
- for (j=0 ; j<leafbytes ; j++)
- {
- bitbyte = scan[j];
- if (!bitbyte)
- continue;
- for (k=0 ; k<8 ; k++)
- {
- if (! (bitbyte & (1<<k)) )
- continue;
- // OR this pvs row into the phs
- index = ((j<<3)+k);
- if (index >= portalclusters)
- Error ("Bad bit in PVS"); // pad bits should be 0
- src = (long *)(uncompressedvis + index*leafbytes);
- dest = (long *)uncompressed;
- for (l=0 ; l<leaflongs ; l++)
- ((long *)uncompressed)[l] |= src[l];
- }
- }
- for (j=0 ; j<portalclusters ; j++)
- {
- if ( CheckBit( uncompressed, j ) )
- {
- count++;
- }
- }
-
- //
- // compress the bit string
- //
- j = CompressVis (uncompressed, compressed);
-
- dest = (long *)vismap_p;
- vismap_p += j;
-
- if (vismap_p > vismap_end)
- Error ("Vismap expansion overflow");
-
- dvis->bitofs[i][DVIS_PAS] = (byte *)dest-vismap;
-
- memcpy (dest, compressed, j);
- }
-
- Msg ("Average clusters audible: %i\n", count/portalclusters);
-}
-
-
-
-static void GetBoundsForFace( int faceID, Vector &faceMin, Vector &faceMax )
-{
- ClearBounds( faceMin, faceMax );
- dface_t *pFace = &dfaces[faceID];
- int i;
- for( i = pFace->firstedge; i < pFace->firstedge + pFace->numedges; i++ )
- {
- int edgeID = dsurfedges[i];
- if( edgeID < 0 )
- {
- edgeID = -edgeID;
- }
- dedge_t *pEdge = &dedges[edgeID];
- dvertex_t *pVert0 = &dvertexes[pEdge->v[0]];
- dvertex_t *pVert1 = &dvertexes[pEdge->v[1]];
- AddPointToBounds( pVert0->point, faceMin, faceMax );
- AddPointToBounds( pVert1->point, faceMin, faceMax );
- }
-}
-
-// FIXME: should stick this in mathlib
-static float GetMinDistanceBetweenBoundingBoxes( const Vector &min1, const Vector &max1,
- const Vector &min2, const Vector &max2 )
-{
- if( IsBoxIntersectingBox( min1, max1, min2, max2 ) )
- {
- return 0.0f;
- }
-
- Vector axisDist;
- int i;
- for( i = 0; i < 3; i++ )
- {
- if( min1[i] <= max2[i] && max1[i] >= min2[i] )
- {
- // the intersection in this dimension.
- axisDist[i] = 0.0f;
- }
- else
- {
- float dist1, dist2;
- dist1 = min1[i] - max2[i];
- dist2 = min2[i] - max1[i];
- axisDist[i] = dist1 > dist2 ? dist1 : dist2;
- Assert( axisDist[i] > 0.0f );
- }
- }
-
- float mag = axisDist.Length();
- Assert( mag > 0.0f );
- return mag;
-}
-
-static float CalcDistanceFromLeafToWater( int leafNum )
-{
- byte uncompressed[MAX_MAP_LEAFS/8];
-
- int j, k;
-
- // If we know that this one doesn't see a water surface then don't bother doing anything.
- if( ((dleafs[leafNum].contents & CONTENTS_TESTFOGVOLUME) == 0) && ( dleafs[leafNum].leafWaterDataID == -1 ) )
- return 65535; // FIXME: make a define for this.
-
- // First get the vis data..
- int cluster = dleafs[leafNum].cluster;
- if (cluster < 0)
- return 65535; // FIXME: make a define for this.
-
- DecompressVis( &dvisdata[dvis->bitofs[cluster][DVIS_PVS]], uncompressed );
-
- float minDist = 65535.0f; // FIXME: make a define for this.
-
- Vector leafMin, leafMax;
-
- leafMin[0] = ( float )dleafs[leafNum].mins[0];
- leafMin[1] = ( float )dleafs[leafNum].mins[1];
- leafMin[2] = ( float )dleafs[leafNum].mins[2];
- leafMax[0] = ( float )dleafs[leafNum].maxs[0];
- leafMax[1] = ( float )dleafs[leafNum].maxs[1];
- leafMax[2] = ( float )dleafs[leafNum].maxs[2];
-
-/*
- CUtlVector<listplane_t> temp;
-
- // build a convex solid out of the planes so that we can get at the triangles.
- for( j = dleafs[i].firstleafbrush; j < dleafs[i].firstleafbrush + dleafs[i].numleafbrushes; j++ )
- {
- dbrush_t *pBrush = &dbrushes[j];
- for( k = pBrush->firstside; k < pBrush->firstside + pBrush->numsides; k++ )
- {
- dbrushside_t *pside = dbrushsides + k;
- dplane_t *pplane = dplanes + pside->planenum;
- AddListPlane( &temp, pplane->normal[0], pplane->normal[1], pplane->normal[2], pplane->dist );
- }
- CPhysConvex *pConvex = physcollision->ConvexFromPlanes( (float *)temp.Base(), temp.Count(), VPHYSICS_MERGE );
- ConvertConvexToCollide( &pConvex,
- temp.RemoveAll();
- }
-*/
-
- // Iterate over all potentially visible clusters from this leaf
- for (j = 0; j < dvis->numclusters; ++j)
- {
- // Don't need to bother if this is the same as the current cluster
- if (j == cluster)
- continue;
-
- // If the cluster isn't in our current pvs, then get out of here.
- if ( !CheckBit( uncompressed, j ) )
- continue;
-
- // Found a visible cluster, now iterate over all leaves
- // inside that cluster
- for (k = 0; k < g_ClusterLeaves[j].leafCount; ++k)
- {
- int nClusterLeaf = g_ClusterLeaves[j].leafs[k];
-
- // Don't bother testing the ones that don't see a water boundary.
- if( ((dleafs[nClusterLeaf].contents & CONTENTS_TESTFOGVOLUME) == 0) && ( dleafs[nClusterLeaf].leafWaterDataID == -1 ) )
- continue;
-
- // Find the minimum distance between each surface on the boundary of the leaf
- // that we have the pvs for and each water surface in the leaf that we are testing.
- int nFirstFaceID = dleafs[nClusterLeaf].firstleafface;
- for( int leafFaceID = 0; leafFaceID < dleafs[nClusterLeaf].numleaffaces; ++leafFaceID )
- {
- int faceID = dleaffaces[nFirstFaceID + leafFaceID];
- dface_t *pFace = &dfaces[faceID];
- if( pFace->texinfo == -1 )
- continue;
-
- texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
- if( pTexInfo->flags & SURF_WARP )
- {
- // Woo hoo!!! We found a water face.
- // compare the bounding box of the face with the bounding
- // box of the leaf that we are looking from and see
- // what the closest distance is.
- // FIXME: this could be a face/face distance between the water
- // face and the bounding volume of the leaf.
-
- // Get the bounding box of the face
- Vector faceMin, faceMax;
- GetBoundsForFace( faceID, faceMin, faceMax );
- float dist = GetMinDistanceBetweenBoundingBoxes( leafMin, leafMax, faceMin, faceMax );
- if( dist < minDist )
- {
- minDist = dist;
- }
- }
- }
- }
- }
- return minDist;
-}
-
-static void CalcDistanceFromLeavesToWater( void )
-{
- int i;
- for( i = 0; i < numleafs; i++ )
- {
- g_LeafMinDistToWater[i] = ( unsigned short )CalcDistanceFromLeafToWater( i );
- }
-}
-
-//-----------------------------------------------------------------------------
-// Using the PVS, compute the visible fog volumes from each leaf
-//-----------------------------------------------------------------------------
-static void CalcVisibleFogVolumes()
-{
- byte uncompressed[MAX_MAP_LEAFS/8];
-
- int i, j, k;
-
- // Clear the contents flags for water testing
- for (i = 0; i < numleafs; ++i)
- {
- dleafs[i].contents &= ~CONTENTS_TESTFOGVOLUME;
- g_LeafMinDistToWater[i] = 65535;
- }
-
- for (i = 0; i < numleafs; ++i)
- {
- // If we've already discovered that this leaf needs testing,
- // no need to go through the work again...
- if (dleafs[i].contents & CONTENTS_TESTFOGVOLUME)
- {
- Assert((dleafs[i].contents & (CONTENTS_SLIME | CONTENTS_WATER)) == 0);
- continue;
- }
-
- // Don't bother checking fog volumes from solid leaves
- if (dleafs[i].contents & CONTENTS_SOLID)
- continue;
-
- // Look only for leaves which are visible from leaves that have fluid in them.
- if ( dleafs[i].leafWaterDataID == -1 )
- continue;
-
- // Don't bother about looking from CONTENTS_SLIME; we're not going to treat that as interesting.
- // because slime is opaque
- if ( dleafs[i].contents & CONTENTS_SLIME )
- continue;
-
- // First get the vis data..
- int cluster = dleafs[i].cluster;
- if (cluster < 0)
- continue;
-
- DecompressVis( &dvisdata[dvis->bitofs[cluster][DVIS_PVS]], uncompressed );
-
- // Iterate over all potentially visible clusters from this leaf
- for (j = 0; j < dvis->numclusters; ++j)
- {
- // Don't need to bother if this is the same as the current cluster
- if (j == cluster)
- continue;
-
- if ( !CheckBit( uncompressed, j ) )
- continue;
-
- // Found a visible cluster, now iterate over all leaves
- // inside that cluster
- for (k = 0; k < g_ClusterLeaves[j].leafCount; ++k)
- {
- int nClusterLeaf = g_ClusterLeaves[j].leafs[k];
-
- // Don't bother checking fog volumes from solid leaves
- if ( dleafs[nClusterLeaf].contents & CONTENTS_SOLID )
- continue;
-
- // Don't bother checking from any leaf that's got fluid in it
- if ( dleafs[nClusterLeaf].leafWaterDataID != -1 )
- continue;
-
- // Here, we've found a case where a non-liquid leaf is visible from a liquid leaf
- // So, in this case, we have to do the expensive test during rendering.
- dleafs[nClusterLeaf].contents |= CONTENTS_TESTFOGVOLUME;
- }
- }
- }
-}
-
-
-//-----------------------------------------------------------------------------
-// Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues
-//-----------------------------------------------------------------------------
-float DetermineVisRadius( )
-{
- float flRadius = -1;
-
- // Check the max vis range to determine the vis radius
- for (int i = 0; i < num_entities; ++i)
- {
- char* pEntity = ValueForKey(&entities[i], "classname");
- if (!stricmp(pEntity, "env_fog_controller"))
- {
- flRadius = FloatForKey (&entities[i], "farz");
- if (flRadius == 0.0f)
- flRadius = -1.0f;
- break;
- }
- }
-
- return flRadius;
-}
-
-void MarkLeavesAsRadial()
-{
- for ( int i = 0; i < numleafs; i++ )
- {
- dleafs[i].flags |= LEAF_FLAGS_RADIAL;
- }
-}
-
-
-int ParseCommandLine( int argc, char **argv )
-{
- int i;
- for (i=1 ; i<argc ; i++)
- {
- if (!Q_stricmp(argv[i],"-threads"))
- {
- numthreads = atoi (argv[i+1]);
- i++;
- }
- else if (!Q_stricmp(argv[i], "-fast"))
- {
- Msg ("fastvis = true\n");
- fastvis = true;
- }
- else if (!Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose"))
- {
- Msg ("verbose = true\n");
- verbose = true;
- }
- else if( !Q_stricmp( argv[i], "-radius_override" ) )
- {
- g_bUseRadius = true;
- g_VisRadius = atof( argv[i+1] );
- i++;
- Msg( "Vis Radius = %4.2f\n", g_VisRadius );
- g_VisRadius = g_VisRadius * g_VisRadius; // so distance check can be squared
- }
- else if( !Q_stricmp( argv[i], "-trace" ) )
- {
- g_TraceClusterStart = atoi( argv[i+1] );
- i++;
- g_TraceClusterStop = atoi( argv[i+1] );
- i++;
- Msg( "Tracing vis from cluster %d to %d\n", g_TraceClusterStart, g_TraceClusterStop );
- }
- else if (!Q_stricmp (argv[i],"-nosort"))
- {
- Msg ("nosort = true\n");
- nosort = true;
- }
- else if (!Q_stricmp (argv[i],"-tmpin"))
- strcpy (inbase, "/tmp");
- else if( !Q_stricmp( argv[i], "-low" ) )
- {
- g_bLowPriority = true;
- }
- else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
- {
- EnableFullMinidumps( true );
- }
- else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
- {
- }
- else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
- {
- ++i;
- }
- else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
- {
- // nothing to do here, but don't bail on this option
- }
- // NOTE: the -mpi checks must come last here because they allow the previous argument
- // to be -mpi as well. If it game before something else like -game, then if the previous
- // argument was -mpi and the current argument was something valid like -game, it would skip it.
- else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) )
- {
- if ( stricmp( argv[i], "-mpi" ) == 0 )
- g_bUseMPI = true;
-
- // Any other args that start with -mpi are ok too.
- if ( i == argc - 1 )
- break;
- }
- else if (argv[i][0] == '-')
- {
- Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
- i = 100000; // force it to print the usage
- break;
- }
- else
- break;
- }
- return i;
-}
-
-
-void PrintCommandLine( int argc, char **argv )
-{
- Warning( "Command line: " );
- for ( int z=0; z < argc; z++ )
- {
- Warning( "\"%s\" ", argv[z] );
- }
- Warning( "\n\n" );
-}
-
-
-void PrintUsage( int argc, char **argv )
-{
- PrintCommandLine( argc, argv );
-
- Warning(
- "usage : vvis [options...] bspfile\n"
- "example: vvis -fast c:\\hl2\\hl2\\maps\\test\n"
- "\n"
- "Common options:\n"
- "\n"
- " -v (or -verbose): Turn on verbose output (also shows more command\n"
- " -fast : Only do first quick pass on vis calculations.\n"
- " -mpi : Use VMPI to distribute computations.\n"
- " -low : Run as an idle-priority process.\n"
- " env_fog_controller specifies one.\n"
- "\n"
- " -vproject <directory> : Override the VPROJECT environment variable.\n"
- " -game <directory> : Same as -vproject.\n"
- "\n"
- "Other options:\n"
- " -novconfig : Don't bring up graphical UI on vproject errors.\n"
- " -radius_override: Force a vis radius, regardless of whether an\n"
- " -mpi_pw <pw> : Use a password to choose a specific set of VMPI workers.\n"
- " -threads : Control the number of threads vbsp uses (defaults to the #\n"
- " or processors on your machine).\n"
- " -nosort : Don't sort portals (sorting is an optimization).\n"
- " -tmpin : Make portals come from \\tmp\\<mapname>.\n"
- " -tmpout : Make portals come from \\tmp\\<mapname>.\n"
- " -trace <start cluster> <end cluster> : Writes a linefile that traces the vis from one cluster to another for debugging map vis.\n"
- " -FullMinidumps : Write large minidumps on crash.\n"
- " -x360 : Generate Xbox360 version of vsp\n"
- " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
- "\n"
-#if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users.
- );
-#else
- " -mpi_ListParams : Show a list of VMPI parameters.\n"
- "\n"
- );
-
- // Show VMPI parameters?
- for ( int i=1; i < argc; i++ )
- {
- if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 )
- {
- Warning( "VMPI-specific options:\n\n" );
-
- bool bIsSDKMode = VMPI_IsSDKMode();
- for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ )
- {
- if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode )
- continue;
-
- Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) );
- Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) );
- Warning( "\n\n" );
- }
- break;
- }
- }
-#endif
-}
-
-
-int RunVVis( int argc, char **argv )
-{
- char portalfile[1024];
- char source[1024];
- double start, end;
-
-
- Msg( "Valve Software - vvis.exe (%s)\n", __DATE__ );
-
- verbose = false;
-
- Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) );
- CmdLib_InitFileSystem( argv[ argc - 1 ] );
-
- Q_FileBase( source, source, sizeof( source ) );
-
- LoadCmdLineFromFile( argc, argv, source, "vvis" );
- int i = ParseCommandLine( argc, argv );
-
- // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames,
- // so we prepend qdir here.
- strcpy( source, ExpandPath( source ) );
-
- if (i != argc - 1)
- {
- PrintUsage( argc, argv );
- DeleteCmdLine( argc, argv );
- CmdLib_Exit( 1 );
- }
-
- start = Plat_FloatTime();
-
-
- if (!g_bUseMPI)
- {
- // Setup the logfile.
- char logFile[512];
- _snprintf( logFile, sizeof(logFile), "%s.log", source );
- SetSpewFunctionLogFile( logFile );
- }
-
- // Run in the background?
- if( g_bLowPriority )
- {
- SetLowPriority();
- }
-
- ThreadSetDefault ();
-
- char targetPath[1024];
- GetPlatformMapPath( source, targetPath, 0, 1024 );
- Msg ("reading %s\n", targetPath);
- LoadBSPFile (targetPath);
- if (numnodes == 0 || numfaces == 0)
- Error ("Empty map");
- ParseEntities ();
-
- // Check the VMF for a vis radius
- if (!g_bUseRadius)
- {
- float flRadius = DetermineVisRadius( );
- if (flRadius > 0.0f)
- {
- g_bUseRadius = true;
- g_VisRadius = flRadius * flRadius;
- }
- }
-
- if ( g_bUseRadius )
- {
- MarkLeavesAsRadial();
- }
-
- if ( inbase[0] == 0 )
- {
- strcpy( portalfile, source );
- }
- else
- {
- sprintf ( portalfile, "%s%s", inbase, argv[i] );
- Q_StripExtension( portalfile, portalfile, sizeof( portalfile ) );
- }
- strcat (portalfile, ".prt");
-
- Msg ("reading %s\n", portalfile);
- LoadPortals (portalfile);
-
- // don't write out results when simply doing a trace
- if ( g_TraceClusterStart < 0 )
- {
- CalcVis ();
- CalcPAS ();
-
- // We need a mapping from cluster to leaves, since the PVS
- // deals with clusters for both CalcVisibleFogVolumes and
- BuildClusterTable();
-
- CalcVisibleFogVolumes();
- CalcDistanceFromLeavesToWater();
-
- visdatasize = vismap_p - dvisdata;
- Msg ("visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize*2);
-
- Msg ("writing %s\n", targetPath);
- WriteBSPFile (targetPath);
- }
- else
- {
- if ( g_TraceClusterStart < 0 || g_TraceClusterStart >= portalclusters || g_TraceClusterStop < 0 || g_TraceClusterStop >= portalclusters )
- {
- Error("Invalid cluster trace: %d to %d, valid range is 0 to %d\n", g_TraceClusterStart, g_TraceClusterStop, portalclusters-1 );
- }
- if ( g_bUseMPI )
- {
- Warning("Can't compile trace in MPI mode\n");
- }
- CalcVisTrace ();
- WritePortalTrace(source);
- }
-
- end = Plat_FloatTime();
-
- char str[512];
- GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
- Msg( "%s elapsed\n", str );
-
- ReleasePakFileLumps();
- DeleteCmdLine( argc, argv );
- CmdLib_Cleanup();
- return 0;
-}
-
-
-/*
-===========
-main
-===========
-*/
-int main (int argc, char **argv)
-{
- CommandLine()->CreateCmdLine( argc, argv );
-
- MathLib_Init( 2.2f, 2.2f, 0.0f, 1.0f, false, false, false, false );
- InstallAllocationFunctions();
- InstallSpewFunction();
-
- VVIS_SetupMPI( argc, argv );
-
- // Install an exception handler.
- if ( g_bUseMPI && !g_bMPIMaster )
- SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
- else
- SetupDefaultToolsMinidumpHandler();
-
- return RunVVis( argc, argv );
-}
-
-
-// When VVIS is used as a DLL (makes debugging vmpi vvis a lot easier), this is used to
-// get it going.
-class CVVisDLL : public ILaunchableDLL
-{
-public:
- virtual int main( int argc, char **argv )
- {
- return ::main( argc, argv );
- }
-};
-
-EXPOSE_SINGLE_INTERFACE( CVVisDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION );
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// vis.c
+
+#include <windows.h>
+#include "vis.h"
+#include "threads.h"
+#include "stdlib.h"
+#include "pacifier.h"
+#include "vmpi.h"
+#include "mpivis.h"
+#include "tier1/strtools.h"
+#include "collisionutils.h"
+#include "tier0/icommandline.h"
+#include "vmpi_tools_shared.h"
+#include "ilaunchabledll.h"
+#include "tools_minidump.h"
+#include "loadcmdline.h"
+#include "byteswap.h"
+
+
+int g_numportals;
+int portalclusters;
+
+char inbase[32];
+
+portal_t *portals;
+leaf_t *leafs;
+
+int c_portaltest, c_portalpass, c_portalcheck;
+
+byte *uncompressedvis;
+
+byte *vismap, *vismap_p, *vismap_end; // past visfile
+int originalvismapsize;
+
+int leafbytes; // (portalclusters+63)>>3
+int leaflongs;
+
+int portalbytes, portallongs;
+
+bool fastvis;
+bool nosort;
+
+int totalvis;
+
+portal_t *sorted_portals[MAX_MAP_PORTALS*2];
+
+bool g_bUseRadius = false;
+double g_VisRadius = 4096.0f * 4096.0f;
+
+bool g_bLowPriority = false;
+
+//=============================================================================
+
+void PlaneFromWinding (winding_t *w, plane_t *plane)
+{
+ Vector v1, v2;
+
+// calc plane
+ VectorSubtract (w->points[2], w->points[1], v1);
+ VectorSubtract (w->points[0], w->points[1], v2);
+ CrossProduct (v2, v1, plane->normal);
+ VectorNormalize (plane->normal);
+ plane->dist = DotProduct (w->points[0], plane->normal);
+}
+
+
+/*
+==================
+NewWinding
+==================
+*/
+winding_t *NewWinding (int points)
+{
+ winding_t *w;
+ int size;
+
+ if (points > MAX_POINTS_ON_WINDING)
+ Error ("NewWinding: %i points, max %d", points, MAX_POINTS_ON_WINDING);
+
+ size = (int)(&((winding_t *)0)->points[points]);
+ w = (winding_t*)malloc (size);
+ memset (w, 0, size);
+
+ return w;
+}
+
+void pw(winding_t *w)
+{
+ int i;
+ for (i=0 ; i<w->numpoints ; i++)
+ Msg ("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]);
+}
+
+void prl(leaf_t *l)
+{
+ int i;
+ portal_t *p;
+ plane_t pl;
+
+ int count = l->portals.Count();
+ for (i=0 ; i<count ; i++)
+ {
+ p = l->portals[i];
+ pl = p->plane;
+ Msg ("portal %4i to leaf %4i : %7.1f : (%4.1f, %4.1f, %4.1f)\n",(int)(p-portals),p->leaf,pl.dist, pl.normal[0], pl.normal[1], pl.normal[2]);
+ }
+}
+
+
+//=============================================================================
+
+/*
+=============
+SortPortals
+
+Sorts the portals from the least complex, so the later ones can reuse
+the earlier information.
+=============
+*/
+int PComp (const void *a, const void *b)
+{
+ if ( (*(portal_t **)a)->nummightsee == (*(portal_t **)b)->nummightsee)
+ return 0;
+ if ( (*(portal_t **)a)->nummightsee < (*(portal_t **)b)->nummightsee)
+ return -1;
+
+ return 1;
+}
+
+void BuildTracePortals( int clusterStart )
+{
+ leaf_t *leaf = &leafs[g_TraceClusterStart];
+ g_numportals = leaf->portals.Count();
+ for ( int i = 0; i < g_numportals; i++ )
+ {
+ sorted_portals[i] = leaf->portals[i];
+ }
+}
+
+void SortPortals (void)
+{
+ int i;
+
+ for (i=0 ; i<g_numportals*2 ; i++)
+ sorted_portals[i] = &portals[i];
+
+ if (nosort)
+ return;
+ qsort (sorted_portals, g_numportals*2, sizeof(sorted_portals[0]), PComp);
+}
+
+
+/*
+==============
+LeafVectorFromPortalVector
+==============
+*/
+int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
+{
+ int i;
+ portal_t *p;
+ int c_leafs;
+
+
+ memset (leafbits, 0, leafbytes);
+
+ for (i=0 ; i<g_numportals*2 ; i++)
+ {
+ if ( CheckBit( portalbits, i ) )
+ {
+ p = portals+i;
+ SetBit( leafbits, p->leaf );
+ }
+ }
+
+ c_leafs = CountBits (leafbits, portalclusters);
+
+ return c_leafs;
+}
+
+
+/*
+===============
+ClusterMerge
+
+Merges the portal visibility for a leaf
+===============
+*/
+void ClusterMerge (int clusternum)
+{
+ leaf_t *leaf;
+// byte portalvector[MAX_PORTALS/8];
+ byte portalvector[MAX_PORTALS/4]; // 4 because portal bytes is * 2
+ byte uncompressed[MAX_MAP_LEAFS/8];
+ int i, j;
+ int numvis;
+ portal_t *p;
+ int pnum;
+
+ // OR together all the portalvis bits
+
+ memset (portalvector, 0, portalbytes);
+ leaf = &leafs[clusternum];
+ for (i=0 ; i < leaf->portals.Count(); i++)
+ {
+ p = leaf->portals[i];
+ if (p->status != stat_done)
+ Error ("portal not done %d %p %p\n", i, p, portals);
+ for (j=0 ; j<portallongs ; j++)
+ ((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
+ pnum = p - portals;
+ SetBit( portalvector, pnum );
+ }
+
+ // convert portal bits to leaf bits
+ numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
+
+#if 0
+ // func_viscluster makes this happen all the time because it allows a non-convex set of portals
+ // My analysis says this is ok, but it does make this check for errors in vis kind of useless
+ if ( CheckBit( uncompressed, clusternum ) )
+ Warning("WARNING: Cluster portals saw into cluster\n");
+#endif
+
+ SetBit( uncompressed, clusternum );
+ numvis++; // count the leaf itself
+
+ // save uncompressed for PHS calculation
+ memcpy (uncompressedvis + clusternum*leafbytes, uncompressed, leafbytes);
+
+ qprintf ("cluster %4i : %4i visible\n", clusternum, numvis);
+ totalvis += numvis;
+}
+
+static int CompressAndCrosscheckClusterVis( int clusternum )
+{
+ int optimized = 0;
+ byte compressed[MAX_MAP_LEAFS/8];
+//
+// compress the bit string
+//
+ byte *uncompressed = uncompressedvis + clusternum*leafbytes;
+ for ( int i = 0; i < portalclusters; i++ )
+ {
+ if ( i == clusternum )
+ continue;
+
+ if ( CheckBit( uncompressed, i ) )
+ {
+ byte *other = uncompressedvis + i*leafbytes;
+ if ( !CheckBit( other, clusternum ) )
+ {
+ ClearBit( uncompressed, i );
+ optimized++;
+ }
+ }
+ }
+ int numbytes = CompressVis( uncompressed, compressed );
+
+ byte *dest = vismap_p;
+ vismap_p += numbytes;
+
+ if (vismap_p > vismap_end)
+ Error ("Vismap expansion overflow");
+
+ dvis->bitofs[clusternum][DVIS_PVS] = dest-vismap;
+
+ memcpy( dest, compressed, numbytes );
+
+ // check vis data
+ DecompressVis( vismap + dvis->bitofs[clusternum][DVIS_PVS], compressed );
+
+ return optimized;
+}
+
+
+/*
+==================
+CalcPortalVis
+==================
+*/
+void CalcPortalVis (void)
+{
+ int i;
+
+ // fastvis just uses mightsee for a very loose bound
+ if( fastvis )
+ {
+ for (i=0 ; i<g_numportals*2 ; i++)
+ {
+ portals[i].portalvis = portals[i].portalflood;
+ portals[i].status = stat_done;
+ }
+ return;
+ }
+
+
+ if (g_bUseMPI)
+ {
+ RunMPIPortalFlow();
+ }
+ else
+ {
+ RunThreadsOnIndividual (g_numportals*2, true, PortalFlow);
+ }
+}
+
+
+void CalcVisTrace (void)
+{
+ RunThreadsOnIndividual (g_numportals*2, true, BasePortalVis);
+ BuildTracePortals( g_TraceClusterStart );
+ // NOTE: We only schedule the one-way portals out of the start cluster here
+ // so don't run g_numportals*2 in this case
+ RunThreadsOnIndividual (g_numportals, true, PortalFlow);
+}
+
+/*
+==================
+CalcVis
+==================
+*/
+void CalcVis (void)
+{
+ int i;
+
+ if (g_bUseMPI)
+ {
+ RunMPIBasePortalVis();
+ }
+ else
+ {
+ RunThreadsOnIndividual (g_numportals*2, true, BasePortalVis);
+ }
+
+ SortPortals ();
+
+ CalcPortalVis ();
+
+ //
+ // assemble the leaf vis lists by oring the portal lists
+ //
+ for ( i = 0; i < portalclusters; i++ )
+ {
+ ClusterMerge( i );
+ }
+
+ int count = 0;
+ // Now crosscheck each leaf's vis and compress
+ for ( i = 0; i < portalclusters; i++ )
+ {
+ count += CompressAndCrosscheckClusterVis( i );
+ }
+
+
+ Msg ("Optimized: %d visible clusters (%.2f%%)\n", count, count*100.0/totalvis);
+ Msg ("Total clusters visible: %i\n", totalvis);
+ Msg ("Average clusters visible: %i\n", totalvis / portalclusters);
+}
+
+
+void SetPortalSphere (portal_t *p)
+{
+ int i;
+ Vector total, dist;
+ winding_t *w;
+ float r, bestr;
+
+ w = p->winding;
+ VectorCopy (vec3_origin, total);
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorAdd (total, w->points[i], total);
+ }
+
+ for (i=0 ; i<3 ; i++)
+ total[i] /= w->numpoints;
+
+ bestr = 0;
+ for (i=0 ; i<w->numpoints ; i++)
+ {
+ VectorSubtract (w->points[i], total, dist);
+ r = VectorLength (dist);
+ if (r > bestr)
+ bestr = r;
+ }
+ VectorCopy (total, p->origin);
+ p->radius = bestr;
+}
+
+/*
+============
+LoadPortals
+============
+*/
+void LoadPortals (char *name)
+{
+ int i, j;
+ portal_t *p;
+ leaf_t *l;
+ char magic[80];
+ int numpoints;
+ winding_t *w;
+ int leafnums[2];
+ plane_t plane;
+
+ FILE *f;
+
+ // Open the portal file.
+ if ( g_bUseMPI )
+ {
+ // If we're using MPI, copy off the file to a temporary first. This will download the file
+ // from the MPI master, then we get to use nice functions like fscanf on it.
+ char tempPath[MAX_PATH], tempFile[MAX_PATH];
+ if ( GetTempPath( sizeof( tempPath ), tempPath ) == 0 )
+ {
+ Error( "LoadPortals: GetTempPath failed.\n" );
+ }
+
+ if ( GetTempFileName( tempPath, "vvis_portal_", 0, tempFile ) == 0 )
+ {
+ Error( "LoadPortals: GetTempFileName failed.\n" );
+ }
+
+ // Read all the data from the network file into memory.
+ FileHandle_t hFile = g_pFileSystem->Open(name, "r");
+ if ( hFile == FILESYSTEM_INVALID_HANDLE )
+ Error( "LoadPortals( %s ): couldn't get file from master.\n", name );
+
+ CUtlVector<char> data;
+ data.SetSize( g_pFileSystem->Size( hFile ) );
+ g_pFileSystem->Read( data.Base(), data.Count(), hFile );
+ g_pFileSystem->Close( hFile );
+
+ // Dump it into a temp file.
+ f = fopen( tempFile, "wt" );
+ fwrite( data.Base(), 1, data.Count(), f );
+ fclose( f );
+
+ // Open the temp file up.
+ f = fopen( tempFile, "rSTD" ); // read only, sequential, temporary, delete on close
+ }
+ else
+ {
+ f = fopen( name, "r" );
+ }
+
+ if ( !f )
+ Error ("LoadPortals: couldn't read %s\n",name);
+
+ if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalclusters, &g_numportals) != 3)
+ Error ("LoadPortals %s: failed to read header", name);
+ if (stricmp(magic,PORTALFILE))
+ Error ("LoadPortals %s: not a portal file", name);
+
+ Msg ("%4i portalclusters\n", portalclusters);
+ Msg ("%4i numportals\n", g_numportals);
+
+ if (g_numportals * 2 >= MAX_PORTALS)
+ {
+ Error("The map overflows the max portal count (%d of max %d)!\n", g_numportals, MAX_PORTALS / 2 );
+ }
+
+ // these counts should take advantage of 64 bit systems automatically
+ leafbytes = ((portalclusters+63)&~63)>>3;
+ leaflongs = leafbytes/sizeof(long);
+
+ portalbytes = ((g_numportals*2+63)&~63)>>3;
+ portallongs = portalbytes/sizeof(long);
+
+// each file portal is split into two memory portals
+ portals = (portal_t*)malloc(2*g_numportals*sizeof(portal_t));
+ memset (portals, 0, 2*g_numportals*sizeof(portal_t));
+
+ leafs = (leaf_t*)malloc(portalclusters*sizeof(leaf_t));
+ memset (leafs, 0, portalclusters*sizeof(leaf_t));
+
+ originalvismapsize = portalclusters*leafbytes;
+ uncompressedvis = (byte*)malloc(originalvismapsize);
+
+ vismap = vismap_p = dvisdata;
+ dvis->numclusters = portalclusters;
+ vismap_p = (byte *)&dvis->bitofs[portalclusters];
+
+ vismap_end = vismap + MAX_MAP_VISIBILITY;
+
+ for (i=0, p=portals ; i<g_numportals ; i++)
+ {
+ if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1])
+ != 3)
+ Error ("LoadPortals: reading portal %i", i);
+ if (numpoints > MAX_POINTS_ON_WINDING)
+ Error ("LoadPortals: portal %i has too many points", i);
+ if ( (unsigned)leafnums[0] > portalclusters
+ || (unsigned)leafnums[1] > portalclusters)
+ Error ("LoadPortals: reading portal %i", i);
+
+ w = p->winding = NewWinding (numpoints);
+ w->original = true;
+ w->numpoints = numpoints;
+
+ for (j=0 ; j<numpoints ; j++)
+ {
+ double v[3];
+ int k;
+
+ // scanf into double, then assign to vec_t
+ // so we don't care what size vec_t is
+ if (fscanf (f, "(%lf %lf %lf ) "
+ , &v[0], &v[1], &v[2]) != 3)
+ Error ("LoadPortals: reading portal %i", i);
+ for (k=0 ; k<3 ; k++)
+ w->points[j][k] = v[k];
+ }
+ fscanf (f, "\n");
+
+ // calc plane
+ PlaneFromWinding (w, &plane);
+
+ // create forward portal
+ l = &leafs[leafnums[0]];
+ l->portals.AddToTail(p);
+
+ p->winding = w;
+ VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
+ p->plane.dist = -plane.dist;
+ p->leaf = leafnums[1];
+ SetPortalSphere (p);
+ p++;
+
+ // create backwards portal
+ l = &leafs[leafnums[1]];
+ l->portals.AddToTail(p);
+
+ p->winding = NewWinding(w->numpoints);
+ p->winding->numpoints = w->numpoints;
+ for (j=0 ; j<w->numpoints ; j++)
+ {
+ VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
+ }
+
+ p->plane = plane;
+ p->leaf = leafnums[0];
+ SetPortalSphere (p);
+ p++;
+
+ }
+
+ fclose (f);
+}
+
+
+/*
+================
+CalcPAS
+
+Calculate the PAS (Potentially Audible Set)
+by ORing together all the PVS visible from a leaf
+================
+*/
+void CalcPAS (void)
+{
+ int i, j, k, l, index;
+ int bitbyte;
+ long *dest, *src;
+ byte *scan;
+ int count;
+ byte uncompressed[MAX_MAP_LEAFS/8];
+ byte compressed[MAX_MAP_LEAFS/8];
+
+ Msg ("Building PAS...\n");
+
+ count = 0;
+ for (i=0 ; i<portalclusters ; i++)
+ {
+ scan = uncompressedvis + i*leafbytes;
+ memcpy (uncompressed, scan, leafbytes);
+ for (j=0 ; j<leafbytes ; j++)
+ {
+ bitbyte = scan[j];
+ if (!bitbyte)
+ continue;
+ for (k=0 ; k<8 ; k++)
+ {
+ if (! (bitbyte & (1<<k)) )
+ continue;
+ // OR this pvs row into the phs
+ index = ((j<<3)+k);
+ if (index >= portalclusters)
+ Error ("Bad bit in PVS"); // pad bits should be 0
+ src = (long *)(uncompressedvis + index*leafbytes);
+ dest = (long *)uncompressed;
+ for (l=0 ; l<leaflongs ; l++)
+ ((long *)uncompressed)[l] |= src[l];
+ }
+ }
+ for (j=0 ; j<portalclusters ; j++)
+ {
+ if ( CheckBit( uncompressed, j ) )
+ {
+ count++;
+ }
+ }
+
+ //
+ // compress the bit string
+ //
+ j = CompressVis (uncompressed, compressed);
+
+ dest = (long *)vismap_p;
+ vismap_p += j;
+
+ if (vismap_p > vismap_end)
+ Error ("Vismap expansion overflow");
+
+ dvis->bitofs[i][DVIS_PAS] = (byte *)dest-vismap;
+
+ memcpy (dest, compressed, j);
+ }
+
+ Msg ("Average clusters audible: %i\n", count/portalclusters);
+}
+
+
+
+static void GetBoundsForFace( int faceID, Vector &faceMin, Vector &faceMax )
+{
+ ClearBounds( faceMin, faceMax );
+ dface_t *pFace = &dfaces[faceID];
+ int i;
+ for( i = pFace->firstedge; i < pFace->firstedge + pFace->numedges; i++ )
+ {
+ int edgeID = dsurfedges[i];
+ if( edgeID < 0 )
+ {
+ edgeID = -edgeID;
+ }
+ dedge_t *pEdge = &dedges[edgeID];
+ dvertex_t *pVert0 = &dvertexes[pEdge->v[0]];
+ dvertex_t *pVert1 = &dvertexes[pEdge->v[1]];
+ AddPointToBounds( pVert0->point, faceMin, faceMax );
+ AddPointToBounds( pVert1->point, faceMin, faceMax );
+ }
+}
+
+// FIXME: should stick this in mathlib
+static float GetMinDistanceBetweenBoundingBoxes( const Vector &min1, const Vector &max1,
+ const Vector &min2, const Vector &max2 )
+{
+ if( IsBoxIntersectingBox( min1, max1, min2, max2 ) )
+ {
+ return 0.0f;
+ }
+
+ Vector axisDist;
+ int i;
+ for( i = 0; i < 3; i++ )
+ {
+ if( min1[i] <= max2[i] && max1[i] >= min2[i] )
+ {
+ // the intersection in this dimension.
+ axisDist[i] = 0.0f;
+ }
+ else
+ {
+ float dist1, dist2;
+ dist1 = min1[i] - max2[i];
+ dist2 = min2[i] - max1[i];
+ axisDist[i] = dist1 > dist2 ? dist1 : dist2;
+ Assert( axisDist[i] > 0.0f );
+ }
+ }
+
+ float mag = axisDist.Length();
+ Assert( mag > 0.0f );
+ return mag;
+}
+
+static float CalcDistanceFromLeafToWater( int leafNum )
+{
+ byte uncompressed[MAX_MAP_LEAFS/8];
+
+ int j, k;
+
+ // If we know that this one doesn't see a water surface then don't bother doing anything.
+ if( ((dleafs[leafNum].contents & CONTENTS_TESTFOGVOLUME) == 0) && ( dleafs[leafNum].leafWaterDataID == -1 ) )
+ return 65535; // FIXME: make a define for this.
+
+ // First get the vis data..
+ int cluster = dleafs[leafNum].cluster;
+ if (cluster < 0)
+ return 65535; // FIXME: make a define for this.
+
+ DecompressVis( &dvisdata[dvis->bitofs[cluster][DVIS_PVS]], uncompressed );
+
+ float minDist = 65535.0f; // FIXME: make a define for this.
+
+ Vector leafMin, leafMax;
+
+ leafMin[0] = ( float )dleafs[leafNum].mins[0];
+ leafMin[1] = ( float )dleafs[leafNum].mins[1];
+ leafMin[2] = ( float )dleafs[leafNum].mins[2];
+ leafMax[0] = ( float )dleafs[leafNum].maxs[0];
+ leafMax[1] = ( float )dleafs[leafNum].maxs[1];
+ leafMax[2] = ( float )dleafs[leafNum].maxs[2];
+
+/*
+ CUtlVector<listplane_t> temp;
+
+ // build a convex solid out of the planes so that we can get at the triangles.
+ for( j = dleafs[i].firstleafbrush; j < dleafs[i].firstleafbrush + dleafs[i].numleafbrushes; j++ )
+ {
+ dbrush_t *pBrush = &dbrushes[j];
+ for( k = pBrush->firstside; k < pBrush->firstside + pBrush->numsides; k++ )
+ {
+ dbrushside_t *pside = dbrushsides + k;
+ dplane_t *pplane = dplanes + pside->planenum;
+ AddListPlane( &temp, pplane->normal[0], pplane->normal[1], pplane->normal[2], pplane->dist );
+ }
+ CPhysConvex *pConvex = physcollision->ConvexFromPlanes( (float *)temp.Base(), temp.Count(), VPHYSICS_MERGE );
+ ConvertConvexToCollide( &pConvex,
+ temp.RemoveAll();
+ }
+*/
+
+ // Iterate over all potentially visible clusters from this leaf
+ for (j = 0; j < dvis->numclusters; ++j)
+ {
+ // Don't need to bother if this is the same as the current cluster
+ if (j == cluster)
+ continue;
+
+ // If the cluster isn't in our current pvs, then get out of here.
+ if ( !CheckBit( uncompressed, j ) )
+ continue;
+
+ // Found a visible cluster, now iterate over all leaves
+ // inside that cluster
+ for (k = 0; k < g_ClusterLeaves[j].leafCount; ++k)
+ {
+ int nClusterLeaf = g_ClusterLeaves[j].leafs[k];
+
+ // Don't bother testing the ones that don't see a water boundary.
+ if( ((dleafs[nClusterLeaf].contents & CONTENTS_TESTFOGVOLUME) == 0) && ( dleafs[nClusterLeaf].leafWaterDataID == -1 ) )
+ continue;
+
+ // Find the minimum distance between each surface on the boundary of the leaf
+ // that we have the pvs for and each water surface in the leaf that we are testing.
+ int nFirstFaceID = dleafs[nClusterLeaf].firstleafface;
+ for( int leafFaceID = 0; leafFaceID < dleafs[nClusterLeaf].numleaffaces; ++leafFaceID )
+ {
+ int faceID = dleaffaces[nFirstFaceID + leafFaceID];
+ dface_t *pFace = &dfaces[faceID];
+ if( pFace->texinfo == -1 )
+ continue;
+
+ texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
+ if( pTexInfo->flags & SURF_WARP )
+ {
+ // Woo hoo!!! We found a water face.
+ // compare the bounding box of the face with the bounding
+ // box of the leaf that we are looking from and see
+ // what the closest distance is.
+ // FIXME: this could be a face/face distance between the water
+ // face and the bounding volume of the leaf.
+
+ // Get the bounding box of the face
+ Vector faceMin, faceMax;
+ GetBoundsForFace( faceID, faceMin, faceMax );
+ float dist = GetMinDistanceBetweenBoundingBoxes( leafMin, leafMax, faceMin, faceMax );
+ if( dist < minDist )
+ {
+ minDist = dist;
+ }
+ }
+ }
+ }
+ }
+ return minDist;
+}
+
+static void CalcDistanceFromLeavesToWater( void )
+{
+ int i;
+ for( i = 0; i < numleafs; i++ )
+ {
+ g_LeafMinDistToWater[i] = ( unsigned short )CalcDistanceFromLeafToWater( i );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Using the PVS, compute the visible fog volumes from each leaf
+//-----------------------------------------------------------------------------
+static void CalcVisibleFogVolumes()
+{
+ byte uncompressed[MAX_MAP_LEAFS/8];
+
+ int i, j, k;
+
+ // Clear the contents flags for water testing
+ for (i = 0; i < numleafs; ++i)
+ {
+ dleafs[i].contents &= ~CONTENTS_TESTFOGVOLUME;
+ g_LeafMinDistToWater[i] = 65535;
+ }
+
+ for (i = 0; i < numleafs; ++i)
+ {
+ // If we've already discovered that this leaf needs testing,
+ // no need to go through the work again...
+ if (dleafs[i].contents & CONTENTS_TESTFOGVOLUME)
+ {
+ Assert((dleafs[i].contents & (CONTENTS_SLIME | CONTENTS_WATER)) == 0);
+ continue;
+ }
+
+ // Don't bother checking fog volumes from solid leaves
+ if (dleafs[i].contents & CONTENTS_SOLID)
+ continue;
+
+ // Look only for leaves which are visible from leaves that have fluid in them.
+ if ( dleafs[i].leafWaterDataID == -1 )
+ continue;
+
+ // Don't bother about looking from CONTENTS_SLIME; we're not going to treat that as interesting.
+ // because slime is opaque
+ if ( dleafs[i].contents & CONTENTS_SLIME )
+ continue;
+
+ // First get the vis data..
+ int cluster = dleafs[i].cluster;
+ if (cluster < 0)
+ continue;
+
+ DecompressVis( &dvisdata[dvis->bitofs[cluster][DVIS_PVS]], uncompressed );
+
+ // Iterate over all potentially visible clusters from this leaf
+ for (j = 0; j < dvis->numclusters; ++j)
+ {
+ // Don't need to bother if this is the same as the current cluster
+ if (j == cluster)
+ continue;
+
+ if ( !CheckBit( uncompressed, j ) )
+ continue;
+
+ // Found a visible cluster, now iterate over all leaves
+ // inside that cluster
+ for (k = 0; k < g_ClusterLeaves[j].leafCount; ++k)
+ {
+ int nClusterLeaf = g_ClusterLeaves[j].leafs[k];
+
+ // Don't bother checking fog volumes from solid leaves
+ if ( dleafs[nClusterLeaf].contents & CONTENTS_SOLID )
+ continue;
+
+ // Don't bother checking from any leaf that's got fluid in it
+ if ( dleafs[nClusterLeaf].leafWaterDataID != -1 )
+ continue;
+
+ // Here, we've found a case where a non-liquid leaf is visible from a liquid leaf
+ // So, in this case, we have to do the expensive test during rendering.
+ dleafs[nClusterLeaf].contents |= CONTENTS_TESTFOGVOLUME;
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues
+//-----------------------------------------------------------------------------
+float DetermineVisRadius( )
+{
+ float flRadius = -1;
+
+ // Check the max vis range to determine the vis radius
+ for (int i = 0; i < num_entities; ++i)
+ {
+ char* pEntity = ValueForKey(&entities[i], "classname");
+ if (!stricmp(pEntity, "env_fog_controller"))
+ {
+ flRadius = FloatForKey (&entities[i], "farz");
+ if (flRadius == 0.0f)
+ flRadius = -1.0f;
+ break;
+ }
+ }
+
+ return flRadius;
+}
+
+void MarkLeavesAsRadial()
+{
+ for ( int i = 0; i < numleafs; i++ )
+ {
+ dleafs[i].flags |= LEAF_FLAGS_RADIAL;
+ }
+}
+
+
+int ParseCommandLine( int argc, char **argv )
+{
+ int i;
+ for (i=1 ; i<argc ; i++)
+ {
+ if (!Q_stricmp(argv[i],"-threads"))
+ {
+ numthreads = atoi (argv[i+1]);
+ i++;
+ }
+ else if (!Q_stricmp(argv[i], "-fast"))
+ {
+ Msg ("fastvis = true\n");
+ fastvis = true;
+ }
+ else if (!Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose"))
+ {
+ Msg ("verbose = true\n");
+ verbose = true;
+ }
+ else if( !Q_stricmp( argv[i], "-radius_override" ) )
+ {
+ g_bUseRadius = true;
+ g_VisRadius = atof( argv[i+1] );
+ i++;
+ Msg( "Vis Radius = %4.2f\n", g_VisRadius );
+ g_VisRadius = g_VisRadius * g_VisRadius; // so distance check can be squared
+ }
+ else if( !Q_stricmp( argv[i], "-trace" ) )
+ {
+ g_TraceClusterStart = atoi( argv[i+1] );
+ i++;
+ g_TraceClusterStop = atoi( argv[i+1] );
+ i++;
+ Msg( "Tracing vis from cluster %d to %d\n", g_TraceClusterStart, g_TraceClusterStop );
+ }
+ else if (!Q_stricmp (argv[i],"-nosort"))
+ {
+ Msg ("nosort = true\n");
+ nosort = true;
+ }
+ else if (!Q_stricmp (argv[i],"-tmpin"))
+ strcpy (inbase, "/tmp");
+ else if( !Q_stricmp( argv[i], "-low" ) )
+ {
+ g_bLowPriority = true;
+ }
+ else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
+ {
+ EnableFullMinidumps( true );
+ }
+ else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
+ {
+ }
+ else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
+ {
+ ++i;
+ }
+ else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
+ {
+ // nothing to do here, but don't bail on this option
+ }
+ // NOTE: the -mpi checks must come last here because they allow the previous argument
+ // to be -mpi as well. If it game before something else like -game, then if the previous
+ // argument was -mpi and the current argument was something valid like -game, it would skip it.
+ else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) )
+ {
+ if ( stricmp( argv[i], "-mpi" ) == 0 )
+ g_bUseMPI = true;
+
+ // Any other args that start with -mpi are ok too.
+ if ( i == argc - 1 )
+ break;
+ }
+ else if (argv[i][0] == '-')
+ {
+ Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
+ i = 100000; // force it to print the usage
+ break;
+ }
+ else
+ break;
+ }
+ return i;
+}
+
+
+void PrintCommandLine( int argc, char **argv )
+{
+ Warning( "Command line: " );
+ for ( int z=0; z < argc; z++ )
+ {
+ Warning( "\"%s\" ", argv[z] );
+ }
+ Warning( "\n\n" );
+}
+
+
+void PrintUsage( int argc, char **argv )
+{
+ PrintCommandLine( argc, argv );
+
+ Warning(
+ "usage : vvis [options...] bspfile\n"
+ "example: vvis -fast c:\\hl2\\hl2\\maps\\test\n"
+ "\n"
+ "Common options:\n"
+ "\n"
+ " -v (or -verbose): Turn on verbose output (also shows more command\n"
+ " -fast : Only do first quick pass on vis calculations.\n"
+ " -mpi : Use VMPI to distribute computations.\n"
+ " -low : Run as an idle-priority process.\n"
+ " env_fog_controller specifies one.\n"
+ "\n"
+ " -vproject <directory> : Override the VPROJECT environment variable.\n"
+ " -game <directory> : Same as -vproject.\n"
+ "\n"
+ "Other options:\n"
+ " -novconfig : Don't bring up graphical UI on vproject errors.\n"
+ " -radius_override: Force a vis radius, regardless of whether an\n"
+ " -mpi_pw <pw> : Use a password to choose a specific set of VMPI workers.\n"
+ " -threads : Control the number of threads vbsp uses (defaults to the #\n"
+ " or processors on your machine).\n"
+ " -nosort : Don't sort portals (sorting is an optimization).\n"
+ " -tmpin : Make portals come from \\tmp\\<mapname>.\n"
+ " -tmpout : Make portals come from \\tmp\\<mapname>.\n"
+ " -trace <start cluster> <end cluster> : Writes a linefile that traces the vis from one cluster to another for debugging map vis.\n"
+ " -FullMinidumps : Write large minidumps on crash.\n"
+ " -x360 : Generate Xbox360 version of vsp\n"
+ " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
+ "\n"
+#if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users.
+ );
+#else
+ " -mpi_ListParams : Show a list of VMPI parameters.\n"
+ "\n"
+ );
+
+ // Show VMPI parameters?
+ for ( int i=1; i < argc; i++ )
+ {
+ if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 )
+ {
+ Warning( "VMPI-specific options:\n\n" );
+
+ bool bIsSDKMode = VMPI_IsSDKMode();
+ for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ )
+ {
+ if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode )
+ continue;
+
+ Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) );
+ Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) );
+ Warning( "\n\n" );
+ }
+ break;
+ }
+ }
+#endif
+}
+
+
+int RunVVis( int argc, char **argv )
+{
+ char portalfile[1024];
+ char source[1024];
+ double start, end;
+
+
+ Msg( "Valve Software - vvis.exe (%s)\n", __DATE__ );
+
+ verbose = false;
+
+ Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) );
+ CmdLib_InitFileSystem( argv[ argc - 1 ] );
+
+ Q_FileBase( source, source, sizeof( source ) );
+
+ LoadCmdLineFromFile( argc, argv, source, "vvis" );
+ int i = ParseCommandLine( argc, argv );
+
+ // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames,
+ // so we prepend qdir here.
+ strcpy( source, ExpandPath( source ) );
+
+ if (i != argc - 1)
+ {
+ PrintUsage( argc, argv );
+ DeleteCmdLine( argc, argv );
+ CmdLib_Exit( 1 );
+ }
+
+ start = Plat_FloatTime();
+
+
+ if (!g_bUseMPI)
+ {
+ // Setup the logfile.
+ char logFile[512];
+ _snprintf( logFile, sizeof(logFile), "%s.log", source );
+ SetSpewFunctionLogFile( logFile );
+ }
+
+ // Run in the background?
+ if( g_bLowPriority )
+ {
+ SetLowPriority();
+ }
+
+ ThreadSetDefault ();
+
+ char targetPath[1024];
+ GetPlatformMapPath( source, targetPath, 0, 1024 );
+ Msg ("reading %s\n", targetPath);
+ LoadBSPFile (targetPath);
+ if (numnodes == 0 || numfaces == 0)
+ Error ("Empty map");
+ ParseEntities ();
+
+ // Check the VMF for a vis radius
+ if (!g_bUseRadius)
+ {
+ float flRadius = DetermineVisRadius( );
+ if (flRadius > 0.0f)
+ {
+ g_bUseRadius = true;
+ g_VisRadius = flRadius * flRadius;
+ }
+ }
+
+ if ( g_bUseRadius )
+ {
+ MarkLeavesAsRadial();
+ }
+
+ if ( inbase[0] == 0 )
+ {
+ strcpy( portalfile, source );
+ }
+ else
+ {
+ sprintf ( portalfile, "%s%s", inbase, argv[i] );
+ Q_StripExtension( portalfile, portalfile, sizeof( portalfile ) );
+ }
+ strcat (portalfile, ".prt");
+
+ Msg ("reading %s\n", portalfile);
+ LoadPortals (portalfile);
+
+ // don't write out results when simply doing a trace
+ if ( g_TraceClusterStart < 0 )
+ {
+ CalcVis ();
+ CalcPAS ();
+
+ // We need a mapping from cluster to leaves, since the PVS
+ // deals with clusters for both CalcVisibleFogVolumes and
+ BuildClusterTable();
+
+ CalcVisibleFogVolumes();
+ CalcDistanceFromLeavesToWater();
+
+ visdatasize = vismap_p - dvisdata;
+ Msg ("visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize*2);
+
+ Msg ("writing %s\n", targetPath);
+ WriteBSPFile (targetPath);
+ }
+ else
+ {
+ if ( g_TraceClusterStart < 0 || g_TraceClusterStart >= portalclusters || g_TraceClusterStop < 0 || g_TraceClusterStop >= portalclusters )
+ {
+ Error("Invalid cluster trace: %d to %d, valid range is 0 to %d\n", g_TraceClusterStart, g_TraceClusterStop, portalclusters-1 );
+ }
+ if ( g_bUseMPI )
+ {
+ Warning("Can't compile trace in MPI mode\n");
+ }
+ CalcVisTrace ();
+ WritePortalTrace(source);
+ }
+
+ end = Plat_FloatTime();
+
+ char str[512];
+ GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
+ Msg( "%s elapsed\n", str );
+
+ ReleasePakFileLumps();
+ DeleteCmdLine( argc, argv );
+ CmdLib_Cleanup();
+ return 0;
+}
+
+
+/*
+===========
+main
+===========
+*/
+int main (int argc, char **argv)
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 1.0f, false, false, false, false );
+ InstallAllocationFunctions();
+ InstallSpewFunction();
+
+ VVIS_SetupMPI( argc, argv );
+
+ // Install an exception handler.
+ if ( g_bUseMPI && !g_bMPIMaster )
+ SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
+ else
+ SetupDefaultToolsMinidumpHandler();
+
+ return RunVVis( argc, argv );
+}
+
+
+// When VVIS is used as a DLL (makes debugging vmpi vvis a lot easier), this is used to
+// get it going.
+class CVVisDLL : public ILaunchableDLL
+{
+public:
+ virtual int main( int argc, char **argv )
+ {
+ return ::main( argc, argv );
+ }
+};
+
+EXPOSE_SINGLE_INTERFACE( CVVisDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION );