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.

1757 lines
52 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: BSP Building tool
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "vbsp.h"
  8. #include "detail.h"
  9. #include "physdll.h"
  10. #include "utilmatlib.h"
  11. #include "disp_vbsp.h"
  12. #include "writebsp.h"
  13. #include "tier0/icommandline.h"
  14. #include "materialsystem/imaterialsystem.h"
  15. #include "map.h"
  16. #include "tools_minidump.h"
  17. #include "materialsub.h"
  18. #include "loadcmdline.h"
  19. #include "byteswap.h"
  20. #include "worldvertextransitionfixup.h"
  21. #include "lzma/lzma.h"
  22. #include "tier1/UtlBuffer.h"
  23. extern float g_maxLightmapDimension;
  24. char source[1024];
  25. char mapbase[ 64 ];
  26. char name[1024];
  27. char materialPath[1024];
  28. vec_t microvolume = 1.0;
  29. qboolean noprune;
  30. qboolean glview;
  31. qboolean nodetail;
  32. qboolean fulldetail;
  33. qboolean onlyents;
  34. bool onlyprops;
  35. qboolean nomerge;
  36. qboolean nomergewater = false;
  37. qboolean nowater;
  38. qboolean nocsg;
  39. qboolean noweld;
  40. qboolean noshare;
  41. qboolean nosubdiv;
  42. qboolean notjunc;
  43. qboolean noopt;
  44. qboolean leaktest;
  45. qboolean verboseentities;
  46. qboolean staticpropcombine = false;
  47. qboolean staticpropcombine_delsources = true;
  48. qboolean staticpropcombine_doflagcompare_STATIC_PROP_IGNORE_NORMALS = true;
  49. qboolean staticpropcombine_doflagcompare_STATIC_PROP_NO_SHADOW = true;
  50. qboolean staticpropcombine_doflagcompare_STATIC_PROP_NO_FLASHLIGHT = true;
  51. qboolean staticpropcombine_doflagcompare_STATIC_PROP_MARKED_FOR_FAST_REFLECTION = true;
  52. qboolean staticpropcombine_doflagcompare_STATIC_PROP_NO_PER_VERTEX_LIGHTING = true;
  53. qboolean staticpropcombine_doflagcompare_STATIC_PROP_NO_SELF_SHADOWING = true;
  54. qboolean staticpropcombine_doflagcompare_STATIC_PROP_FLAGS_EX_DISABLE_SHADOW_DEPTH = true;
  55. qboolean staticpropcombine_considervis = false;
  56. qboolean staticpropcombine_autocombine = false;
  57. qboolean staticpropcombine_suggestcombinerules = false;
  58. int g_nAutoCombineMinInstances = 2;
  59. qboolean dumpcollide = false;
  60. qboolean g_bLowPriority = false;
  61. qboolean g_DumpStaticProps = false;
  62. qboolean g_bSkyVis = false; // skybox vis is off by default, toggle this to enable it
  63. bool g_bLightIfMissing = false;
  64. bool g_snapAxialPlanes = false;
  65. bool g_bKeepStaleZip = false;
  66. bool g_NodrawTriggers = false;
  67. bool g_DisableWaterLighting = false;
  68. bool g_bAllowDetailCracks = false;
  69. bool g_bNoVirtualMesh = false;
  70. int g_nVisGranularityX = 0, g_nVisGranularityY = 0, g_nVisGranularityZ = 0;
  71. float g_defaultLuxelSize = DEFAULT_LUXEL_SIZE;
  72. float g_luxelScale = 1.0f;
  73. float g_minLuxelScale = 1.0f;
  74. float g_maxLuxelScale = 999999.0f;
  75. bool g_BumpAll = false;
  76. // Convert structural BSP brushes (which affect visibility) to detail brushes
  77. bool g_bConvertStructureToDetail = false;
  78. CUtlVector<int> g_SkyAreas;
  79. char outbase[32];
  80. // HLTOOLS: Introduce these calcs to make the block algorithm proportional to the proper
  81. // world coordinate extents. Assumes square spatial constraints.
  82. int g_nBlockSize = 1024;
  83. #define BLOCKS_SPACE (COORD_EXTENT/g_nBlockSize)
  84. #define BLOCKX_OFFSET ((BLOCKS_SPACE/2)+1)
  85. #define BLOCKY_OFFSET ((BLOCKS_SPACE/2)+1)
  86. #define BLOCKS_MIN (-(BLOCKS_SPACE/2))
  87. #define BLOCKS_MAX ((BLOCKS_SPACE/2)-1)
  88. #define BLOCKS_ARRAY_WIDTH ( BLOCKS_SPACE + 2 )
  89. int block_xl = BLOCKS_MIN, block_xh = BLOCKS_MAX, block_yl = BLOCKS_MIN, block_yh = BLOCKS_MAX;
  90. int entity_num;
  91. node_t **ppBlockNodes = NULL;
  92. //-----------------------------------------------------------------------------
  93. // Assign occluder areas (must happen *after* the world model is processed)
  94. //-----------------------------------------------------------------------------
  95. void AssignOccluderAreas( tree_t *pTree );
  96. static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas );
  97. /*
  98. ============
  99. BlockTree
  100. ============
  101. */
  102. node_t *BlockTree ( int xl, int yl, int xh, int yh )
  103. {
  104. node_t *pNode;
  105. Vector normal;
  106. float dist;
  107. int mid;
  108. if ( xl == xh && yl == yh )
  109. {
  110. pNode = ppBlockNodes[ xl+BLOCKX_OFFSET + ( ( yl+BLOCKY_OFFSET ) * BLOCKS_ARRAY_WIDTH ) ];
  111. if ( !pNode )
  112. { // return an empty leaf
  113. pNode = AllocNode ();
  114. pNode->planenum = PLANENUM_LEAF;
  115. pNode->contents = 0; //CONTENTS_SOLID;
  116. return pNode;
  117. }
  118. return pNode;
  119. }
  120. // create a seperator along the largest axis
  121. pNode = AllocNode ();
  122. if ( xh - xl > yh - yl )
  123. { // split x axis
  124. mid = xl + (xh-xl)/2 + 1;
  125. normal[0] = 1;
  126. normal[1] = 0;
  127. normal[2] = 0;
  128. dist = mid*g_nBlockSize;
  129. pNode->planenum = g_MainMap->FindFloatPlane (normal, dist);
  130. pNode->children[0] = BlockTree ( mid, yl, xh, yh);
  131. pNode->children[1] = BlockTree ( xl, yl, mid-1, yh);
  132. }
  133. else
  134. {
  135. mid = yl + (yh-yl)/2 + 1;
  136. normal[0] = 0;
  137. normal[1] = 1;
  138. normal[2] = 0;
  139. dist = mid*g_nBlockSize;
  140. pNode->planenum = g_MainMap->FindFloatPlane (normal, dist);
  141. pNode->children[0] = BlockTree ( xl, mid, xh, yh);
  142. pNode->children[1] = BlockTree ( xl, yl, xh, mid-1);
  143. }
  144. return pNode;
  145. }
  146. /*
  147. ============
  148. ProcessBlock_Thread
  149. ============
  150. */
  151. int brush_start, brush_end;
  152. void ProcessBlock_Thread (int threadnum, int blocknum)
  153. {
  154. int xblock, yblock;
  155. Vector mins, maxs;
  156. bspbrush_t *brushes;
  157. tree_t *tree;
  158. node_t *node;
  159. yblock = block_yl + blocknum / (block_xh-block_xl+1);
  160. xblock = block_xl + blocknum % (block_xh-block_xl+1);
  161. qprintf ("############### block %2i,%2i ###############\n", xblock, yblock);
  162. mins[0] = xblock*g_nBlockSize;
  163. mins[1] = yblock*g_nBlockSize;
  164. mins[2] = MIN_COORD_INTEGER;
  165. maxs[0] = (xblock+1)*g_nBlockSize;
  166. maxs[1] = (yblock+1)*g_nBlockSize;
  167. maxs[2] = MAX_COORD_INTEGER;
  168. // the makelist and chopbrushes could be cached between the passes...
  169. brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs, NO_DETAIL);
  170. if (!brushes)
  171. {
  172. node = AllocNode ();
  173. node->planenum = PLANENUM_LEAF;
  174. node->contents = CONTENTS_SOLID;
  175. ppBlockNodes[ xblock+BLOCKX_OFFSET + ( ( yblock+BLOCKY_OFFSET ) * BLOCKS_ARRAY_WIDTH ) ] = node;
  176. return;
  177. }
  178. FixupAreaportalWaterBrushes( brushes );
  179. if (!nocsg)
  180. brushes = ChopBrushes (brushes);
  181. tree = BrushBSP (brushes, mins, maxs);
  182. ppBlockNodes[ xblock+BLOCKX_OFFSET + ( ( yblock+BLOCKY_OFFSET ) * BLOCKS_ARRAY_WIDTH ) ] = tree->headnode;
  183. }
  184. /*
  185. ============
  186. ProcessWorldModel
  187. ============
  188. */
  189. void SplitSubdividedFaces( node_t *headnode ); // garymcthack
  190. void ProcessWorldModel (void)
  191. {
  192. entity_t *e;
  193. tree_t *tree = NULL;
  194. qboolean leaked;
  195. int optimize;
  196. int start;
  197. e = &entities[entity_num];
  198. brush_start = e->firstbrush;
  199. brush_end = brush_start + e->numbrushes;
  200. leaked = false;
  201. if ( ppBlockNodes == NULL )
  202. {
  203. ppBlockNodes = new node_t *[ BLOCKS_ARRAY_WIDTH * BLOCKS_ARRAY_WIDTH ];
  204. Q_memset( ppBlockNodes, 0, BLOCKS_ARRAY_WIDTH * BLOCKS_ARRAY_WIDTH * sizeof( node_t* ) );
  205. }
  206. //
  207. // perform per-block operations
  208. //
  209. if (block_xh * g_nBlockSize > g_MainMap->map_maxs[0])
  210. {
  211. block_xh = floor(g_MainMap->map_maxs[0]/g_nBlockSize);
  212. }
  213. if ( (block_xl+1) * g_nBlockSize < g_MainMap->map_mins[0])
  214. {
  215. block_xl = floor(g_MainMap->map_mins[0]/g_nBlockSize);
  216. }
  217. if (block_yh * g_nBlockSize > g_MainMap->map_maxs[1])
  218. {
  219. block_yh = floor(g_MainMap->map_maxs[1]/g_nBlockSize);
  220. }
  221. if ( (block_yl+1) * g_nBlockSize < g_MainMap->map_mins[1])
  222. {
  223. block_yl = floor(g_MainMap->map_mins[1]/g_nBlockSize);
  224. }
  225. // HLTOOLS: updated to +/- MAX_COORD_INTEGER ( new world size limits / worldsize.h )
  226. if (block_xl < BLOCKS_MIN)
  227. {
  228. block_xl = BLOCKS_MIN;
  229. }
  230. if (block_yl < BLOCKS_MIN)
  231. {
  232. block_yl = BLOCKS_MIN;
  233. }
  234. if (block_xh > BLOCKS_MAX)
  235. {
  236. block_xh = BLOCKS_MAX;
  237. }
  238. if (block_yh > BLOCKS_MAX)
  239. {
  240. block_yh = BLOCKS_MAX;
  241. }
  242. for (optimize = 0 ; optimize <= 1 ; optimize++)
  243. {
  244. qprintf ("--------------------------------------------\n");
  245. RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1),
  246. !verbose, ProcessBlock_Thread);
  247. //
  248. // build the division tree
  249. // oversizing the blocks guarantees that all the boundaries
  250. // will also get nodes.
  251. //
  252. qprintf ("--------------------------------------------\n");
  253. tree = AllocTree ();
  254. tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
  255. tree->mins[0] = (block_xl)*g_nBlockSize;
  256. tree->mins[1] = (block_yl)*g_nBlockSize;
  257. tree->mins[2] = g_MainMap->map_mins[2] - 8;
  258. tree->maxs[0] = (block_xh+1)*g_nBlockSize;
  259. tree->maxs[1] = (block_yh+1)*g_nBlockSize;
  260. tree->maxs[2] = g_MainMap->map_maxs[2] + 8;
  261. //
  262. // perform the global operations
  263. //
  264. // make the portals/faces by traversing down to each empty leaf
  265. MakeTreePortals (tree);
  266. if (FloodEntities (tree))
  267. {
  268. // turns everthing outside into solid
  269. FillOutside (tree->headnode);
  270. }
  271. else
  272. {
  273. Warning( ("**** leaked ****\n") );
  274. leaked = true;
  275. LeakFile (tree);
  276. if (leaktest)
  277. {
  278. Warning( ("--- MAP LEAKED ---\n") );
  279. exit (0);
  280. }
  281. }
  282. // mark the brush sides that actually turned into faces
  283. MarkVisibleSides (tree, brush_start, brush_end, NO_DETAIL);
  284. if (noopt || leaked)
  285. break;
  286. if (!optimize)
  287. {
  288. // If we are optimizing, free the tree. Next time we will construct it again, but
  289. // we'll use the information in MarkVisibleSides() so we'll only split with planes that
  290. // actually contribute renderable geometry
  291. FreeTree (tree);
  292. }
  293. }
  294. FloodAreas (tree);
  295. RemoveAreaPortalBrushes_R( tree->headnode );
  296. start = Plat_FloatTime();
  297. Msg("Building Faces...");
  298. // this turns portals with one solid side into faces
  299. // it also subdivides each face if necessary to fit max lightmap dimensions
  300. MakeFaces (tree->headnode);
  301. Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
  302. if (glview)
  303. {
  304. WriteGLView (tree, source);
  305. }
  306. AssignOccluderAreas( tree );
  307. Compute3DSkyboxAreas( tree->headnode, g_SkyAreas );
  308. face_t *pLeafFaceList = NULL;
  309. if ( !nodetail )
  310. {
  311. pLeafFaceList = MergeDetailTree( tree, brush_start, brush_end );
  312. }
  313. start = Plat_FloatTime();
  314. Msg("FixTjuncs...\n");
  315. // This unifies the vertex list for all edges (splits collinear edges to remove t-junctions)
  316. // It also welds the list of vertices out of each winding/portal and rounds nearly integer verts to integer
  317. pLeafFaceList = FixTjuncs (tree->headnode, pLeafFaceList);
  318. // this merges all of the solid nodes that have separating planes
  319. if (!noprune)
  320. {
  321. Msg("PruneNodes...\n");
  322. PruneNodes (tree->headnode);
  323. }
  324. // Msg( "SplitSubdividedFaces...\n" );
  325. // SplitSubdividedFaces( tree->headnode );
  326. Msg("WriteBSP...\n");
  327. WriteBSP (tree->headnode, pLeafFaceList);
  328. Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
  329. if (!leaked)
  330. {
  331. WritePortalFile (tree);
  332. }
  333. FreeTree( tree );
  334. FreeLeafFaces( pLeafFaceList );
  335. }
  336. /*
  337. ============
  338. ProcessSubModel
  339. ============
  340. */
  341. void ProcessSubModel( )
  342. {
  343. entity_t *e;
  344. int start, end;
  345. tree_t *tree;
  346. bspbrush_t *list;
  347. Vector mins, maxs;
  348. e = &entities[entity_num];
  349. start = e->firstbrush;
  350. end = start + e->numbrushes;
  351. mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
  352. maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
  353. list = MakeBspBrushList (start, end, mins, maxs, FULL_DETAIL);
  354. if (!nocsg)
  355. list = ChopBrushes (list);
  356. tree = BrushBSP (list, mins, maxs);
  357. // This would wind up crashing the engine because we'd have a negative leaf index in dmodel_t::headnode.
  358. if ( tree->headnode->planenum == PLANENUM_LEAF )
  359. {
  360. const char *pClassName = ValueForKey( e, "classname" );
  361. const char *pTargetName = ValueForKey( e, "targetname" );
  362. Error( "bmodel %d has no head node (class '%s', targetname '%s')", entity_num, pClassName, pTargetName );
  363. }
  364. MakeTreePortals (tree);
  365. #if DEBUG_BRUSHMODEL
  366. if ( entity_num == DEBUG_BRUSHMODEL )
  367. WriteGLView( tree, "tree_all" );
  368. #endif
  369. MarkVisibleSides (tree, start, end, FULL_DETAIL);
  370. MakeFaces (tree->headnode);
  371. FixTjuncs( tree->headnode, NULL );
  372. WriteBSP( tree->headnode, NULL );
  373. #if DEBUG_BRUSHMODEL
  374. if ( entity_num == DEBUG_BRUSHMODEL )
  375. {
  376. WriteGLView( tree, "tree_vis" );
  377. WriteGLViewFaces( tree, "tree_faces" );
  378. }
  379. #endif
  380. FreeTree (tree);
  381. }
  382. //-----------------------------------------------------------------------------
  383. // Helper routine to setup side and texture setting of a splitting hint brush
  384. //-----------------------------------------------------------------------------
  385. bool InsertVisibilitySplittingHintBrush( Vector const &cut0, Vector const &cut1, Vector const &cut2, Vector const &cut3, Vector vNormal, int &nSideID, int &nBrushID )
  386. {
  387. if ( g_MainMap->nummapbrushes == MAX_MAP_BRUSHES )
  388. {
  389. Error( "nummapbrushes == MAX_MAP_BRUSHES when inserting visibility split hint brushes" );
  390. }
  391. mapbrush_t &mbr = g_MainMap->mapbrushes[g_MainMap->nummapbrushes];
  392. V_memset( &mbr, 0, sizeof( mbr ) );
  393. mbr.brushnum = g_MainMap->nummapbrushes;
  394. mbr.id = ( ++ nBrushID );
  395. mbr.numsides = 6;
  396. mbr.original_sides = &g_MainMap->brushsides[g_MainMap->nummapbrushsides];
  397. g_MainMap->nummapbrushes ++;
  398. //
  399. // HINT
  400. //
  401. {
  402. if ( g_MainMap->nummapbrushsides == MAX_MAP_BRUSHSIDES )
  403. {
  404. Error( "nummapbrushsides == MAX_MAP_BRUSHSIDES when inserting visibility split hint brushes" );
  405. }
  406. side_t &side = g_MainMap->brushsides[g_MainMap->nummapbrushsides];
  407. V_memset( &side, 0, sizeof( side ) );
  408. side.planenum = g_MainMap->PlaneFromPoints( cut0, cut1, cut2 );
  409. side.id = ( ++ nSideID );
  410. side.visible = true;
  411. side.thin = true;
  412. side.surf = ( SURF_NODRAW | SURF_NOLIGHT | SURF_HINT );
  413. side.texinfo = FindMiptex( "TOOLS/TOOLSHINT" );
  414. brush_texture_t &btt = g_MainMap->side_brushtextures[g_MainMap->nummapbrushsides];
  415. V_memset( &btt, 0, sizeof( btt ) );
  416. V_strcpy_safe( btt.name, "TOOLS/TOOLSHINT" );
  417. btt.flags = ( SURF_NODRAW | SURF_NOLIGHT | SURF_HINT );
  418. btt.lightmapWorldUnitsPerLuxel = 16;
  419. btt.textureWorldUnitsPerTexel[0] = btt.textureWorldUnitsPerTexel[1] = 0.25f;
  420. btt.UAxis[0] = 1;
  421. btt.VAxis[2] = -1;
  422. g_MainMap->nummapbrushsides ++;
  423. }
  424. // SKIP points array
  425. Vector arrSkipPoints[15] =
  426. {
  427. cut2 + vNormal, cut1 + vNormal, cut0 + vNormal, // LARGE skip
  428. cut1 + vNormal, cut1, cut0, // small skip 0->1->1'
  429. cut2 + vNormal, cut2, cut1, // small skip 1->2->2'
  430. cut3 + vNormal, cut3, cut2, // small skip 3'->3->2
  431. cut0 + vNormal, cut0, cut3, // small skip 3->0->0'
  432. };
  433. for ( int iSkip = 0; iSkip < 5; ++ iSkip )
  434. {
  435. if ( g_MainMap->nummapbrushsides == MAX_MAP_BRUSHSIDES )
  436. {
  437. Error( "nummapbrushsides == MAX_MAP_BRUSHSIDES when inserting visibility split hint brushes" );
  438. }
  439. side_t &side = g_MainMap->brushsides[g_MainMap->nummapbrushsides];
  440. V_memset( &side, 0, sizeof( side ) );
  441. side.planenum = g_MainMap->PlaneFromPoints( arrSkipPoints[ iSkip*3 + 0 ], arrSkipPoints[ iSkip*3 + 1 ], arrSkipPoints[ iSkip*3 + 2 ] );
  442. side.id = ( ++ nSideID );
  443. side.visible = false;
  444. side.thin = true;
  445. side.surf = ( SURF_NODRAW | SURF_NOLIGHT | SURF_SKIP );
  446. side.texinfo = FindMiptex( "TOOLS/TOOLSSKIP" );
  447. brush_texture_t &btt = g_MainMap->side_brushtextures[g_MainMap->nummapbrushsides];
  448. V_memset( &btt, 0, sizeof( btt ) );
  449. V_strcpy_safe( btt.name, "TOOLS/TOOLSSKIP" );
  450. btt.flags = ( SURF_NODRAW | SURF_NOLIGHT | SURF_SKIP );
  451. btt.lightmapWorldUnitsPerLuxel = 16;
  452. btt.textureWorldUnitsPerTexel[0] = btt.textureWorldUnitsPerTexel[1] = 0.25f;
  453. btt.UAxis[0] = 1;
  454. btt.VAxis[2] = -1;
  455. g_MainMap->nummapbrushsides ++;
  456. }
  457. g_MainMap->MakeBrushWindings( &mbr );
  458. return true;
  459. }
  460. //-----------------------------------------------------------------------------
  461. // Inserts visibility splitting hint brushes
  462. //-----------------------------------------------------------------------------
  463. void InsertVisibilitySplittingHintBrushes()
  464. {
  465. // Compute max brush side ID
  466. int max_side_id = 0;
  467. for( int i = 0; i < g_MainMap->nummapbrushsides; i++ )
  468. {
  469. if ( g_MainMap->brushsides[ i ].id > max_side_id )
  470. {
  471. max_side_id = g_MainMap->brushsides[ i ].id;
  472. }
  473. }
  474. // Compute max brush ID
  475. int max_brush_id = 0;
  476. for( int i = 0; i < g_MainMap->nummapbrushes; i++ )
  477. {
  478. if ( g_MainMap->mapbrushes[ i ].id > max_brush_id )
  479. {
  480. max_brush_id = g_MainMap->mapbrushes[ i ].id;
  481. }
  482. }
  483. // Have a fake entity tracking all the splits that we added
  484. entity_t entityForVisibilitySplits;
  485. V_memset( &entityForVisibilitySplits, 0, sizeof( entityForVisibilitySplits ) );
  486. entityForVisibilitySplits.firstbrush = g_MainMap->nummapbrushes;
  487. // Force visibility splits
  488. if ( g_nVisGranularityX > 0 )
  489. {
  490. int nMinX = g_MainMap->map_mins.x;
  491. nMinX = 1 + ( nMinX / g_nVisGranularityX ) * g_nVisGranularityX;
  492. int nMaxX = g_MainMap->map_maxs.x;
  493. nMaxX = -1 + ( nMaxX / g_nVisGranularityX ) * g_nVisGranularityX;
  494. int numCuts = 0;
  495. for ( ; nMinX < nMaxX; nMinX += g_nVisGranularityX )
  496. {
  497. Vector cut0( nMinX, g_MainMap->map_maxs.y, g_MainMap->map_maxs.z );
  498. Vector cut1( nMinX, g_MainMap->map_mins.y, g_MainMap->map_maxs.z );
  499. Vector cut2( nMinX, g_MainMap->map_mins.y, g_MainMap->map_mins.z );
  500. Vector cut3( nMinX, g_MainMap->map_maxs.y, g_MainMap->map_mins.z );
  501. Vector vNormal( 1, 0, 0 );
  502. if ( InsertVisibilitySplittingHintBrush( cut0, cut1, cut2, cut3, vNormal, max_side_id, max_brush_id ) )
  503. {
  504. ++ entityForVisibilitySplits.numbrushes;
  505. ++ numCuts;
  506. }
  507. }
  508. Msg("Vis granularity X introduced %i cuts between %.0f and %.0f\n", numCuts, g_MainMap->map_mins.x, g_MainMap->map_maxs.x );
  509. }
  510. if ( g_nVisGranularityY > 0 )
  511. {
  512. int nMinY = g_MainMap->map_mins.y;
  513. nMinY = 1 + ( nMinY / g_nVisGranularityY ) * g_nVisGranularityY;
  514. int nMaxY = g_MainMap->map_maxs.y;
  515. nMaxY = -1 + ( nMaxY / g_nVisGranularityY ) * g_nVisGranularityY;
  516. int numCuts = 0;
  517. for ( ; nMinY < nMaxY; nMinY += g_nVisGranularityY )
  518. {
  519. Vector cut0( g_MainMap->map_maxs.x, nMinY, g_MainMap->map_mins.z );
  520. Vector cut1( g_MainMap->map_mins.x, nMinY, g_MainMap->map_mins.z );
  521. Vector cut2( g_MainMap->map_mins.x, nMinY, g_MainMap->map_maxs.z );
  522. Vector cut3( g_MainMap->map_maxs.x, nMinY, g_MainMap->map_maxs.z );
  523. Vector vNormal( 0, 1, 0 );
  524. if ( InsertVisibilitySplittingHintBrush( cut0, cut1, cut2, cut3, vNormal, max_side_id, max_brush_id ) )
  525. {
  526. ++ entityForVisibilitySplits.numbrushes;
  527. ++ numCuts;
  528. }
  529. }
  530. Msg("Vis granularity Y introduced %i cuts between %.0f and %.0f\n", numCuts, g_MainMap->map_mins.y, g_MainMap->map_maxs.y );
  531. }
  532. if ( g_nVisGranularityZ > 0 )
  533. {
  534. int nMinZ = g_MainMap->map_mins.z;
  535. nMinZ = 1 + ( nMinZ / g_nVisGranularityZ ) * g_nVisGranularityZ;
  536. int nMaxZ = g_MainMap->map_maxs.z;
  537. nMaxZ = -1 + ( nMaxZ / g_nVisGranularityZ ) * g_nVisGranularityZ;
  538. int numCuts = 0;
  539. for ( ; nMinZ < nMaxZ; nMinZ += g_nVisGranularityZ )
  540. {
  541. Vector cut0( g_MainMap->map_mins.x, g_MainMap->map_maxs.y, nMinZ );
  542. Vector cut1( g_MainMap->map_mins.x, g_MainMap->map_mins.y, nMinZ );
  543. Vector cut2( g_MainMap->map_maxs.x, g_MainMap->map_mins.y, nMinZ );
  544. Vector cut3( g_MainMap->map_maxs.x, g_MainMap->map_maxs.y, nMinZ );
  545. Vector vNormal( 0, 0, 1 );
  546. if ( InsertVisibilitySplittingHintBrush( cut0, cut1, cut2, cut3, vNormal, max_side_id, max_brush_id ) )
  547. {
  548. ++ entityForVisibilitySplits.numbrushes;
  549. ++ numCuts;
  550. }
  551. }
  552. Msg("Vis granularity Z introduced %i cuts between %.0f and %.0f\n", numCuts, g_MainMap->map_mins.z, g_MainMap->map_maxs.z );
  553. }
  554. // Now move all the newly introduced brushes to world
  555. if ( entityForVisibilitySplits.numbrushes )
  556. {
  557. g_MainMap->MoveBrushesToWorld( &entityForVisibilitySplits );
  558. if ( num_entities != g_MainMap->num_entities )
  559. {
  560. Error( "Entities accounting error while enforcing visibility granularity!\n" );
  561. }
  562. else
  563. { // Force a re-copy since moving brushes to world affected all brushes and sides
  564. memcpy( entities, g_MainMap->entities, sizeof( g_MainMap->entities ) );
  565. }
  566. }
  567. }
  568. //-----------------------------------------------------------------------------
  569. // Returns true if the entity is a func_occluder
  570. //-----------------------------------------------------------------------------
  571. bool IsFuncOccluder( int entity_num )
  572. {
  573. entity_t *mapent = &entities[entity_num];
  574. const char *pClassName = ValueForKey( mapent, "classname" );
  575. return (strcmp("func_occluder", pClassName) == 0);
  576. }
  577. //-----------------------------------------------------------------------------
  578. // Computes the area of a brush's occluders
  579. //-----------------------------------------------------------------------------
  580. float ComputeOccluderBrushArea( mapbrush_t *pBrush )
  581. {
  582. float flArea = 0.0f;
  583. for ( int j = 0; j < pBrush->numsides; ++j )
  584. {
  585. side_t *pSide = &(pBrush->original_sides[j]);
  586. // Skip nodraw surfaces
  587. if ( texinfo[pSide->texinfo].flags & SURF_NODRAW )
  588. continue;
  589. if ( !pSide->winding )
  590. continue;
  591. flArea += WindingArea( pSide->winding );
  592. }
  593. return flArea;
  594. }
  595. //-----------------------------------------------------------------------------
  596. // Clips all occluder brushes against each other
  597. //-----------------------------------------------------------------------------
  598. static tree_t *ClipOccluderBrushes( )
  599. {
  600. // Create a list of all occluder brushes in the level
  601. CUtlVector< mapbrush_t * > mapBrushes( 1024, 1024 );
  602. for ( entity_num=0; entity_num < g_MainMap->num_entities; ++entity_num )
  603. {
  604. if (!IsFuncOccluder(entity_num))
  605. continue;
  606. entity_t *e = &entities[entity_num];
  607. int end = e->firstbrush + e->numbrushes;
  608. int i;
  609. for ( i = e->firstbrush; i < end; ++i )
  610. {
  611. mapBrushes.AddToTail( &g_MainMap->mapbrushes[i] );
  612. }
  613. }
  614. int nBrushCount = mapBrushes.Count();
  615. if ( nBrushCount == 0 )
  616. return NULL;
  617. Vector mins, maxs;
  618. mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
  619. maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
  620. bspbrush_t *list = MakeBspBrushList( mapBrushes.Base(), nBrushCount, mins, maxs );
  621. if (!nocsg)
  622. list = ChopBrushes (list);
  623. tree_t *tree = BrushBSP (list, mins, maxs);
  624. MakeTreePortals (tree);
  625. MarkVisibleSides (tree, mapBrushes.Base(), nBrushCount);
  626. MakeFaces( tree->headnode );
  627. // NOTE: This will output the occluder face vertices + planes
  628. FixTjuncs( tree->headnode, NULL );
  629. return tree;
  630. }
  631. //-----------------------------------------------------------------------------
  632. // Generate a list of unique sides in the occluder tree
  633. //-----------------------------------------------------------------------------
  634. static void GenerateOccluderSideList( int nEntity, CUtlVector<side_t*> &occluderSides )
  635. {
  636. entity_t *e = &entities[nEntity];
  637. int end = e->firstbrush + e->numbrushes;
  638. int i, j;
  639. for ( i = e->firstbrush; i < end; ++i )
  640. {
  641. mapbrush_t *mb = &g_MainMap->mapbrushes[i];
  642. for ( j = 0; j < mb->numsides; ++j )
  643. {
  644. occluderSides.AddToTail( &(mb->original_sides[j]) );
  645. }
  646. }
  647. }
  648. //-----------------------------------------------------------------------------
  649. // Generate a list of unique faces in the occluder tree
  650. //-----------------------------------------------------------------------------
  651. static void GenerateOccluderFaceList( node_t *pOccluderNode, CUtlVector<face_t*> &occluderFaces )
  652. {
  653. if (pOccluderNode->planenum == PLANENUM_LEAF)
  654. return;
  655. for ( face_t *f=pOccluderNode->faces ; f ; f = f->next )
  656. {
  657. occluderFaces.AddToTail( f );
  658. }
  659. GenerateOccluderFaceList( pOccluderNode->children[0], occluderFaces );
  660. GenerateOccluderFaceList( pOccluderNode->children[1], occluderFaces );
  661. }
  662. //-----------------------------------------------------------------------------
  663. // For occluder area assignment
  664. //-----------------------------------------------------------------------------
  665. struct OccluderInfo_t
  666. {
  667. int m_nOccluderEntityIndex;
  668. };
  669. static CUtlVector< OccluderInfo_t > g_OccluderInfo;
  670. //-----------------------------------------------------------------------------
  671. // Emits occluder brushes
  672. //-----------------------------------------------------------------------------
  673. static void EmitOccluderBrushes()
  674. {
  675. char str[64];
  676. g_OccluderData.RemoveAll();
  677. g_OccluderPolyData.RemoveAll();
  678. g_OccluderVertexIndices.RemoveAll();
  679. tree_t *pOccluderTree = ClipOccluderBrushes();
  680. if (!pOccluderTree)
  681. return;
  682. CUtlVector<face_t*> faceList( 1024, 1024 );
  683. CUtlVector<side_t*> sideList( 1024, 1024 );
  684. GenerateOccluderFaceList( pOccluderTree->headnode, faceList );
  685. #ifdef _DEBUG
  686. int *pEmitted = (int*)stackalloc( faceList.Count() * sizeof(int) );
  687. memset( pEmitted, 0, faceList.Count() * sizeof(int) );
  688. #endif
  689. for ( entity_num=1; entity_num < num_entities; ++entity_num )
  690. {
  691. if (!IsFuncOccluder(entity_num))
  692. continue;
  693. // Output only those parts of the occluder tree which are a part of the brush
  694. int nOccluder = g_OccluderData.AddToTail();
  695. doccluderdata_t &occluderData = g_OccluderData[ nOccluder ];
  696. occluderData.firstpoly = g_OccluderPolyData.Count();
  697. occluderData.mins.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  698. occluderData.maxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  699. occluderData.flags = 0;
  700. occluderData.area = -1;
  701. // NOTE: If you change the algorithm by which occluder numbers are allocated,
  702. // then you must also change FixupOnlyEntsOccluderEntities() below
  703. sprintf (str, "%i", nOccluder);
  704. SetKeyValue (&entities[entity_num], "occludernumber", str);
  705. int nIndex = g_OccluderInfo.AddToTail();
  706. g_OccluderInfo[nIndex].m_nOccluderEntityIndex = entity_num;
  707. sideList.RemoveAll();
  708. GenerateOccluderSideList( entity_num, sideList );
  709. for ( int i = faceList.Count(); --i >= 0; )
  710. {
  711. // Skip nodraw surfaces, but not triggers that have been marked as nodraw
  712. face_t *f = faceList[i];
  713. if ( ( texinfo[f->texinfo].flags & SURF_NODRAW ) &&
  714. (( texinfo[f->texinfo].flags & SURF_TRIGGER ) == 0 ) )
  715. continue;
  716. // Only emit faces that appear in the side list of the occluder
  717. for ( int j = sideList.Count(); --j >= 0; )
  718. {
  719. if ( sideList[j] != f->originalface )
  720. continue;
  721. if ( f->numpoints < 3 )
  722. continue;
  723. // not a final face
  724. Assert ( !f->merged && !f->split[0] && !f->split[1] );
  725. #ifdef _DEBUG
  726. Assert( !pEmitted[i] );
  727. pEmitted[i] = entity_num;
  728. #endif
  729. int k = g_OccluderPolyData.AddToTail();
  730. doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[k];
  731. pOccluderPoly->planenum = f->planenum;
  732. pOccluderPoly->vertexcount = f->numpoints;
  733. pOccluderPoly->firstvertexindex = g_OccluderVertexIndices.Count();
  734. for( k = 0; k < f->numpoints; ++k )
  735. {
  736. g_OccluderVertexIndices.AddToTail( f->vertexnums[k] );
  737. const Vector &p = dvertexes[f->vertexnums[k]].point;
  738. VectorMin( occluderData.mins, p, occluderData.mins );
  739. VectorMax( occluderData.maxs, p, occluderData.maxs );
  740. }
  741. break;
  742. }
  743. }
  744. occluderData.polycount = g_OccluderPolyData.Count() - occluderData.firstpoly;
  745. // Mark this brush as not having brush geometry so it won't be re-emitted with a brush model
  746. entities[entity_num].numbrushes = 0;
  747. }
  748. FreeTree( pOccluderTree );
  749. }
  750. //-----------------------------------------------------------------------------
  751. // Set occluder area
  752. //-----------------------------------------------------------------------------
  753. void SetOccluderArea( int nOccluder, int nArea, int nEntityNum )
  754. {
  755. if ( g_OccluderData[nOccluder].area <= 0 )
  756. {
  757. g_OccluderData[nOccluder].area = nArea;
  758. }
  759. else if ( (nArea != 0) && (g_OccluderData[nOccluder].area != nArea) )
  760. {
  761. const char *pTargetName = ValueForKey( &entities[nEntityNum], "targetname" );
  762. if (!pTargetName)
  763. {
  764. pTargetName = "<no name>";
  765. }
  766. Warning("Occluder \"%s\" straddles multiple areas. This is invalid!\n", pTargetName );
  767. }
  768. }
  769. //-----------------------------------------------------------------------------
  770. // Assign occluder areas (must happen *after* the world model is processed)
  771. //-----------------------------------------------------------------------------
  772. void AssignAreaToOccluder( int nOccluder, tree_t *pTree, bool bCrossAreaPortals )
  773. {
  774. int nFirstPoly = g_OccluderData[nOccluder].firstpoly;
  775. int nEntityNum = g_OccluderInfo[nOccluder].m_nOccluderEntityIndex;
  776. for ( int j = 0; j < g_OccluderData[nOccluder].polycount; ++j )
  777. {
  778. doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[nFirstPoly + j];
  779. int nFirstVertex = pOccluderPoly->firstvertexindex;
  780. for ( int k = 0; k < pOccluderPoly->vertexcount; ++k )
  781. {
  782. int nVertexIndex = g_OccluderVertexIndices[nFirstVertex + k];
  783. node_t *pNode = NodeForPoint( pTree->headnode, dvertexes[ nVertexIndex ].point );
  784. SetOccluderArea( nOccluder, pNode->area, nEntityNum );
  785. int nOtherSideIndex;
  786. portal_t *pPortal;
  787. for ( pPortal = pNode->portals; pPortal; pPortal = pPortal->next[!nOtherSideIndex] )
  788. {
  789. nOtherSideIndex = (pPortal->nodes[0] == pNode) ? 1 : 0;
  790. if (!pPortal->onnode)
  791. continue; // edge of world
  792. // Don't cross over area portals for the area check
  793. if ((!bCrossAreaPortals) && pPortal->nodes[nOtherSideIndex]->contents & CONTENTS_AREAPORTAL)
  794. continue;
  795. int nAdjacentArea = pPortal->nodes[nOtherSideIndex] ? pPortal->nodes[nOtherSideIndex]->area : 0;
  796. SetOccluderArea( nOccluder, nAdjacentArea, nEntityNum );
  797. }
  798. }
  799. }
  800. }
  801. //-----------------------------------------------------------------------------
  802. // Assign occluder areas (must happen *after* the world model is processed)
  803. //-----------------------------------------------------------------------------
  804. void AssignOccluderAreas( tree_t *pTree )
  805. {
  806. for ( int i = 0; i < g_OccluderData.Count(); ++i )
  807. {
  808. AssignAreaToOccluder( i, pTree, false );
  809. // This can only have happened if the only valid portal out leads into an areaportal
  810. if ( g_OccluderData[i].area <= 0 )
  811. {
  812. AssignAreaToOccluder( i, pTree, true );
  813. }
  814. }
  815. }
  816. //-----------------------------------------------------------------------------
  817. // Make sure the func_occluders have the appropriate data set
  818. //-----------------------------------------------------------------------------
  819. void FixupOnlyEntsOccluderEntities()
  820. {
  821. char str[64];
  822. int nOccluder = 0;
  823. for ( entity_num=1; entity_num < num_entities; ++entity_num )
  824. {
  825. if (!IsFuncOccluder(entity_num))
  826. continue;
  827. // NOTE: If you change the algorithm by which occluder numbers are allocated above,
  828. // then you must also change this
  829. sprintf (str, "%i", nOccluder);
  830. SetKeyValue (&entities[entity_num], "occludernumber", str);
  831. ++nOccluder;
  832. }
  833. }
  834. void MarkNoDynamicShadowSides()
  835. {
  836. for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
  837. {
  838. g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = true;
  839. }
  840. for ( int i=0; i < g_NoDynamicShadowSides.Count(); i++ )
  841. {
  842. int brushSideID = g_NoDynamicShadowSides[i];
  843. // Find the side with this ID.
  844. for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
  845. {
  846. if ( g_MainMap->brushsides[iSide].id == brushSideID )
  847. g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = false;
  848. }
  849. }
  850. }
  851. // These must match what is used in engine/networkstringtable.cpp!!!
  852. #define BSPPACK_STRINGTABLE_DICTIONARY_FALLBACK "stringtable_dictionary_fallback.dct"
  853. #define BSPPACK_STRINGTABLE_DICTIONARY_X360_FALLBACK "stringtable_dictionary_fallback_xbox.dct"
  854. #define RESLISTS_FOLDER "reslists"
  855. #define RESLISTS_FOLDER_X360 "reslists_xbox"
  856. static void AddBufferToPackAndLZMACompress( const char *relativename, void *data, int length )
  857. {
  858. unsigned int compressedSize = 0;
  859. byte *compressed = LZMA_Compress( (byte *)data, length, &compressedSize );
  860. if ( compressed )
  861. {
  862. ::AddBufferToPak( GetPakFile(), relativename, compressed, compressedSize, false );
  863. free( compressed );
  864. }
  865. else
  866. {
  867. ::AddBufferToPak( GetPakFile(), relativename, data, length, false );
  868. }
  869. }
  870. void AddDefaultStringtableDictionaries()
  871. {
  872. CUtlBuffer buf;
  873. char reslistsPath[ MAX_PATH ];
  874. // PC default
  875. Q_snprintf( reslistsPath, sizeof( reslistsPath ), "%s%s/%s.dict", gamedir, RESLISTS_FOLDER, mapbase );
  876. // Add PC default file
  877. if ( g_pFileSystem->ReadFile( reslistsPath, NULL, buf ) )
  878. {
  879. // Add to BSP pack file
  880. ::AddBufferToPak( GetPakFile(), BSPPACK_STRINGTABLE_DICTIONARY_FALLBACK, buf.Base(), buf.TellPut(), false );
  881. }
  882. buf.Clear();
  883. // Add 360 default file
  884. Q_snprintf( reslistsPath, sizeof( reslistsPath ), "%s%s/%s.dict", gamedir, RESLISTS_FOLDER_X360, mapbase );
  885. if ( g_pFileSystem->ReadFile( reslistsPath, NULL, buf ) )
  886. {
  887. // Add to BSP pack file
  888. ::AddBufferToPak( GetPakFile(), BSPPACK_STRINGTABLE_DICTIONARY_X360_FALLBACK, buf.Base(), buf.TellPut(), false );
  889. }
  890. }
  891. //-----------------------------------------------------------------------------
  892. // Compute the 3D skybox areas
  893. //-----------------------------------------------------------------------------
  894. static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas )
  895. {
  896. for (int i = 0; i < g_MainMap->num_entities; ++i)
  897. {
  898. char* pEntity = ValueForKey(&entities[i], "classname");
  899. if (!strcmp(pEntity, "sky_camera"))
  900. {
  901. // Found a 3D skybox camera, get a leaf that lies in it
  902. node_t *pLeaf = PointInLeaf( headnode, entities[i].origin );
  903. if (pLeaf->contents & CONTENTS_SOLID)
  904. {
  905. Error ("Error! Entity sky_camera in solid volume! at %.1f %.1f %.1f\n", entities[i].origin.x, entities[i].origin.y, entities[i].origin.z);
  906. }
  907. areas.AddToTail( pLeaf->area );
  908. }
  909. }
  910. }
  911. bool Is3DSkyboxArea( int area )
  912. {
  913. for ( int i = g_SkyAreas.Count(); --i >=0; )
  914. {
  915. if ( g_SkyAreas[i] == area )
  916. return true;
  917. }
  918. return false;
  919. }
  920. /*
  921. ============
  922. ProcessModels
  923. ============
  924. */
  925. void ProcessModels (void)
  926. {
  927. BeginBSPFile ();
  928. // Mark sides that have no dynamic shadows.
  929. MarkNoDynamicShadowSides();
  930. // emit the displacement surfaces
  931. EmitInitialDispInfos();
  932. // Clip occluder brushes against each other,
  933. // Remove them from the list of models to process below
  934. EmitOccluderBrushes( );
  935. for ( entity_num=0; entity_num < num_entities; ++entity_num )
  936. {
  937. entity_t *pEntity = &entities[ entity_num ];
  938. if ( !pEntity->numbrushes )
  939. continue;
  940. qprintf ("############### model %i ###############\n", nummodels);
  941. BeginModel ();
  942. if (entity_num == 0)
  943. {
  944. ProcessWorldModel();
  945. }
  946. else
  947. {
  948. ProcessSubModel( );
  949. }
  950. EndModel ();
  951. if (!verboseentities)
  952. {
  953. verbose = false; // don't bother printing submodels
  954. }
  955. }
  956. GetMapDataFilesMgr()->AddAllRegisteredFilesToPak();
  957. // Turn the skybox into a cubemap in case we don't build env_cubemap textures.
  958. Cubemap_CreateDefaultCubemaps();
  959. EndBSPFile ();
  960. }
  961. void LoadPhysicsDLL( void )
  962. {
  963. PhysicsDLLPath( "vphysics.dll" );
  964. }
  965. void PrintCommandLine( int argc, char **argv )
  966. {
  967. Warning( "Command line: " );
  968. for ( int z=0; z < argc; z++ )
  969. {
  970. Warning( "\"%s\" ", argv[z] );
  971. }
  972. Warning( "\n\n" );
  973. }
  974. int RunVBSP( int argc, char **argv )
  975. {
  976. int i;
  977. double start, end;
  978. char path[1024];
  979. CommandLine()->CreateCmdLine( argc, argv );
  980. MathLib_Init( 2.2f, 2.2f, 0.0f, OVERBRIGHT, false, false, false, false );
  981. InstallSpewFunction();
  982. LoggingSystem_SetChannelSpewLevelByTag( "Developer", LS_MESSAGE );
  983. CmdLib_InitFileSystem( argv[ argc-1 ] );
  984. Q_StripExtension( ExpandArg( argv[ argc-1 ] ), source, sizeof( source ) );
  985. Q_FileBase( source, mapbase, sizeof( mapbase ) );
  986. strlwr( mapbase );
  987. LoadCmdLineFromFile( argc, argv, mapbase, "vbsp" );
  988. Msg( "Valve Software - vbsp.exe (%s)\n", __DATE__ );
  989. for (i=1 ; i<argc ; i++)
  990. {
  991. if (!stricmp(argv[i],"-threads"))
  992. {
  993. numthreads = atoi (argv[i+1]);
  994. i++;
  995. }
  996. else if (!Q_stricmp(argv[i],"-glview"))
  997. {
  998. glview = true;
  999. }
  1000. else if ( !Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose") )
  1001. {
  1002. Msg("verbose = true\n");
  1003. verbose = true;
  1004. }
  1005. else if (!Q_stricmp(argv[i], "-noweld"))
  1006. {
  1007. Msg ("noweld = true\n");
  1008. noweld = true;
  1009. }
  1010. else if (!Q_stricmp(argv[i], "-nocsg"))
  1011. {
  1012. Msg ("nocsg = true\n");
  1013. nocsg = true;
  1014. }
  1015. else if (!Q_stricmp(argv[i], "-noshare"))
  1016. {
  1017. Msg ("noshare = true\n");
  1018. noshare = true;
  1019. }
  1020. else if (!Q_stricmp(argv[i], "-notjunc"))
  1021. {
  1022. Msg ("notjunc = true\n");
  1023. notjunc = true;
  1024. }
  1025. else if (!Q_stricmp(argv[i], "-nowater"))
  1026. {
  1027. Msg ("nowater = true\n");
  1028. nowater = true;
  1029. }
  1030. else if (!Q_stricmp(argv[i], "-staticpropcombine"))
  1031. {
  1032. Msg ("staticpropcombine = true\n");
  1033. staticpropcombine = true;
  1034. }
  1035. else if (!Q_stricmp(argv[i], "-keepsources"))
  1036. {
  1037. Msg ("keepsources = true\n");
  1038. staticpropcombine_delsources = false;
  1039. }
  1040. else if ( !Q_stricmp( argv[ i ], "-staticpropcombine_considervis" ) )
  1041. {
  1042. Msg( "staticpropcombine_considervis = true\n" );
  1043. staticpropcombine_considervis = true;
  1044. }
  1045. else if ( !Q_stricmp( argv[ i ], "-staticpropcombine_autocombine" ) )
  1046. {
  1047. Msg( "staticpropcombine_autocombine = true\n" );
  1048. staticpropcombine_autocombine = true;
  1049. }
  1050. else if ( !Q_stricmp( argv[ i ], "-staticpropcombine_suggestrules" ) )
  1051. {
  1052. Msg( "staticpropcombine_suggestcombinerules = true\n" );
  1053. staticpropcombine_suggestcombinerules = true;
  1054. }
  1055. else if ( !Q_stricmp( argv[ i ], "-staticpropcombine_mininstances" ) )
  1056. {
  1057. g_nAutoCombineMinInstances = atoi( argv[ i + 1 ] );
  1058. g_nAutoCombineMinInstances = Max( g_nAutoCombineMinInstances, 2 );
  1059. Msg( "staticpropcombine_mininstances = %d\n", g_nAutoCombineMinInstances );
  1060. i++;
  1061. }
  1062. else if (!Q_stricmp(argv[i], "-combineignore_normals")) { Msg ("combineignore_normals = true\n"); staticpropcombine_doflagcompare_STATIC_PROP_IGNORE_NORMALS = false; }
  1063. else if (!Q_stricmp(argv[i], "-combineignore_noshadow")) { Msg ("combineignore_noshadow = true\n"); staticpropcombine_doflagcompare_STATIC_PROP_NO_SHADOW = false; }
  1064. else if (!Q_stricmp(argv[i], "-combineignore_noflashlight")) { Msg ("combineignore_noflashlight = true\n"); staticpropcombine_doflagcompare_STATIC_PROP_NO_FLASHLIGHT = false; }
  1065. else if (!Q_stricmp(argv[i], "-combineignore_fastreflection")) { Msg ("combineignore_fastreflection = true\n"); staticpropcombine_doflagcompare_STATIC_PROP_MARKED_FOR_FAST_REFLECTION = false; }
  1066. else if (!Q_stricmp(argv[i], "-combineignore_novertexlighting")) { Msg ("combineignore_novertexlighting = true\n"); staticpropcombine_doflagcompare_STATIC_PROP_NO_PER_VERTEX_LIGHTING = false; }
  1067. else if (!Q_stricmp(argv[i], "-combineignore_noselfshadowing")) { Msg ("combineignore_noselfshadowing = true\n"); staticpropcombine_doflagcompare_STATIC_PROP_NO_SELF_SHADOWING = false; }
  1068. else if (!Q_stricmp(argv[i], "-combineignore_disableshadowdepth")) { Msg ("combineignore_disableshadowdepth = true\n"); staticpropcombine_doflagcompare_STATIC_PROP_FLAGS_EX_DISABLE_SHADOW_DEPTH = false; }
  1069. else if (!Q_stricmp(argv[i], "-noopt"))
  1070. {
  1071. Msg ("noopt = true\n");
  1072. noopt = true;
  1073. }
  1074. else if (!Q_stricmp(argv[i], "-noprune"))
  1075. {
  1076. Msg ("noprune = true\n");
  1077. noprune = true;
  1078. }
  1079. else if (!Q_stricmp(argv[i], "-nomerge"))
  1080. {
  1081. Msg ("nomerge = true\n");
  1082. nomerge = true;
  1083. }
  1084. else if (!Q_stricmp(argv[i], "-nomergewater"))
  1085. {
  1086. Msg ("nomergewater = true\n");
  1087. nomergewater = true;
  1088. }
  1089. else if (!Q_stricmp(argv[i], "-nosubdiv"))
  1090. {
  1091. Msg ("nosubdiv = true\n");
  1092. nosubdiv = true;
  1093. }
  1094. else if (!Q_stricmp(argv[i], "-nodetail"))
  1095. {
  1096. Msg ("nodetail = true\n");
  1097. nodetail = true;
  1098. }
  1099. else if (!Q_stricmp(argv[i], "-fulldetail"))
  1100. {
  1101. Msg ("fulldetail = true\n");
  1102. fulldetail = true;
  1103. }
  1104. else if (!Q_stricmp(argv[i], "-alldetail"))
  1105. {
  1106. g_bConvertStructureToDetail = true;
  1107. }
  1108. else if (!Q_stricmp(argv[i], "-onlyents"))
  1109. {
  1110. Msg ("onlyents = true\n");
  1111. onlyents = true;
  1112. }
  1113. else if (!Q_stricmp(argv[i], "-onlyprops"))
  1114. {
  1115. Msg ("onlyprops = true\n");
  1116. onlyprops = true;
  1117. }
  1118. else if (!Q_stricmp(argv[i], "-micro"))
  1119. {
  1120. microvolume = atof(argv[i+1]);
  1121. Msg ("microvolume = %f\n", microvolume);
  1122. i++;
  1123. }
  1124. else if (!Q_stricmp(argv[i], "-leaktest"))
  1125. {
  1126. Msg ("leaktest = true\n");
  1127. leaktest = true;
  1128. }
  1129. else if (!Q_stricmp(argv[i], "-verboseentities"))
  1130. {
  1131. Msg ("verboseentities = true\n");
  1132. verboseentities = true;
  1133. }
  1134. else if (!Q_stricmp(argv[i], "-snapaxial"))
  1135. {
  1136. Msg ("snap axial = true\n");
  1137. g_snapAxialPlanes = true;
  1138. }
  1139. #if 0
  1140. else if (!Q_stricmp(argv[i], "-maxlightmapdim"))
  1141. {
  1142. g_maxLightmapDimension = atof(argv[i+1]);
  1143. Msg ("g_maxLightmapDimension = %f\n", g_maxLightmapDimension);
  1144. i++;
  1145. }
  1146. #endif
  1147. else if (!Q_stricmp(argv[i], "-block"))
  1148. {
  1149. block_xl = block_xh = atoi(argv[i+1]);
  1150. block_yl = block_yh = atoi(argv[i+2]);
  1151. Msg ("block: %i,%i\n", block_xl, block_yl);
  1152. i+=2;
  1153. }
  1154. else if (!Q_stricmp(argv[i], "-blocks"))
  1155. {
  1156. block_xl = atoi(argv[i+1]);
  1157. block_yl = atoi(argv[i+2]);
  1158. block_xh = atoi(argv[i+3]);
  1159. block_yh = atoi(argv[i+4]);
  1160. Msg ("blocks: %i,%i to %i,%i\n",
  1161. block_xl, block_yl, block_xh, block_yh);
  1162. i+=4;
  1163. }
  1164. else if (!Q_stricmp(argv[i], "-visgranularity"))
  1165. {
  1166. g_nVisGranularityX = abs( atoi( argv[i+1] ) );
  1167. g_nVisGranularityY = abs( atoi( argv[i+2] ) );
  1168. g_nVisGranularityZ = abs( atoi( argv[i+3] ) );
  1169. Msg ("visgranularity: %i,%i,%i\n",
  1170. g_nVisGranularityX, g_nVisGranularityY, g_nVisGranularityZ);
  1171. i += 3;
  1172. }
  1173. else if (!Q_stricmp(argv[i], "-blocksize"))
  1174. {
  1175. g_nBlockSize = atoi(argv[i+1]);
  1176. i++;
  1177. }
  1178. else if ( !Q_stricmp( argv[i], "-dumpcollide" ) )
  1179. {
  1180. Msg("Dumping collision models to collideXXX.txt\n" );
  1181. dumpcollide = true;
  1182. }
  1183. else if ( !Q_stricmp( argv[i], "-dumpstaticprop" ) )
  1184. {
  1185. Msg("Dumping static props to staticpropXXX.txt\n" );
  1186. g_DumpStaticProps = true;
  1187. }
  1188. else if ( !Q_stricmp( argv[i], "-forceskyvis" ) )
  1189. {
  1190. Msg("Enabled vis in 3d skybox\n" );
  1191. g_bSkyVis = true;
  1192. }
  1193. else if (!Q_stricmp (argv[i],"-tmpout"))
  1194. {
  1195. strcpy (outbase, "/tmp");
  1196. }
  1197. #if 0
  1198. else if( !Q_stricmp( argv[i], "-defaultluxelsize" ) )
  1199. {
  1200. g_defaultLuxelSize = atof( argv[i+1] );
  1201. i++;
  1202. }
  1203. #endif
  1204. else if( !Q_stricmp( argv[i], "-luxelscale" ) )
  1205. {
  1206. g_luxelScale = atof( argv[i+1] );
  1207. i++;
  1208. }
  1209. else if( !strcmp( argv[i], "-minluxelscale" ) )
  1210. {
  1211. g_minLuxelScale = atof( argv[i+1] );
  1212. if (g_minLuxelScale < 1)
  1213. g_minLuxelScale = 1;
  1214. i++;
  1215. }
  1216. else if( !strcmp( argv[i], "-maxluxelscale" ) )
  1217. {
  1218. g_maxLuxelScale = atof( argv[i+1] );
  1219. if ( g_maxLuxelScale < 1 )
  1220. g_maxLuxelScale = 1;
  1221. i++;
  1222. }
  1223. else if( !Q_stricmp( argv[i], "-bumpall" ) )
  1224. {
  1225. g_BumpAll = true;
  1226. }
  1227. else if( !Q_stricmp( argv[i], "-low" ) )
  1228. {
  1229. g_bLowPriority = true;
  1230. }
  1231. else if( !Q_stricmp( argv[i], "-lightifmissing" ) )
  1232. {
  1233. g_bLightIfMissing = true;
  1234. }
  1235. else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
  1236. {
  1237. }
  1238. else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
  1239. {
  1240. // nothing to do here, but don't bail on this option
  1241. }
  1242. else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) )
  1243. {
  1244. ++i;
  1245. }
  1246. else if ( !Q_stricmp( argv[i], "-keepstalezip" ) )
  1247. {
  1248. g_bKeepStaleZip = true;
  1249. }
  1250. else if ( !Q_stricmp( argv[i], "-xbox" ) )
  1251. {
  1252. // enable mandatory xbox extensions
  1253. g_NodrawTriggers = true;
  1254. g_DisableWaterLighting = true;
  1255. }
  1256. else if ( !Q_stricmp( argv[i], "-allowdetailcracks"))
  1257. {
  1258. g_bAllowDetailCracks = true;
  1259. }
  1260. else if ( !Q_stricmp( argv[i], "-novirtualmesh"))
  1261. {
  1262. g_bNoVirtualMesh = true;
  1263. }
  1264. else if ( !Q_stricmp( argv[i], "-replacematerials" ) )
  1265. {
  1266. g_ReplaceMaterials = true;
  1267. }
  1268. else if ( !Q_stricmp(argv[i], "-nodrawtriggers") )
  1269. {
  1270. g_NodrawTriggers = true;
  1271. }
  1272. else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
  1273. {
  1274. EnableFullMinidumps( true );
  1275. }
  1276. else if ( !Q_stricmp( argv[i], "-tempcontent" ) )
  1277. {
  1278. // ... Do nothing, just let this pass to the filesystem
  1279. }
  1280. else if ( !Q_stricmp( argv[ i ], "-processheap" ) )
  1281. {
  1282. // ... Do nothing, just let this pass to the mem system
  1283. }
  1284. else if (argv[i][0] == '-')
  1285. {
  1286. Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
  1287. i = 100000; // force it to print the usage
  1288. break;
  1289. }
  1290. else
  1291. break;
  1292. }
  1293. if (i != argc - 1)
  1294. {
  1295. PrintCommandLine( argc, argv );
  1296. Warning(
  1297. "usage : vbsp [options...] mapfile\n"
  1298. "example: vbsp -onlyents c:\\hl2\\hl2\\maps\\test\n"
  1299. "\n"
  1300. "Common options (use -v to see all options):\n"
  1301. "\n"
  1302. " -v (or -verbose): Turn on verbose output (also shows more command\n"
  1303. " line options).\n"
  1304. "\n"
  1305. " -onlyents : This option causes vbsp only import the entities from the .vmf\n"
  1306. " file. -onlyents won't reimport brush models.\n"
  1307. " -onlyprops : Only update the static props and detail props.\n"
  1308. " -glview : Writes .gl files in the current directory that can be viewed\n"
  1309. " with glview.exe. If you use -tmpout, it will write the files\n"
  1310. " into the \\tmp folder.\n"
  1311. " -nodetail : Get rid of all detail geometry. The geometry left over is\n"
  1312. " what affects visibility.\n"
  1313. " -nowater : Get rid of water brushes.\n"
  1314. " -staticpropcombine : Cluster specially supported static prop models.\n"
  1315. " -keepsources : Don't clean up cluster models after bspzip.\n"
  1316. " -staticpropcombine_considervis : Cluster static prop models only within\n"
  1317. " vis clusters.\n"
  1318. " -staticpropcombine_autocombine : Automatically combine simple static props\n"
  1319. " without an explicit combine rule.\n"
  1320. " -staticpropcombine_suggestrules: Suggest rules to add to spcombinerules.txt\n"
  1321. " -low : Run as an idle-priority process.\n"
  1322. "\n"
  1323. " -vproject <directory> : Override the VPROJECT environment variable.\n"
  1324. " -game <directory> : Same as -vproject.\n"
  1325. "\n" );
  1326. if ( verbose )
  1327. {
  1328. Warning(
  1329. "Other options :\n"
  1330. " -novconfig : Don't bring up graphical UI on vproject errors.\n"
  1331. " -threads : Control the number of threads vbsp uses (defaults to the # of\n"
  1332. " processors on your machine).\n"
  1333. " -verboseentities: If -v is on, this disables verbose output for submodels.\n"
  1334. " -noweld : Don't join face vertices together.\n"
  1335. " -nocsg : Don't chop out intersecting brush areas.\n"
  1336. " -noshare : Emit unique face edges instead of sharing them.\n"
  1337. " -notjunc : Don't fixup t-junctions.\n"
  1338. " -noopt : By default, vbsp removes the 'outer shell' of the map, which\n"
  1339. " are all the faces you can't see because you can never get\n"
  1340. " outside the map. -noopt disables this behaviour.\n"
  1341. " -noprune : Don't prune neighboring solid nodes.\n"
  1342. " -nomerge : Don't merge together chopped faces on nodes.\n"
  1343. " -nomergewater: Don't merge together chopped faces on water.\n"
  1344. " -nosubdiv : Don't subdivide faces for lightmapping.\n"
  1345. " -micro <#> : vbsp will warn when brushes are output with a volume less\n"
  1346. " than this number (default: 1.0).\n"
  1347. " -fulldetail : Mark all detail geometry as normal geometry (so all detail\n"
  1348. " geometry will affect visibility).\n"
  1349. " -alldetail : Convert all structural brushes to detail brushes, except\n"
  1350. " func_brush entities whose names begin with ""structure_"".\n"
  1351. );
  1352. Warning(
  1353. " -leaktest : Stop processing the map if a leak is detected. Whether or not\n"
  1354. " this flag is set, a leak file will be written out at\n"
  1355. " <vmf filename>.lin, and it can be imported into Hammer.\n"
  1356. " -bumpall : Force all surfaces to be bump mapped.\n"
  1357. " -snapaxial : Snap axial planes to integer coordinates.\n"
  1358. " -block # # : Control the grid size mins that vbsp chops the level on.\n"
  1359. " -blocks # # # # : Enter the mins and maxs for the grid size vbsp uses.\n"
  1360. " -blocksize # : Control the size of each grid square that vbsp chops the level on. Default is 1024."
  1361. " -dumpstaticprops: Dump static props to staticprop*.txt\n"
  1362. " -dumpcollide : Write files with collision info.\n"
  1363. " -forceskyvis : Enable vis calculations in 3d skybox leaves\n"
  1364. " -luxelscale # : Scale all lightmaps by this amount (default: 1.0).\n"
  1365. " -minluxelscale #: No luxel scale will be lower than this amount (default: 1.0).\n"
  1366. " -maxluxelscale #: No luxel scale will be higher than this amount (default: 999999.0).\n"
  1367. " -lightifmissing : Force lightmaps to be generated for all surfaces even if\n"
  1368. " they don't need lightmaps.\n"
  1369. " -keepstalezip : Keep the BSP's zip files intact but regenerate everything\n"
  1370. " else.\n"
  1371. " -virtualdispphysics : Use virtual (not precomputed) displacement collision\n"
  1372. " models\n"
  1373. " -visgranularity # # # : Force visibility splits # of units along X, Y, Z\n"
  1374. " -xbox : Enable mandatory xbox options\n"
  1375. " -x360 : Generate Xbox360 version of vsp\n"
  1376. " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
  1377. " -replacematerials : Substitute materials according to materialsub.txt in\n"
  1378. " content\\maps\n"
  1379. " -FullMinidumps : Write large minidumps on crash.\n"
  1380. );
  1381. }
  1382. DeleteCmdLine( argc, argv );
  1383. CmdLib_Cleanup();
  1384. exit( 1 );
  1385. }
  1386. start = Plat_FloatTime();
  1387. // Run in the background?
  1388. if( g_bLowPriority )
  1389. {
  1390. SetLowPriority();
  1391. }
  1392. ThreadSetDefault ();
  1393. numthreads = 1; // multiple threads aren't helping...
  1394. // Setup the logfile.
  1395. char logFile[512];
  1396. _snprintf( logFile, sizeof(logFile), "%s.log", source );
  1397. g_CmdLibFileLoggingListener.Open( logFile );
  1398. LoadPhysicsDLL();
  1399. LoadSurfaceProperties();
  1400. #if 0
  1401. Msg( "qdir: %s This is the the path of the initial source file \n", qdir );
  1402. Msg( "gamedir: %s This is the base engine + mod-specific game dir (e.g. d:/tf2/mytfmod/) \n", gamedir );
  1403. Msg( "basegamedir: %s This is the base engine + base game directory (e.g. e:/hl2/hl2/, or d:/tf2/tf2/ )\n", basegamedir );
  1404. #endif
  1405. sprintf( materialPath, "%smaterials", gamedir );
  1406. InitMaterialSystem( materialPath, CmdLib_GetFileSystemFactory() );
  1407. Msg( "materialPath: %s\n", materialPath );
  1408. // delete portal and line files
  1409. sprintf (path, "%s.prt", source);
  1410. remove (path);
  1411. sprintf (path, "%s.lin", source);
  1412. remove (path);
  1413. strcpy (name, ExpandArg (argv[i]));
  1414. const char *pszExtension = V_GetFileExtension( name );
  1415. if ( !pszExtension )
  1416. {
  1417. V_SetExtension( name, ".vmm", sizeof( name ) );
  1418. if ( !FileExists( name ) )
  1419. {
  1420. V_SetExtension( name, ".vmf", sizeof( name ) );
  1421. }
  1422. }
  1423. char platformBSPFileName[1024];
  1424. GetPlatformMapPath( source, platformBSPFileName, 0, 1024 );
  1425. // if we're combining materials, load the script file
  1426. if ( g_ReplaceMaterials )
  1427. {
  1428. LoadMaterialReplacementKeys( gamedir, mapbase );
  1429. }
  1430. //
  1431. // if onlyents, just grab the entites and resave
  1432. //
  1433. if (onlyents)
  1434. {
  1435. LoadBSPFile (platformBSPFileName);
  1436. num_entities = 0;
  1437. // Clear out the cubemap samples since they will be reparsed even with -onlyents
  1438. g_nCubemapSamples = 0;
  1439. // Mark as stale since the lighting could be screwed with new ents.
  1440. AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
  1441. LoadMapFile (name);
  1442. SetModelNumbers ();
  1443. SetLightStyles ();
  1444. // NOTE: If we ever precompute lighting for static props in
  1445. // vrad, EmitStaticProps should be removed here
  1446. // Emit static props found in the .vmf file
  1447. EmitStaticProps();
  1448. // NOTE: Don't deal with detail props here, it blows away lighting
  1449. // Recompute the skybox
  1450. ComputeBoundsNoSkybox();
  1451. // Make sure that we have a water lod control eneity if we have water in the map.
  1452. EnsurePresenceOfWaterLODControlEntity();
  1453. // Make sure the func_occluders have the appropriate data set
  1454. FixupOnlyEntsOccluderEntities();
  1455. // Doing this here because stuff abov may filter out entities
  1456. UnparseEntities ();
  1457. WriteBSPFile (platformBSPFileName);
  1458. }
  1459. else if (onlyprops)
  1460. {
  1461. // In the only props case, deal with static + detail props only
  1462. LoadBSPFile (platformBSPFileName);
  1463. LoadMapFile(name);
  1464. SetModelNumbers();
  1465. SetLightStyles();
  1466. // Emit static props found in the .vmf file
  1467. EmitStaticProps();
  1468. // Place detail props found in .vmf and based on material properties
  1469. LoadEmitDetailObjectDictionary( gamedir );
  1470. EmitDetailObjects();
  1471. WriteBSPFile (platformBSPFileName);
  1472. }
  1473. else
  1474. {
  1475. //
  1476. // start from scratch
  1477. //
  1478. // Load just the file system from the bsp
  1479. if( g_bKeepStaleZip && FileExists( platformBSPFileName ) )
  1480. {
  1481. LoadBSPFile_FileSystemOnly (platformBSPFileName);
  1482. // Mark as stale since the lighting could be screwed with new ents.
  1483. AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
  1484. }
  1485. LoadMapFile (name);
  1486. InsertVisibilitySplittingHintBrushes();
  1487. WorldVertexTransitionFixup();
  1488. Cubemap_FixupBrushSidesMaterials();
  1489. Cubemap_AttachDefaultCubemapToSpecularSides();
  1490. Cubemap_AddUnreferencedCubemaps();
  1491. SetModelNumbers ();
  1492. SetLightStyles ();
  1493. LoadEmitDetailObjectDictionary( gamedir );
  1494. AddDefaultStringtableDictionaries();
  1495. ProcessModels ();
  1496. }
  1497. end = Plat_FloatTime();
  1498. char str[512];
  1499. GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
  1500. Msg( "%s elapsed\n", str );
  1501. DeleteCmdLine( argc, argv );
  1502. ReleasePakFileLumps();
  1503. DeleteMaterialReplacementKeys();
  1504. ShutdownMaterialSystem();
  1505. CmdLib_Cleanup();
  1506. return 0;
  1507. }
  1508. /*
  1509. =============
  1510. main
  1511. ============
  1512. */
  1513. int main (int argc, char **argv)
  1514. {
  1515. // Install an exception handler.
  1516. SetupDefaultToolsMinidumpHandler();
  1517. return RunVBSP( argc, argv );
  1518. }