Counter Strike : Global Offensive Source Code
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.

1244 lines
30 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. // vis.c
  9. #include <windows.h>
  10. #include "vis.h"
  11. #include "threads.h"
  12. #include "stdlib.h"
  13. #include "pacifier.h"
  14. #include "vmpi.h"
  15. #include "mpivis.h"
  16. #include "tier1/strtools.h"
  17. #include "collisionutils.h"
  18. #include "tier0/icommandline.h"
  19. #include "vmpi_tools_shared.h"
  20. #include "ilaunchabledll.h"
  21. #include "tools_minidump.h"
  22. #include "loadcmdline.h"
  23. #include "byteswap.h"
  24. int g_numportals;
  25. int portalclusters;
  26. char inbase[32];
  27. portal_t *portals;
  28. leaf_t *leafs;
  29. int c_portaltest, c_portalpass, c_portalcheck;
  30. byte *uncompressedvis;
  31. byte *vismap, *vismap_p, *vismap_end; // past visfile
  32. int originalvismapsize;
  33. int leafbytes; // (portalclusters+63)>>3
  34. int leaflongs;
  35. int portalbytes, portallongs;
  36. bool fastvis;
  37. bool nosort;
  38. int totalvis;
  39. portal_t *sorted_portals[MAX_MAP_PORTALS*2];
  40. bool g_bUseRadius = false;
  41. double g_VisRadius = 4096.0f * 4096.0f;
  42. bool g_bLowPriority = false;
  43. //=============================================================================
  44. void PlaneFromWinding (winding_t *w, plane_t *plane)
  45. {
  46. Vector v1, v2;
  47. // calc plane
  48. VectorSubtract (w->points[2], w->points[1], v1);
  49. VectorSubtract (w->points[0], w->points[1], v2);
  50. CrossProduct (v2, v1, plane->normal);
  51. VectorNormalize (plane->normal);
  52. plane->dist = DotProduct (w->points[0], plane->normal);
  53. }
  54. /*
  55. ==================
  56. NewWinding
  57. ==================
  58. */
  59. winding_t *NewWinding (int points)
  60. {
  61. winding_t *w;
  62. int size;
  63. if (points > MAX_POINTS_ON_WINDING)
  64. Error ("NewWinding: %i points, max %d", points, MAX_POINTS_ON_WINDING);
  65. size = (int)(&((winding_t *)0)->points[points]);
  66. w = (winding_t*)malloc (size);
  67. memset (w, 0, size);
  68. return w;
  69. }
  70. void pw(winding_t *w)
  71. {
  72. int i;
  73. for (i=0 ; i<w->numpoints ; i++)
  74. Msg ("(%5.1f, %5.1f, %5.1f)\n",w->points[i][0], w->points[i][1],w->points[i][2]);
  75. }
  76. void prl(leaf_t *l)
  77. {
  78. int i;
  79. portal_t *p;
  80. plane_t pl;
  81. int count = l->portals.Count();
  82. for (i=0 ; i<count ; i++)
  83. {
  84. p = l->portals[i];
  85. pl = p->plane;
  86. 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]);
  87. }
  88. }
  89. //=============================================================================
  90. /*
  91. =============
  92. SortPortals
  93. Sorts the portals from the least complex, so the later ones can reuse
  94. the earlier information.
  95. =============
  96. */
  97. int PComp (const void *a, const void *b)
  98. {
  99. if ( (*(portal_t **)a)->nummightsee == (*(portal_t **)b)->nummightsee)
  100. return 0;
  101. if ( (*(portal_t **)a)->nummightsee < (*(portal_t **)b)->nummightsee)
  102. return -1;
  103. return 1;
  104. }
  105. void BuildTracePortals( int clusterStart )
  106. {
  107. leaf_t *leaf = &leafs[g_TraceClusterStart];
  108. g_numportals = leaf->portals.Count();
  109. for ( int i = 0; i < g_numportals; i++ )
  110. {
  111. sorted_portals[i] = leaf->portals[i];
  112. }
  113. }
  114. void SortPortals (void)
  115. {
  116. int i;
  117. for (i=0 ; i<g_numportals*2 ; i++)
  118. sorted_portals[i] = &portals[i];
  119. if (nosort)
  120. return;
  121. qsort (sorted_portals, g_numportals*2, sizeof(sorted_portals[0]), PComp);
  122. }
  123. /*
  124. ==============
  125. LeafVectorFromPortalVector
  126. ==============
  127. */
  128. int LeafVectorFromPortalVector (byte *portalbits, byte *leafbits)
  129. {
  130. int i;
  131. portal_t *p;
  132. int c_leafs;
  133. memset (leafbits, 0, leafbytes);
  134. for (i=0 ; i<g_numportals*2 ; i++)
  135. {
  136. if ( CheckBit( portalbits, i ) )
  137. {
  138. p = portals+i;
  139. SetBit( leafbits, p->leaf );
  140. }
  141. }
  142. c_leafs = CountBits (leafbits, portalclusters);
  143. return c_leafs;
  144. }
  145. /*
  146. ===============
  147. ClusterMerge
  148. Merges the portal visibility for a leaf
  149. ===============
  150. */
  151. void ClusterMerge (int clusternum)
  152. {
  153. leaf_t *leaf;
  154. // byte portalvector[MAX_PORTALS/8];
  155. byte portalvector[MAX_PORTALS/4]; // 4 because portal bytes is * 2
  156. byte uncompressed[MAX_MAP_LEAFS/8];
  157. int i, j;
  158. int numvis;
  159. portal_t *p;
  160. int pnum;
  161. // OR together all the portalvis bits
  162. memset (portalvector, 0, portalbytes);
  163. leaf = &leafs[clusternum];
  164. for (i=0 ; i < leaf->portals.Count(); i++)
  165. {
  166. p = leaf->portals[i];
  167. if (p->status != stat_done)
  168. Error ("portal not done %d %d %d\n", i, p, portals);
  169. for (j=0 ; j<portallongs ; j++)
  170. ((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
  171. pnum = p - portals;
  172. SetBit( portalvector, pnum );
  173. }
  174. // convert portal bits to leaf bits
  175. numvis = LeafVectorFromPortalVector (portalvector, uncompressed);
  176. #if 0
  177. // func_viscluster makes this happen all the time because it allows a non-convex set of portals
  178. // My analysis says this is ok, but it does make this check for errors in vis kind of useless
  179. if ( CheckBit( uncompressed, clusternum ) )
  180. Warning("WARNING: Cluster portals saw into cluster\n");
  181. #endif
  182. SetBit( uncompressed, clusternum );
  183. numvis++; // count the leaf itself
  184. // save uncompressed for PHS calculation
  185. memcpy (uncompressedvis + clusternum*leafbytes, uncompressed, leafbytes);
  186. qprintf ("cluster %4i : %4i visible\n", clusternum, numvis);
  187. totalvis += numvis;
  188. }
  189. static int CompressAndCrosscheckClusterVis( int clusternum )
  190. {
  191. int optimized = 0;
  192. byte compressed[MAX_MAP_LEAFS/8];
  193. //
  194. // compress the bit string
  195. //
  196. byte *uncompressed = uncompressedvis + clusternum*leafbytes;
  197. for ( int i = 0; i < portalclusters; i++ )
  198. {
  199. if ( i == clusternum )
  200. continue;
  201. if ( CheckBit( uncompressed, i ) )
  202. {
  203. byte *other = uncompressedvis + i*leafbytes;
  204. if ( !CheckBit( other, clusternum ) )
  205. {
  206. ClearBit( uncompressed, i );
  207. optimized++;
  208. }
  209. }
  210. }
  211. int numbytes = CompressVis( uncompressed, compressed );
  212. byte *dest = vismap_p;
  213. vismap_p += numbytes;
  214. if (vismap_p > vismap_end)
  215. Error ("Vismap expansion overflow");
  216. dvis->bitofs[clusternum][DVIS_PVS] = dest-vismap;
  217. memcpy( dest, compressed, numbytes );
  218. // check vis data
  219. DecompressVis( vismap + dvis->bitofs[clusternum][DVIS_PVS], compressed );
  220. return optimized;
  221. }
  222. /*
  223. ==================
  224. CalcPortalVis
  225. ==================
  226. */
  227. void CalcPortalVis (void)
  228. {
  229. int i;
  230. // fastvis just uses mightsee for a very loose bound
  231. if( fastvis )
  232. {
  233. for (i=0 ; i<g_numportals*2 ; i++)
  234. {
  235. portals[i].portalvis = portals[i].portalflood;
  236. portals[i].status = stat_done;
  237. }
  238. return;
  239. }
  240. if (g_bUseMPI)
  241. {
  242. RunMPIPortalFlow();
  243. }
  244. else
  245. {
  246. RunThreadsOnIndividual (g_numportals*2, true, PortalFlow);
  247. }
  248. }
  249. void CalcVisTrace (void)
  250. {
  251. RunThreadsOnIndividual (g_numportals*2, true, BasePortalVis);
  252. BuildTracePortals( g_TraceClusterStart );
  253. // NOTE: We only schedule the one-way portals out of the start cluster here
  254. // so don't run g_numportals*2 in this case
  255. RunThreadsOnIndividual (g_numportals, true, PortalFlow);
  256. }
  257. /*
  258. ==================
  259. CalcVis
  260. ==================
  261. */
  262. void CalcVis (void)
  263. {
  264. int i;
  265. if (g_bUseMPI)
  266. {
  267. RunMPIBasePortalVis();
  268. }
  269. else
  270. {
  271. RunThreadsOnIndividual (g_numportals*2, true, BasePortalVis);
  272. }
  273. SortPortals ();
  274. CalcPortalVis ();
  275. //
  276. // assemble the leaf vis lists by oring the portal lists
  277. //
  278. for ( i = 0; i < portalclusters; i++ )
  279. {
  280. ClusterMerge( i );
  281. }
  282. int count = 0;
  283. // Now crosscheck each leaf's vis and compress
  284. for ( i = 0; i < portalclusters; i++ )
  285. {
  286. count += CompressAndCrosscheckClusterVis( i );
  287. }
  288. Msg ("Optimized: %d visible clusters (%.2f%%)\n", count, totalvis, count*100/totalvis);
  289. Msg ("Total clusters visible: %i\n", totalvis);
  290. Msg ("Average clusters visible: %i\n", totalvis / portalclusters);
  291. }
  292. void SetPortalSphere (portal_t *p)
  293. {
  294. int i;
  295. Vector total, dist;
  296. winding_t *w;
  297. float r, bestr;
  298. w = p->winding;
  299. VectorCopy (vec3_origin, total);
  300. for (i=0 ; i<w->numpoints ; i++)
  301. {
  302. VectorAdd (total, w->points[i], total);
  303. }
  304. for (i=0 ; i<3 ; i++)
  305. total[i] /= w->numpoints;
  306. bestr = 0;
  307. for (i=0 ; i<w->numpoints ; i++)
  308. {
  309. VectorSubtract (w->points[i], total, dist);
  310. r = VectorLength (dist);
  311. if (r > bestr)
  312. bestr = r;
  313. }
  314. VectorCopy (total, p->origin);
  315. p->radius = bestr;
  316. }
  317. /*
  318. ============
  319. LoadPortals
  320. ============
  321. */
  322. void LoadPortals (char *name)
  323. {
  324. int i, j;
  325. portal_t *p;
  326. leaf_t *l;
  327. char magic[80];
  328. int numpoints;
  329. winding_t *w;
  330. int leafnums[2];
  331. plane_t plane;
  332. FILE *f;
  333. // Open the portal file.
  334. if ( g_bUseMPI )
  335. {
  336. // If we're using MPI, copy off the file to a temporary first. This will download the file
  337. // from the MPI master, then we get to use nice functions like fscanf on it.
  338. char tempPath[MAX_PATH], tempFile[MAX_PATH];
  339. if ( GetTempPath( sizeof( tempPath ), tempPath ) == 0 )
  340. {
  341. Error( "LoadPortals: GetTempPath failed.\n" );
  342. }
  343. if ( GetTempFileName( tempPath, "vvis_portal_", 0, tempFile ) == 0 )
  344. {
  345. Error( "LoadPortals: GetTempFileName failed.\n" );
  346. }
  347. // Read all the data from the network file into memory.
  348. FileHandle_t hFile = g_pFileSystem->Open(name, "r");
  349. if ( hFile == FILESYSTEM_INVALID_HANDLE )
  350. Error( "LoadPortals( %s ): couldn't get file from master.\n", name );
  351. CUtlVector<char> data;
  352. data.SetSize( g_pFileSystem->Size( hFile ) );
  353. g_pFileSystem->Read( data.Base(), data.Count(), hFile );
  354. g_pFileSystem->Close( hFile );
  355. // Dump it into a temp file.
  356. f = fopen( tempFile, "wt" );
  357. fwrite( data.Base(), 1, data.Count(), f );
  358. fclose( f );
  359. // Open the temp file up.
  360. f = fopen( tempFile, "rSTD" ); // read only, sequential, temporary, delete on close
  361. }
  362. else
  363. {
  364. f = fopen( name, "r" );
  365. }
  366. if ( !f )
  367. Error ("LoadPortals: couldn't read %s\n",name);
  368. if (fscanf (f,"%79s\n%i\n%i\n",magic, &portalclusters, &g_numportals) != 3)
  369. Error ("LoadPortals %s: failed to read header", name);
  370. if (stricmp(magic,PORTALFILE))
  371. Error ("LoadPortals %s: not a portal file", name);
  372. Msg ("%4i portalclusters\n", portalclusters);
  373. Msg ("%4i numportals\n", g_numportals);
  374. if (g_numportals * 2 >= MAX_PORTALS)
  375. {
  376. Error("The map overflows the max portal count (%d of max %d)!\n", g_numportals, MAX_PORTALS / 2 );
  377. }
  378. // these counts should take advantage of 64 bit systems automatically
  379. leafbytes = ((portalclusters+63)&~63)>>3;
  380. leaflongs = leafbytes/sizeof(long);
  381. portalbytes = ((g_numportals*2+63)&~63)>>3;
  382. portallongs = portalbytes/sizeof(long);
  383. // each file portal is split into two memory portals
  384. portals = (portal_t*)malloc(2*g_numportals*sizeof(portal_t));
  385. memset (portals, 0, 2*g_numportals*sizeof(portal_t));
  386. leafs = (leaf_t*)malloc(portalclusters*sizeof(leaf_t));
  387. memset (leafs, 0, portalclusters*sizeof(leaf_t));
  388. originalvismapsize = portalclusters*leafbytes;
  389. uncompressedvis = (byte*)malloc(originalvismapsize);
  390. vismap = vismap_p = dvisdata;
  391. dvis->numclusters = portalclusters;
  392. vismap_p = (byte *)&dvis->bitofs[portalclusters];
  393. vismap_end = vismap + MAX_MAP_VISIBILITY;
  394. for (i=0, p=portals ; i<g_numportals ; i++)
  395. {
  396. if (fscanf (f, "%i %i %i ", &numpoints, &leafnums[0], &leafnums[1])
  397. != 3)
  398. Error ("LoadPortals: reading portal %i", i);
  399. if (numpoints > MAX_POINTS_ON_WINDING)
  400. Error ("LoadPortals: portal %i has too many points", i);
  401. if ( (unsigned)leafnums[0] > portalclusters
  402. || (unsigned)leafnums[1] > portalclusters)
  403. Error ("LoadPortals: reading portal %i", i);
  404. w = p->winding = NewWinding (numpoints);
  405. w->original = true;
  406. w->numpoints = numpoints;
  407. for (j=0 ; j<numpoints ; j++)
  408. {
  409. double v[3];
  410. int k;
  411. // scanf into double, then assign to vec_t
  412. // so we don't care what size vec_t is
  413. if (fscanf (f, "(%lf %lf %lf ) "
  414. , &v[0], &v[1], &v[2]) != 3)
  415. Error ("LoadPortals: reading portal %i", i);
  416. for (k=0 ; k<3 ; k++)
  417. w->points[j][k] = v[k];
  418. }
  419. fscanf (f, "\n");
  420. // calc plane
  421. PlaneFromWinding (w, &plane);
  422. // create forward portal
  423. l = &leafs[leafnums[0]];
  424. l->portals.AddToTail(p);
  425. p->winding = w;
  426. VectorSubtract (vec3_origin, plane.normal, p->plane.normal);
  427. p->plane.dist = -plane.dist;
  428. p->leaf = leafnums[1];
  429. SetPortalSphere (p);
  430. p++;
  431. // create backwards portal
  432. l = &leafs[leafnums[1]];
  433. l->portals.AddToTail(p);
  434. p->winding = NewWinding(w->numpoints);
  435. p->winding->numpoints = w->numpoints;
  436. for (j=0 ; j<w->numpoints ; j++)
  437. {
  438. VectorCopy (w->points[w->numpoints-1-j], p->winding->points[j]);
  439. }
  440. p->plane = plane;
  441. p->leaf = leafnums[0];
  442. SetPortalSphere (p);
  443. p++;
  444. }
  445. fclose (f);
  446. }
  447. /*
  448. ================
  449. CalcPAS
  450. Calculate the PAS (Potentially Audible Set)
  451. by ORing together all the PVS visible from a leaf
  452. ================
  453. */
  454. void CalcPAS (void)
  455. {
  456. int i, j, k, l, index;
  457. int bitbyte;
  458. long *dest, *src;
  459. byte *scan;
  460. int count;
  461. byte uncompressed[MAX_MAP_LEAFS/8];
  462. byte compressed[MAX_MAP_LEAFS/8];
  463. Msg ("Building PAS...\n");
  464. count = 0;
  465. for (i=0 ; i<portalclusters ; i++)
  466. {
  467. scan = uncompressedvis + i*leafbytes;
  468. memcpy (uncompressed, scan, leafbytes);
  469. for (j=0 ; j<leafbytes ; j++)
  470. {
  471. bitbyte = scan[j];
  472. if (!bitbyte)
  473. continue;
  474. for (k=0 ; k<8 ; k++)
  475. {
  476. if (! (bitbyte & (1<<k)) )
  477. continue;
  478. // OR this pvs row into the phs
  479. index = ((j<<3)+k);
  480. if (index >= portalclusters)
  481. Error ("Bad bit in PVS"); // pad bits should be 0
  482. src = (long *)(uncompressedvis + index*leafbytes);
  483. dest = (long *)uncompressed;
  484. for (l=0 ; l<leaflongs ; l++)
  485. ((long *)uncompressed)[l] |= src[l];
  486. }
  487. }
  488. for (j=0 ; j<portalclusters ; j++)
  489. {
  490. if ( CheckBit( uncompressed, j ) )
  491. {
  492. count++;
  493. }
  494. }
  495. //
  496. // compress the bit string
  497. //
  498. j = CompressVis (uncompressed, compressed);
  499. dest = (long *)vismap_p;
  500. vismap_p += j;
  501. if (vismap_p > vismap_end)
  502. Error ("Vismap expansion overflow");
  503. dvis->bitofs[i][DVIS_PAS] = (byte *)dest-vismap;
  504. memcpy (dest, compressed, j);
  505. }
  506. Msg ("Average clusters audible: %i\n", count/portalclusters);
  507. }
  508. static void GetBoundsForFace( int faceID, Vector &faceMin, Vector &faceMax )
  509. {
  510. ClearBounds( faceMin, faceMax );
  511. dface_t *pFace = &dfaces[faceID];
  512. int i;
  513. for( i = pFace->firstedge; i < pFace->firstedge + pFace->numedges; i++ )
  514. {
  515. int edgeID = dsurfedges[i];
  516. if( edgeID < 0 )
  517. {
  518. edgeID = -edgeID;
  519. }
  520. dedge_t *pEdge = &dedges[edgeID];
  521. dvertex_t *pVert0 = &dvertexes[pEdge->v[0]];
  522. dvertex_t *pVert1 = &dvertexes[pEdge->v[1]];
  523. AddPointToBounds( pVert0->point, faceMin, faceMax );
  524. AddPointToBounds( pVert1->point, faceMin, faceMax );
  525. }
  526. }
  527. // FIXME: should stick this in mathlib
  528. static float GetMinDistanceBetweenBoundingBoxes( const Vector &min1, const Vector &max1,
  529. const Vector &min2, const Vector &max2 )
  530. {
  531. if( IsBoxIntersectingBox( min1, max1, min2, max2 ) )
  532. {
  533. return 0.0f;
  534. }
  535. Vector axisDist;
  536. int i;
  537. for( i = 0; i < 3; i++ )
  538. {
  539. if( min1[i] <= max2[i] && max1[i] >= min2[i] )
  540. {
  541. // the intersection in this dimension.
  542. axisDist[i] = 0.0f;
  543. }
  544. else
  545. {
  546. float dist1, dist2;
  547. dist1 = min1[i] - max2[i];
  548. dist2 = min2[i] - max1[i];
  549. axisDist[i] = dist1 > dist2 ? dist1 : dist2;
  550. Assert( axisDist[i] > 0.0f );
  551. }
  552. }
  553. float mag = axisDist.Length();
  554. Assert( mag > 0.0f );
  555. return mag;
  556. }
  557. static float CalcDistanceFromLeafToWater( int leafNum )
  558. {
  559. byte uncompressed[MAX_MAP_LEAFS/8];
  560. int j, k;
  561. // If we know that this one doesn't see a water surface then don't bother doing anything.
  562. if( ((dleafs[leafNum].contents & CONTENTS_TESTFOGVOLUME) == 0) && ( dleafs[leafNum].leafWaterDataID == -1 ) )
  563. return 65535; // FIXME: make a define for this.
  564. // First get the vis data..
  565. int cluster = dleafs[leafNum].cluster;
  566. if (cluster < 0)
  567. return 65535; // FIXME: make a define for this.
  568. DecompressVis( &dvisdata[dvis->bitofs[cluster][DVIS_PVS]], uncompressed );
  569. float minDist = 65535.0f; // FIXME: make a define for this.
  570. Vector leafMin, leafMax;
  571. leafMin[0] = ( float )dleafs[leafNum].mins[0];
  572. leafMin[1] = ( float )dleafs[leafNum].mins[1];
  573. leafMin[2] = ( float )dleafs[leafNum].mins[2];
  574. leafMax[0] = ( float )dleafs[leafNum].maxs[0];
  575. leafMax[1] = ( float )dleafs[leafNum].maxs[1];
  576. leafMax[2] = ( float )dleafs[leafNum].maxs[2];
  577. /*
  578. CUtlVector<listplane_t> temp;
  579. // build a convex solid out of the planes so that we can get at the triangles.
  580. for( j = dleafs[i].firstleafbrush; j < dleafs[i].firstleafbrush + dleafs[i].numleafbrushes; j++ )
  581. {
  582. dbrush_t *pBrush = &dbrushes[j];
  583. for( k = pBrush->firstside; k < pBrush->firstside + pBrush->numsides; k++ )
  584. {
  585. dbrushside_t *pside = dbrushsides + k;
  586. dplane_t *pplane = dplanes + pside->planenum;
  587. AddListPlane( &temp, pplane->normal[0], pplane->normal[1], pplane->normal[2], pplane->dist );
  588. }
  589. CPhysConvex *pConvex = physcollision->ConvexFromPlanes( (float *)temp.Base(), temp.Count(), VPHYSICS_MERGE );
  590. ConvertConvexToCollide( &pConvex,
  591. temp.RemoveAll();
  592. }
  593. */
  594. // Iterate over all potentially visible clusters from this leaf
  595. for (j = 0; j < dvis->numclusters; ++j)
  596. {
  597. // Don't need to bother if this is the same as the current cluster
  598. if (j == cluster)
  599. continue;
  600. // If the cluster isn't in our current pvs, then get out of here.
  601. if ( !CheckBit( uncompressed, j ) )
  602. continue;
  603. // Found a visible cluster, now iterate over all leaves
  604. // inside that cluster
  605. for (k = 0; k < g_ClusterLeaves[j].leafCount; ++k)
  606. {
  607. int nClusterLeaf = g_ClusterLeaves[j].leafs[k];
  608. // Don't bother testing the ones that don't see a water boundary.
  609. if( ((dleafs[nClusterLeaf].contents & CONTENTS_TESTFOGVOLUME) == 0) && ( dleafs[nClusterLeaf].leafWaterDataID == -1 ) )
  610. continue;
  611. // Find the minimum distance between each surface on the boundary of the leaf
  612. // that we have the pvs for and each water surface in the leaf that we are testing.
  613. int nFirstFaceID = dleafs[nClusterLeaf].firstleafface;
  614. for( int leafFaceID = 0; leafFaceID < dleafs[nClusterLeaf].numleaffaces; ++leafFaceID )
  615. {
  616. int faceID = dleaffaces[nFirstFaceID + leafFaceID];
  617. dface_t *pFace = &dfaces[faceID];
  618. if( pFace->texinfo == -1 )
  619. continue;
  620. texinfo_t *pTexInfo = &texinfo[pFace->texinfo];
  621. if( pTexInfo->flags & SURF_WARP )
  622. {
  623. // Woo hoo!!! We found a water face.
  624. // compare the bounding box of the face with the bounding
  625. // box of the leaf that we are looking from and see
  626. // what the closest distance is.
  627. // FIXME: this could be a face/face distance between the water
  628. // face and the bounding volume of the leaf.
  629. // Get the bounding box of the face
  630. Vector faceMin, faceMax;
  631. GetBoundsForFace( faceID, faceMin, faceMax );
  632. float dist = GetMinDistanceBetweenBoundingBoxes( leafMin, leafMax, faceMin, faceMax );
  633. if( dist < minDist )
  634. {
  635. minDist = dist;
  636. }
  637. }
  638. }
  639. }
  640. }
  641. return minDist;
  642. }
  643. static void CalcDistanceFromLeavesToWater( void )
  644. {
  645. int i;
  646. for( i = 0; i < numleafs; i++ )
  647. {
  648. g_LeafMinDistToWater[i] = ( unsigned short )CalcDistanceFromLeafToWater( i );
  649. }
  650. }
  651. //-----------------------------------------------------------------------------
  652. // Using the PVS, compute the visible fog volumes from each leaf
  653. //-----------------------------------------------------------------------------
  654. static void CalcVisibleFogVolumes()
  655. {
  656. byte uncompressed[MAX_MAP_LEAFS/8];
  657. int i, j, k;
  658. // Clear the contents flags for water testing
  659. for (i = 0; i < numleafs; ++i)
  660. {
  661. dleafs[i].contents &= ~CONTENTS_TESTFOGVOLUME;
  662. g_LeafMinDistToWater[i] = 65535;
  663. }
  664. for (i = 0; i < numleafs; ++i)
  665. {
  666. // If we've already discovered that this leaf needs testing,
  667. // no need to go through the work again...
  668. if (dleafs[i].contents & CONTENTS_TESTFOGVOLUME)
  669. {
  670. Assert((dleafs[i].contents & (CONTENTS_SLIME | CONTENTS_WATER)) == 0);
  671. continue;
  672. }
  673. // Don't bother checking fog volumes from solid leaves
  674. if (dleafs[i].contents & CONTENTS_SOLID)
  675. continue;
  676. // Look only for leaves which are visible from leaves that have fluid in them.
  677. if ( dleafs[i].leafWaterDataID == -1 )
  678. continue;
  679. // Don't bother about looking from CONTENTS_SLIME; we're not going to treat that as interesting.
  680. // because slime is opaque
  681. if ( dleafs[i].contents & CONTENTS_SLIME )
  682. continue;
  683. // First get the vis data..
  684. int cluster = dleafs[i].cluster;
  685. if (cluster < 0)
  686. continue;
  687. DecompressVis( &dvisdata[dvis->bitofs[cluster][DVIS_PVS]], uncompressed );
  688. // Iterate over all potentially visible clusters from this leaf
  689. for (j = 0; j < dvis->numclusters; ++j)
  690. {
  691. // Don't need to bother if this is the same as the current cluster
  692. if (j == cluster)
  693. continue;
  694. if ( !CheckBit( uncompressed, j ) )
  695. continue;
  696. // Found a visible cluster, now iterate over all leaves
  697. // inside that cluster
  698. for (k = 0; k < g_ClusterLeaves[j].leafCount; ++k)
  699. {
  700. int nClusterLeaf = g_ClusterLeaves[j].leafs[k];
  701. // Don't bother checking fog volumes from solid leaves
  702. if ( dleafs[nClusterLeaf].contents & CONTENTS_SOLID )
  703. continue;
  704. // Don't bother checking from any leaf that's got fluid in it
  705. if ( dleafs[nClusterLeaf].leafWaterDataID != -1 )
  706. continue;
  707. // Here, we've found a case where a non-liquid leaf is visible from a liquid leaf
  708. // So, in this case, we have to do the expensive test during rendering.
  709. dleafs[nClusterLeaf].contents |= CONTENTS_TESTFOGVOLUME;
  710. }
  711. }
  712. }
  713. }
  714. //-----------------------------------------------------------------------------
  715. // Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues
  716. //-----------------------------------------------------------------------------
  717. float DetermineVisRadius( )
  718. {
  719. float flRadius = -1;
  720. // Check the max vis range to determine the vis radius
  721. for (int i = 0; i < num_entities; ++i)
  722. {
  723. char* pEntity = ValueForKey(&entities[i], "classname");
  724. if (!stricmp(pEntity, "env_fog_controller"))
  725. {
  726. flRadius = FloatForKey (&entities[i], "farz");
  727. if (flRadius == 0.0f)
  728. flRadius = -1.0f;
  729. break;
  730. }
  731. }
  732. return flRadius;
  733. }
  734. void MarkLeavesAsRadial()
  735. {
  736. for ( int i = 0; i < numleafs; i++ )
  737. {
  738. dleafs[i].flags |= LEAF_FLAGS_RADIAL;
  739. }
  740. }
  741. int ParseCommandLine( int argc, char **argv )
  742. {
  743. int i;
  744. for (i=1 ; i<argc ; i++)
  745. {
  746. if (!Q_stricmp(argv[i],"-threads"))
  747. {
  748. numthreads = atoi (argv[i+1]);
  749. i++;
  750. }
  751. else if (!Q_stricmp(argv[i], "-fast"))
  752. {
  753. Msg ("fastvis = true\n");
  754. fastvis = true;
  755. }
  756. else if (!Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose"))
  757. {
  758. Msg ("verbose = true\n");
  759. verbose = true;
  760. }
  761. else if( !Q_stricmp( argv[i], "-radius_override" ) )
  762. {
  763. g_bUseRadius = true;
  764. g_VisRadius = atof( argv[i+1] );
  765. i++;
  766. Msg( "Vis Radius = %4.2f\n", g_VisRadius );
  767. g_VisRadius = g_VisRadius * g_VisRadius; // so distance check can be squared
  768. }
  769. else if( !Q_stricmp( argv[i], "-trace" ) )
  770. {
  771. g_TraceClusterStart = atoi( argv[i+1] );
  772. i++;
  773. g_TraceClusterStop = atoi( argv[i+1] );
  774. i++;
  775. Msg( "Tracing vis from cluster %d to %d\n", g_TraceClusterStart, g_TraceClusterStop );
  776. }
  777. else if (!Q_stricmp (argv[i],"-nosort"))
  778. {
  779. Msg ("nosort = true\n");
  780. nosort = true;
  781. }
  782. else if (!Q_stricmp (argv[i],"-tmpin"))
  783. strcpy (inbase, "/tmp");
  784. else if( !Q_stricmp( argv[i], "-low" ) )
  785. {
  786. g_bLowPriority = true;
  787. }
  788. else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
  789. {
  790. EnableFullMinidumps( true );
  791. }
  792. else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
  793. {
  794. }
  795. else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
  796. {
  797. ++i;
  798. }
  799. else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
  800. {
  801. // nothing to do here, but don't bail on this option
  802. }
  803. // NOTE: the -mpi checks must come last here because they allow the previous argument
  804. // to be -mpi as well. If it game before something else like -game, then if the previous
  805. // argument was -mpi and the current argument was something valid like -game, it would skip it.
  806. else if ( !Q_strncasecmp( argv[i], "-mpi", 4 ) || !Q_strncasecmp( argv[i-1], "-mpi", 4 ) )
  807. {
  808. if ( stricmp( argv[i], "-mpi" ) == 0 )
  809. g_bUseMPI = true;
  810. // Any other args that start with -mpi are ok too.
  811. if ( i == argc - 1 )
  812. break;
  813. }
  814. else if ( !Q_stricmp( argv[i], "-tempcontent" ) )
  815. {
  816. // ... Do nothing, just let this pass to the filesystem
  817. }
  818. else if (argv[i][0] == '-')
  819. {
  820. Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
  821. i = 100000; // force it to print the usage
  822. break;
  823. }
  824. else
  825. break;
  826. }
  827. return i;
  828. }
  829. void PrintCommandLine( int argc, char **argv )
  830. {
  831. Warning( "Command line: " );
  832. for ( int z=0; z < argc; z++ )
  833. {
  834. Warning( "\"%s\" ", argv[z] );
  835. }
  836. Warning( "\n\n" );
  837. }
  838. void PrintUsage( int argc, char **argv )
  839. {
  840. PrintCommandLine( argc, argv );
  841. Warning(
  842. "usage : vvis [options...] bspfile\n"
  843. "example: vvis -fast c:\\hl2\\hl2\\maps\\test\n"
  844. "\n"
  845. "Common options:\n"
  846. "\n"
  847. " -v (or -verbose): Turn on verbose output (also shows more command\n"
  848. " -fast : Only do first quick pass on vis calculations.\n"
  849. " -mpi : Use VMPI to distribute computations.\n"
  850. " -low : Run as an idle-priority process.\n"
  851. " env_fog_controller specifies one.\n"
  852. "\n"
  853. " -vproject <directory> : Override the VPROJECT environment variable.\n"
  854. " -game <directory> : Same as -vproject.\n"
  855. "\n"
  856. "Other options:\n"
  857. " -novconfig : Don't bring up graphical UI on vproject errors.\n"
  858. " -radius_override: Force a vis radius, regardless of whether an\n"
  859. " -mpi_pw <pw> : Use a password to choose a specific set of VMPI workers.\n"
  860. " -threads : Control the number of threads vbsp uses (defaults to the #\n"
  861. " or processors on your machine).\n"
  862. " -nosort : Don't sort portals (sorting is an optimization).\n"
  863. " -tmpin : Make portals come from \\tmp\\<mapname>.\n"
  864. " -tmpout : Make portals come from \\tmp\\<mapname>.\n"
  865. " -trace <start cluster> <end cluster> : Writes a linefile that traces the vis from one cluster to another for debugging map vis.\n"
  866. " -FullMinidumps : Write large minidumps on crash.\n"
  867. " -x360 : Generate Xbox360 version of vsp\n"
  868. " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
  869. "\n"
  870. #if 1 // Disabled for the initial SDK release with VMPI so we can get feedback from selected users.
  871. );
  872. #else
  873. " -mpi_ListParams : Show a list of VMPI parameters.\n"
  874. "\n"
  875. );
  876. // Show VMPI parameters?
  877. for ( int i=1; i < argc; i++ )
  878. {
  879. if ( V_stricmp( argv[i], "-mpi_ListParams" ) == 0 )
  880. {
  881. Warning( "VMPI-specific options:\n\n" );
  882. bool bIsSDKMode = VMPI_IsSDKMode();
  883. for ( int i=k_eVMPICmdLineParam_FirstParam+1; i < k_eVMPICmdLineParam_LastParam; i++ )
  884. {
  885. if ( (VMPI_GetParamFlags( (EVMPICmdLineParam)i ) & VMPI_PARAM_SDK_HIDDEN) && bIsSDKMode )
  886. continue;
  887. Warning( "[%s]\n", VMPI_GetParamString( (EVMPICmdLineParam)i ) );
  888. Warning( VMPI_GetParamHelpString( (EVMPICmdLineParam)i ) );
  889. Warning( "\n\n" );
  890. }
  891. break;
  892. }
  893. }
  894. #endif
  895. }
  896. int RunVVis( int argc, char **argv )
  897. {
  898. char portalfile[1024];
  899. char source[1024];
  900. double start, end;
  901. Msg( "Valve Software - vvis.exe (%s)\n", __DATE__ );
  902. verbose = false;
  903. Q_StripExtension( argv[ argc - 1 ], source, sizeof( source ) );
  904. CmdLib_InitFileSystem( argv[ argc - 1 ] );
  905. Q_FileBase( source, source, sizeof( source ) );
  906. LoadCmdLineFromFile( argc, argv, source, "vvis" );
  907. int i = ParseCommandLine( argc, argv );
  908. // This part is just for VMPI. VMPI's file system needs the basedir in front of all filenames,
  909. // so we prepend qdir here.
  910. strcpy( source, ExpandPath( source ) );
  911. if (i != argc - 1)
  912. {
  913. PrintUsage( argc, argv );
  914. DeleteCmdLine( argc, argv );
  915. Plat_ExitProcess( 0 );
  916. }
  917. start = Plat_FloatTime();
  918. if (!g_bUseMPI)
  919. {
  920. // Setup the logfile.
  921. char logFile[512];
  922. _snprintf( logFile, sizeof(logFile), "%s.log", source );
  923. g_CmdLibFileLoggingListener.Open( logFile );
  924. }
  925. // Run in the background?
  926. if( g_bLowPriority )
  927. {
  928. SetLowPriority();
  929. }
  930. ThreadSetDefault ();
  931. char targetPath[1024];
  932. GetPlatformMapPath( source, targetPath, 0, 1024 );
  933. Msg ("reading %s\n", targetPath);
  934. LoadBSPFile (targetPath);
  935. if (numnodes == 0 || numfaces == 0)
  936. Error ("Empty map");
  937. ParseEntities ();
  938. // Check the VMF for a vis radius
  939. if (!g_bUseRadius)
  940. {
  941. float flRadius = DetermineVisRadius( );
  942. if (flRadius > 0.0f)
  943. {
  944. g_bUseRadius = true;
  945. g_VisRadius = flRadius * flRadius;
  946. }
  947. }
  948. if ( g_bUseRadius )
  949. {
  950. MarkLeavesAsRadial();
  951. }
  952. if ( inbase[0] == 0 )
  953. {
  954. strcpy( portalfile, source );
  955. }
  956. else
  957. {
  958. sprintf ( portalfile, "%s%s", inbase, argv[i] );
  959. Q_StripExtension( portalfile, portalfile, sizeof( portalfile ) );
  960. }
  961. strcat (portalfile, ".prt");
  962. Msg ("reading %s\n", portalfile);
  963. LoadPortals (portalfile);
  964. // don't write out results when simply doing a trace
  965. if ( g_TraceClusterStart < 0 )
  966. {
  967. CalcVis ();
  968. CalcPAS ();
  969. // We need a mapping from cluster to leaves, since the PVS
  970. // deals with clusters for both CalcVisibleFogVolumes and
  971. BuildClusterTable();
  972. CalcVisibleFogVolumes();
  973. CalcDistanceFromLeavesToWater();
  974. visdatasize = vismap_p - dvisdata;
  975. Msg ("visdatasize:%i compressed from %i\n", visdatasize, originalvismapsize*2);
  976. Msg ("writing %s\n", targetPath);
  977. WriteBSPFile (targetPath);
  978. }
  979. else
  980. {
  981. if ( g_TraceClusterStart < 0 || g_TraceClusterStart >= portalclusters || g_TraceClusterStop < 0 || g_TraceClusterStop >= portalclusters )
  982. {
  983. Error("Invalid cluster trace: %d to %d, valid range is 0 to %d\n", g_TraceClusterStart, g_TraceClusterStop, portalclusters-1 );
  984. }
  985. if ( g_bUseMPI )
  986. {
  987. Warning("Can't compile trace in MPI mode\n");
  988. }
  989. CalcVisTrace ();
  990. WritePortalTrace(source);
  991. }
  992. end = Plat_FloatTime();
  993. char str[512];
  994. GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
  995. Msg( "%s elapsed\n", str );
  996. ReleasePakFileLumps();
  997. DeleteCmdLine( argc, argv );
  998. CmdLib_Cleanup();
  999. return 0;
  1000. }
  1001. /*
  1002. ===========
  1003. main
  1004. ===========
  1005. */
  1006. int main (int argc, char **argv)
  1007. {
  1008. CommandLine()->CreateCmdLine( argc, argv );
  1009. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  1010. InstallAllocationFunctions();
  1011. InstallSpewFunction();
  1012. VVIS_SetupMPI( argc, argv );
  1013. // Install an exception handler.
  1014. if ( g_bUseMPI && !g_bMPIMaster )
  1015. SetupToolsMinidumpHandler( VMPI_ExceptionFilter );
  1016. else
  1017. SetupDefaultToolsMinidumpHandler();
  1018. return RunVVis( argc, argv );
  1019. }
  1020. // When VVIS is used as a DLL (makes debugging vmpi vvis a lot easier), this is used to
  1021. // get it going.
  1022. class CVVisDLL : public ILaunchableDLL
  1023. {
  1024. public:
  1025. virtual int main( int argc, char **argv )
  1026. {
  1027. return ::main( argc, argv );
  1028. }
  1029. };
  1030. EXPOSE_SINGLE_INTERFACE( CVVisDLL, ILaunchableDLL, LAUNCHABLE_DLL_INTERFACE_VERSION );