Team Fortress 2 Source Code as on 22/4/2020
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.

1443 lines
38 KiB

  1. //========= Copyright 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. extern float g_maxLightmapDimension;
  22. char source[1024];
  23. char mapbase[ 64 ];
  24. char name[1024];
  25. char materialPath[1024];
  26. vec_t microvolume = 1.0;
  27. qboolean noprune;
  28. qboolean glview;
  29. qboolean nodetail;
  30. qboolean fulldetail;
  31. qboolean onlyents;
  32. bool onlyprops;
  33. qboolean nomerge;
  34. qboolean nomergewater = false;
  35. qboolean nowater;
  36. qboolean nocsg;
  37. qboolean noweld;
  38. qboolean noshare;
  39. qboolean nosubdiv;
  40. qboolean notjunc;
  41. qboolean noopt;
  42. qboolean leaktest;
  43. qboolean verboseentities;
  44. qboolean dumpcollide = false;
  45. qboolean g_bLowPriority = false;
  46. qboolean g_DumpStaticProps = false;
  47. qboolean g_bSkyVis = false; // skybox vis is off by default, toggle this to enable it
  48. bool g_bLightIfMissing = false;
  49. bool g_snapAxialPlanes = false;
  50. bool g_bKeepStaleZip = false;
  51. bool g_NodrawTriggers = false;
  52. bool g_DisableWaterLighting = false;
  53. bool g_bAllowDetailCracks = false;
  54. bool g_bNoVirtualMesh = false;
  55. float g_defaultLuxelSize = DEFAULT_LUXEL_SIZE;
  56. float g_luxelScale = 1.0f;
  57. float g_minLuxelScale = 1.0f;
  58. bool g_BumpAll = false;
  59. int g_nDXLevel = 0; // default dxlevel if you don't specify it on the command-line.
  60. CUtlVector<int> g_SkyAreas;
  61. char outbase[32];
  62. char g_szEmbedDir[MAX_PATH] = { 0 };
  63. // HLTOOLS: Introduce these calcs to make the block algorithm proportional to the proper
  64. // world coordinate extents. Assumes square spatial constraints.
  65. #define BLOCKS_SIZE 1024
  66. #define BLOCKS_SPACE (COORD_EXTENT/BLOCKS_SIZE)
  67. #define BLOCKX_OFFSET ((BLOCKS_SPACE/2)+1)
  68. #define BLOCKY_OFFSET ((BLOCKS_SPACE/2)+1)
  69. #define BLOCKS_MIN (-(BLOCKS_SPACE/2))
  70. #define BLOCKS_MAX ((BLOCKS_SPACE/2)-1)
  71. int block_xl = BLOCKS_MIN, block_xh = BLOCKS_MAX, block_yl = BLOCKS_MIN, block_yh = BLOCKS_MAX;
  72. int entity_num;
  73. node_t *block_nodes[BLOCKS_SPACE+2][BLOCKS_SPACE+2];
  74. //-----------------------------------------------------------------------------
  75. // Assign occluder areas (must happen *after* the world model is processed)
  76. //-----------------------------------------------------------------------------
  77. void AssignOccluderAreas( tree_t *pTree );
  78. static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas );
  79. /*
  80. ============
  81. BlockTree
  82. ============
  83. */
  84. node_t *BlockTree (int xl, int yl, int xh, int yh)
  85. {
  86. node_t *node;
  87. Vector normal;
  88. float dist;
  89. int mid;
  90. if (xl == xh && yl == yh)
  91. {
  92. node = block_nodes[xl+BLOCKX_OFFSET][yl+BLOCKY_OFFSET];
  93. if (!node)
  94. { // return an empty leaf
  95. node = AllocNode ();
  96. node->planenum = PLANENUM_LEAF;
  97. node->contents = 0; //CONTENTS_SOLID;
  98. return node;
  99. }
  100. return node;
  101. }
  102. // create a seperator along the largest axis
  103. node = AllocNode ();
  104. if (xh - xl > yh - yl)
  105. { // split x axis
  106. mid = xl + (xh-xl)/2 + 1;
  107. normal[0] = 1;
  108. normal[1] = 0;
  109. normal[2] = 0;
  110. dist = mid*BLOCKS_SIZE;
  111. node->planenum = g_MainMap->FindFloatPlane (normal, dist);
  112. node->children[0] = BlockTree ( mid, yl, xh, yh);
  113. node->children[1] = BlockTree ( xl, yl, mid-1, yh);
  114. }
  115. else
  116. {
  117. mid = yl + (yh-yl)/2 + 1;
  118. normal[0] = 0;
  119. normal[1] = 1;
  120. normal[2] = 0;
  121. dist = mid*BLOCKS_SIZE;
  122. node->planenum = g_MainMap->FindFloatPlane (normal, dist);
  123. node->children[0] = BlockTree ( xl, mid, xh, yh);
  124. node->children[1] = BlockTree ( xl, yl, xh, mid-1);
  125. }
  126. return node;
  127. }
  128. /*
  129. ============
  130. ProcessBlock_Thread
  131. ============
  132. */
  133. int brush_start, brush_end;
  134. void ProcessBlock_Thread (int threadnum, int blocknum)
  135. {
  136. int xblock, yblock;
  137. Vector mins, maxs;
  138. bspbrush_t *brushes;
  139. tree_t *tree;
  140. node_t *node;
  141. yblock = block_yl + blocknum / (block_xh-block_xl+1);
  142. xblock = block_xl + blocknum % (block_xh-block_xl+1);
  143. qprintf ("############### block %2i,%2i ###############\n", xblock, yblock);
  144. mins[0] = xblock*BLOCKS_SIZE;
  145. mins[1] = yblock*BLOCKS_SIZE;
  146. mins[2] = MIN_COORD_INTEGER;
  147. maxs[0] = (xblock+1)*BLOCKS_SIZE;
  148. maxs[1] = (yblock+1)*BLOCKS_SIZE;
  149. maxs[2] = MAX_COORD_INTEGER;
  150. // the makelist and chopbrushes could be cached between the passes...
  151. brushes = MakeBspBrushList (brush_start, brush_end, mins, maxs, NO_DETAIL);
  152. if (!brushes)
  153. {
  154. node = AllocNode ();
  155. node->planenum = PLANENUM_LEAF;
  156. node->contents = CONTENTS_SOLID;
  157. block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = node;
  158. return;
  159. }
  160. FixupAreaportalWaterBrushes( brushes );
  161. if (!nocsg)
  162. brushes = ChopBrushes (brushes);
  163. tree = BrushBSP (brushes, mins, maxs);
  164. block_nodes[xblock+BLOCKX_OFFSET][yblock+BLOCKY_OFFSET] = tree->headnode;
  165. }
  166. /*
  167. ============
  168. ProcessWorldModel
  169. ============
  170. */
  171. void SplitSubdividedFaces( node_t *headnode ); // garymcthack
  172. void ProcessWorldModel (void)
  173. {
  174. entity_t *e;
  175. tree_t *tree = NULL;
  176. qboolean leaked;
  177. int optimize;
  178. int start;
  179. e = &entities[entity_num];
  180. brush_start = e->firstbrush;
  181. brush_end = brush_start + e->numbrushes;
  182. leaked = false;
  183. //
  184. // perform per-block operations
  185. //
  186. if (block_xh * BLOCKS_SIZE > g_MainMap->map_maxs[0])
  187. {
  188. block_xh = floor(g_MainMap->map_maxs[0]/BLOCKS_SIZE);
  189. }
  190. if ( (block_xl+1) * BLOCKS_SIZE < g_MainMap->map_mins[0])
  191. {
  192. block_xl = floor(g_MainMap->map_mins[0]/BLOCKS_SIZE);
  193. }
  194. if (block_yh * BLOCKS_SIZE > g_MainMap->map_maxs[1])
  195. {
  196. block_yh = floor(g_MainMap->map_maxs[1]/BLOCKS_SIZE);
  197. }
  198. if ( (block_yl+1) * BLOCKS_SIZE < g_MainMap->map_mins[1])
  199. {
  200. block_yl = floor(g_MainMap->map_mins[1]/BLOCKS_SIZE);
  201. }
  202. // HLTOOLS: updated to +/- MAX_COORD_INTEGER ( new world size limits / worldsize.h )
  203. if (block_xl < BLOCKS_MIN)
  204. {
  205. block_xl = BLOCKS_MIN;
  206. }
  207. if (block_yl < BLOCKS_MIN)
  208. {
  209. block_yl = BLOCKS_MIN;
  210. }
  211. if (block_xh > BLOCKS_MAX)
  212. {
  213. block_xh = BLOCKS_MAX;
  214. }
  215. if (block_yh > BLOCKS_MAX)
  216. {
  217. block_yh = BLOCKS_MAX;
  218. }
  219. for (optimize = 0 ; optimize <= 1 ; optimize++)
  220. {
  221. qprintf ("--------------------------------------------\n");
  222. RunThreadsOnIndividual ((block_xh-block_xl+1)*(block_yh-block_yl+1),
  223. !verbose, ProcessBlock_Thread);
  224. //
  225. // build the division tree
  226. // oversizing the blocks guarantees that all the boundaries
  227. // will also get nodes.
  228. //
  229. qprintf ("--------------------------------------------\n");
  230. tree = AllocTree ();
  231. tree->headnode = BlockTree (block_xl-1, block_yl-1, block_xh+1, block_yh+1);
  232. tree->mins[0] = (block_xl)*BLOCKS_SIZE;
  233. tree->mins[1] = (block_yl)*BLOCKS_SIZE;
  234. tree->mins[2] = g_MainMap->map_mins[2] - 8;
  235. tree->maxs[0] = (block_xh+1)*BLOCKS_SIZE;
  236. tree->maxs[1] = (block_yh+1)*BLOCKS_SIZE;
  237. tree->maxs[2] = g_MainMap->map_maxs[2] + 8;
  238. //
  239. // perform the global operations
  240. //
  241. // make the portals/faces by traversing down to each empty leaf
  242. MakeTreePortals (tree);
  243. if (FloodEntities (tree))
  244. {
  245. // turns everthing outside into solid
  246. FillOutside (tree->headnode);
  247. }
  248. else
  249. {
  250. Warning( ("**** leaked ****\n") );
  251. leaked = true;
  252. LeakFile (tree);
  253. if (leaktest)
  254. {
  255. Warning( ("--- MAP LEAKED ---\n") );
  256. exit (0);
  257. }
  258. }
  259. // mark the brush sides that actually turned into faces
  260. MarkVisibleSides (tree, brush_start, brush_end, NO_DETAIL);
  261. if (noopt || leaked)
  262. break;
  263. if (!optimize)
  264. {
  265. // If we are optimizing, free the tree. Next time we will construct it again, but
  266. // we'll use the information in MarkVisibleSides() so we'll only split with planes that
  267. // actually contribute renderable geometry
  268. FreeTree (tree);
  269. }
  270. }
  271. FloodAreas (tree);
  272. RemoveAreaPortalBrushes_R( tree->headnode );
  273. start = Plat_FloatTime();
  274. Msg("Building Faces...");
  275. // this turns portals with one solid side into faces
  276. // it also subdivides each face if necessary to fit max lightmap dimensions
  277. MakeFaces (tree->headnode);
  278. Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
  279. if (glview)
  280. {
  281. WriteGLView (tree, source);
  282. }
  283. AssignOccluderAreas( tree );
  284. Compute3DSkyboxAreas( tree->headnode, g_SkyAreas );
  285. face_t *pLeafFaceList = NULL;
  286. if ( !nodetail )
  287. {
  288. pLeafFaceList = MergeDetailTree( tree, brush_start, brush_end );
  289. }
  290. start = Plat_FloatTime();
  291. Msg("FixTjuncs...\n");
  292. // This unifies the vertex list for all edges (splits collinear edges to remove t-junctions)
  293. // It also welds the list of vertices out of each winding/portal and rounds nearly integer verts to integer
  294. pLeafFaceList = FixTjuncs (tree->headnode, pLeafFaceList);
  295. // this merges all of the solid nodes that have separating planes
  296. if (!noprune)
  297. {
  298. Msg("PruneNodes...\n");
  299. PruneNodes (tree->headnode);
  300. }
  301. // Msg( "SplitSubdividedFaces...\n" );
  302. // SplitSubdividedFaces( tree->headnode );
  303. Msg("WriteBSP...\n");
  304. WriteBSP (tree->headnode, pLeafFaceList);
  305. Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
  306. if (!leaked)
  307. {
  308. WritePortalFile (tree);
  309. }
  310. FreeTree( tree );
  311. FreeLeafFaces( pLeafFaceList );
  312. }
  313. /*
  314. ============
  315. ProcessSubModel
  316. ============
  317. */
  318. void ProcessSubModel( )
  319. {
  320. entity_t *e;
  321. int start, end;
  322. tree_t *tree;
  323. bspbrush_t *list;
  324. Vector mins, maxs;
  325. e = &entities[entity_num];
  326. start = e->firstbrush;
  327. end = start + e->numbrushes;
  328. mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
  329. maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
  330. list = MakeBspBrushList (start, end, mins, maxs, FULL_DETAIL);
  331. if (!nocsg)
  332. list = ChopBrushes (list);
  333. tree = BrushBSP (list, mins, maxs);
  334. // This would wind up crashing the engine because we'd have a negative leaf index in dmodel_t::headnode.
  335. if ( tree->headnode->planenum == PLANENUM_LEAF )
  336. {
  337. const char *pClassName = ValueForKey( e, "classname" );
  338. const char *pTargetName = ValueForKey( e, "targetname" );
  339. Error( "bmodel %d has no head node (class '%s', targetname '%s')", entity_num, pClassName, pTargetName );
  340. }
  341. MakeTreePortals (tree);
  342. #if DEBUG_BRUSHMODEL
  343. if ( entity_num == DEBUG_BRUSHMODEL )
  344. WriteGLView( tree, "tree_all" );
  345. #endif
  346. MarkVisibleSides (tree, start, end, FULL_DETAIL);
  347. MakeFaces (tree->headnode);
  348. FixTjuncs( tree->headnode, NULL );
  349. WriteBSP( tree->headnode, NULL );
  350. #if DEBUG_BRUSHMODEL
  351. if ( entity_num == DEBUG_BRUSHMODEL )
  352. {
  353. WriteGLView( tree, "tree_vis" );
  354. WriteGLViewFaces( tree, "tree_faces" );
  355. }
  356. #endif
  357. FreeTree (tree);
  358. }
  359. //-----------------------------------------------------------------------------
  360. // Returns true if the entity is a func_occluder
  361. //-----------------------------------------------------------------------------
  362. bool IsFuncOccluder( int entity_num )
  363. {
  364. entity_t *mapent = &entities[entity_num];
  365. const char *pClassName = ValueForKey( mapent, "classname" );
  366. return (strcmp("func_occluder", pClassName) == 0);
  367. }
  368. //-----------------------------------------------------------------------------
  369. // Computes the area of a brush's occluders
  370. //-----------------------------------------------------------------------------
  371. float ComputeOccluderBrushArea( mapbrush_t *pBrush )
  372. {
  373. float flArea = 0.0f;
  374. for ( int j = 0; j < pBrush->numsides; ++j )
  375. {
  376. side_t *pSide = &(pBrush->original_sides[j]);
  377. // Skip nodraw surfaces
  378. if ( texinfo[pSide->texinfo].flags & SURF_NODRAW )
  379. continue;
  380. if ( !pSide->winding )
  381. continue;
  382. flArea += WindingArea( pSide->winding );
  383. }
  384. return flArea;
  385. }
  386. //-----------------------------------------------------------------------------
  387. // Clips all occluder brushes against each other
  388. //-----------------------------------------------------------------------------
  389. static tree_t *ClipOccluderBrushes( )
  390. {
  391. // Create a list of all occluder brushes in the level
  392. CUtlVector< mapbrush_t * > mapBrushes( 1024, 1024 );
  393. for ( entity_num=0; entity_num < g_MainMap->num_entities; ++entity_num )
  394. {
  395. if (!IsFuncOccluder(entity_num))
  396. continue;
  397. entity_t *e = &entities[entity_num];
  398. int end = e->firstbrush + e->numbrushes;
  399. int i;
  400. for ( i = e->firstbrush; i < end; ++i )
  401. {
  402. mapBrushes.AddToTail( &g_MainMap->mapbrushes[i] );
  403. }
  404. }
  405. int nBrushCount = mapBrushes.Count();
  406. if ( nBrushCount == 0 )
  407. return NULL;
  408. Vector mins, maxs;
  409. mins[0] = mins[1] = mins[2] = MIN_COORD_INTEGER;
  410. maxs[0] = maxs[1] = maxs[2] = MAX_COORD_INTEGER;
  411. bspbrush_t *list = MakeBspBrushList( mapBrushes.Base(), nBrushCount, mins, maxs );
  412. if (!nocsg)
  413. list = ChopBrushes (list);
  414. tree_t *tree = BrushBSP (list, mins, maxs);
  415. MakeTreePortals (tree);
  416. MarkVisibleSides (tree, mapBrushes.Base(), nBrushCount);
  417. MakeFaces( tree->headnode );
  418. // NOTE: This will output the occluder face vertices + planes
  419. FixTjuncs( tree->headnode, NULL );
  420. return tree;
  421. }
  422. //-----------------------------------------------------------------------------
  423. // Generate a list of unique sides in the occluder tree
  424. //-----------------------------------------------------------------------------
  425. static void GenerateOccluderSideList( int nEntity, CUtlVector<side_t*> &occluderSides )
  426. {
  427. entity_t *e = &entities[nEntity];
  428. int end = e->firstbrush + e->numbrushes;
  429. int i, j;
  430. for ( i = e->firstbrush; i < end; ++i )
  431. {
  432. mapbrush_t *mb = &g_MainMap->mapbrushes[i];
  433. for ( j = 0; j < mb->numsides; ++j )
  434. {
  435. occluderSides.AddToTail( &(mb->original_sides[j]) );
  436. }
  437. }
  438. }
  439. //-----------------------------------------------------------------------------
  440. // Generate a list of unique faces in the occluder tree
  441. //-----------------------------------------------------------------------------
  442. static void GenerateOccluderFaceList( node_t *pOccluderNode, CUtlVector<face_t*> &occluderFaces )
  443. {
  444. if (pOccluderNode->planenum == PLANENUM_LEAF)
  445. return;
  446. for ( face_t *f=pOccluderNode->faces ; f ; f = f->next )
  447. {
  448. occluderFaces.AddToTail( f );
  449. }
  450. GenerateOccluderFaceList( pOccluderNode->children[0], occluderFaces );
  451. GenerateOccluderFaceList( pOccluderNode->children[1], occluderFaces );
  452. }
  453. //-----------------------------------------------------------------------------
  454. // For occluder area assignment
  455. //-----------------------------------------------------------------------------
  456. struct OccluderInfo_t
  457. {
  458. int m_nOccluderEntityIndex;
  459. };
  460. static CUtlVector< OccluderInfo_t > g_OccluderInfo;
  461. //-----------------------------------------------------------------------------
  462. // Emits occluder brushes
  463. //-----------------------------------------------------------------------------
  464. static void EmitOccluderBrushes()
  465. {
  466. char str[64];
  467. g_OccluderData.RemoveAll();
  468. g_OccluderPolyData.RemoveAll();
  469. g_OccluderVertexIndices.RemoveAll();
  470. tree_t *pOccluderTree = ClipOccluderBrushes();
  471. if (!pOccluderTree)
  472. return;
  473. CUtlVector<face_t*> faceList( 1024, 1024 );
  474. CUtlVector<side_t*> sideList( 1024, 1024 );
  475. GenerateOccluderFaceList( pOccluderTree->headnode, faceList );
  476. #ifdef _DEBUG
  477. int *pEmitted = (int*)stackalloc( faceList.Count() * sizeof(int) );
  478. memset( pEmitted, 0, faceList.Count() * sizeof(int) );
  479. #endif
  480. for ( entity_num=1; entity_num < num_entities; ++entity_num )
  481. {
  482. if (!IsFuncOccluder(entity_num))
  483. continue;
  484. // Output only those parts of the occluder tree which are a part of the brush
  485. int nOccluder = g_OccluderData.AddToTail();
  486. doccluderdata_t &occluderData = g_OccluderData[ nOccluder ];
  487. occluderData.firstpoly = g_OccluderPolyData.Count();
  488. occluderData.mins.Init( FLT_MAX, FLT_MAX, FLT_MAX );
  489. occluderData.maxs.Init( -FLT_MAX, -FLT_MAX, -FLT_MAX );
  490. occluderData.flags = 0;
  491. occluderData.area = -1;
  492. // NOTE: If you change the algorithm by which occluder numbers are allocated,
  493. // then you must also change FixupOnlyEntsOccluderEntities() below
  494. sprintf (str, "%i", nOccluder);
  495. SetKeyValue (&entities[entity_num], "occludernumber", str);
  496. int nIndex = g_OccluderInfo.AddToTail();
  497. g_OccluderInfo[nIndex].m_nOccluderEntityIndex = entity_num;
  498. sideList.RemoveAll();
  499. GenerateOccluderSideList( entity_num, sideList );
  500. for ( int i = faceList.Count(); --i >= 0; )
  501. {
  502. // Skip nodraw surfaces, but not triggers that have been marked as nodraw
  503. face_t *f = faceList[i];
  504. if ( ( texinfo[f->texinfo].flags & SURF_NODRAW ) &&
  505. (( texinfo[f->texinfo].flags & SURF_TRIGGER ) == 0 ) )
  506. continue;
  507. // Only emit faces that appear in the side list of the occluder
  508. for ( int j = sideList.Count(); --j >= 0; )
  509. {
  510. if ( sideList[j] != f->originalface )
  511. continue;
  512. if ( f->numpoints < 3 )
  513. continue;
  514. // not a final face
  515. Assert ( !f->merged && !f->split[0] && !f->split[1] );
  516. #ifdef _DEBUG
  517. Assert( !pEmitted[i] );
  518. pEmitted[i] = entity_num;
  519. #endif
  520. int k = g_OccluderPolyData.AddToTail();
  521. doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[k];
  522. pOccluderPoly->planenum = f->planenum;
  523. pOccluderPoly->vertexcount = f->numpoints;
  524. pOccluderPoly->firstvertexindex = g_OccluderVertexIndices.Count();
  525. for( k = 0; k < f->numpoints; ++k )
  526. {
  527. g_OccluderVertexIndices.AddToTail( f->vertexnums[k] );
  528. const Vector &p = dvertexes[f->vertexnums[k]].point;
  529. VectorMin( occluderData.mins, p, occluderData.mins );
  530. VectorMax( occluderData.maxs, p, occluderData.maxs );
  531. }
  532. break;
  533. }
  534. }
  535. occluderData.polycount = g_OccluderPolyData.Count() - occluderData.firstpoly;
  536. // Mark this brush as not having brush geometry so it won't be re-emitted with a brush model
  537. entities[entity_num].numbrushes = 0;
  538. }
  539. FreeTree( pOccluderTree );
  540. }
  541. //-----------------------------------------------------------------------------
  542. // Set occluder area
  543. //-----------------------------------------------------------------------------
  544. void SetOccluderArea( int nOccluder, int nArea, int nEntityNum )
  545. {
  546. if ( g_OccluderData[nOccluder].area <= 0 )
  547. {
  548. g_OccluderData[nOccluder].area = nArea;
  549. }
  550. else if ( (nArea != 0) && (g_OccluderData[nOccluder].area != nArea) )
  551. {
  552. const char *pTargetName = ValueForKey( &entities[nEntityNum], "targetname" );
  553. if (!pTargetName)
  554. {
  555. pTargetName = "<no name>";
  556. }
  557. Warning("Occluder \"%s\" straddles multiple areas. This is invalid!\n", pTargetName );
  558. }
  559. }
  560. //-----------------------------------------------------------------------------
  561. // Assign occluder areas (must happen *after* the world model is processed)
  562. //-----------------------------------------------------------------------------
  563. void AssignAreaToOccluder( int nOccluder, tree_t *pTree, bool bCrossAreaPortals )
  564. {
  565. int nFirstPoly = g_OccluderData[nOccluder].firstpoly;
  566. int nEntityNum = g_OccluderInfo[nOccluder].m_nOccluderEntityIndex;
  567. for ( int j = 0; j < g_OccluderData[nOccluder].polycount; ++j )
  568. {
  569. doccluderpolydata_t *pOccluderPoly = &g_OccluderPolyData[nFirstPoly + j];
  570. int nFirstVertex = pOccluderPoly->firstvertexindex;
  571. for ( int k = 0; k < pOccluderPoly->vertexcount; ++k )
  572. {
  573. int nVertexIndex = g_OccluderVertexIndices[nFirstVertex + k];
  574. node_t *pNode = NodeForPoint( pTree->headnode, dvertexes[ nVertexIndex ].point );
  575. SetOccluderArea( nOccluder, pNode->area, nEntityNum );
  576. int nOtherSideIndex;
  577. portal_t *pPortal;
  578. for ( pPortal = pNode->portals; pPortal; pPortal = pPortal->next[!nOtherSideIndex] )
  579. {
  580. nOtherSideIndex = (pPortal->nodes[0] == pNode) ? 1 : 0;
  581. if (!pPortal->onnode)
  582. continue; // edge of world
  583. // Don't cross over area portals for the area check
  584. if ((!bCrossAreaPortals) && pPortal->nodes[nOtherSideIndex]->contents & CONTENTS_AREAPORTAL)
  585. continue;
  586. int nAdjacentArea = pPortal->nodes[nOtherSideIndex] ? pPortal->nodes[nOtherSideIndex]->area : 0;
  587. SetOccluderArea( nOccluder, nAdjacentArea, nEntityNum );
  588. }
  589. }
  590. }
  591. }
  592. //-----------------------------------------------------------------------------
  593. // Assign occluder areas (must happen *after* the world model is processed)
  594. //-----------------------------------------------------------------------------
  595. void AssignOccluderAreas( tree_t *pTree )
  596. {
  597. for ( int i = 0; i < g_OccluderData.Count(); ++i )
  598. {
  599. AssignAreaToOccluder( i, pTree, false );
  600. // This can only have happened if the only valid portal out leads into an areaportal
  601. if ( g_OccluderData[i].area <= 0 )
  602. {
  603. AssignAreaToOccluder( i, pTree, true );
  604. }
  605. }
  606. }
  607. //-----------------------------------------------------------------------------
  608. // Make sure the func_occluders have the appropriate data set
  609. //-----------------------------------------------------------------------------
  610. void FixupOnlyEntsOccluderEntities()
  611. {
  612. char str[64];
  613. int nOccluder = 0;
  614. for ( entity_num=1; entity_num < num_entities; ++entity_num )
  615. {
  616. if (!IsFuncOccluder(entity_num))
  617. continue;
  618. // NOTE: If you change the algorithm by which occluder numbers are allocated above,
  619. // then you must also change this
  620. sprintf (str, "%i", nOccluder);
  621. SetKeyValue (&entities[entity_num], "occludernumber", str);
  622. ++nOccluder;
  623. }
  624. }
  625. void MarkNoDynamicShadowSides()
  626. {
  627. for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
  628. {
  629. g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = true;
  630. }
  631. for ( int i=0; i < g_NoDynamicShadowSides.Count(); i++ )
  632. {
  633. int brushSideID = g_NoDynamicShadowSides[i];
  634. // Find the side with this ID.
  635. for ( int iSide=0; iSide < g_MainMap->nummapbrushsides; iSide++ )
  636. {
  637. if ( g_MainMap->brushsides[iSide].id == brushSideID )
  638. g_MainMap->brushsides[iSide].m_bDynamicShadowsEnabled = false;
  639. }
  640. }
  641. }
  642. //-----------------------------------------------------------------------------
  643. // Compute the 3D skybox areas
  644. //-----------------------------------------------------------------------------
  645. static void Compute3DSkyboxAreas( node_t *headnode, CUtlVector<int>& areas )
  646. {
  647. for (int i = 0; i < g_MainMap->num_entities; ++i)
  648. {
  649. char* pEntity = ValueForKey(&entities[i], "classname");
  650. if (!strcmp(pEntity, "sky_camera"))
  651. {
  652. // Found a 3D skybox camera, get a leaf that lies in it
  653. node_t *pLeaf = PointInLeaf( headnode, entities[i].origin );
  654. if (pLeaf->contents & CONTENTS_SOLID)
  655. {
  656. 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);
  657. }
  658. areas.AddToTail( pLeaf->area );
  659. }
  660. }
  661. }
  662. bool Is3DSkyboxArea( int area )
  663. {
  664. for ( int i = g_SkyAreas.Count(); --i >=0; )
  665. {
  666. if ( g_SkyAreas[i] == area )
  667. return true;
  668. }
  669. return false;
  670. }
  671. /*
  672. ============
  673. ProcessModels
  674. ============
  675. */
  676. void ProcessModels (void)
  677. {
  678. BeginBSPFile ();
  679. // Mark sides that have no dynamic shadows.
  680. MarkNoDynamicShadowSides();
  681. // emit the displacement surfaces
  682. EmitInitialDispInfos();
  683. // Clip occluder brushes against each other,
  684. // Remove them from the list of models to process below
  685. EmitOccluderBrushes( );
  686. for ( entity_num=0; entity_num < num_entities; ++entity_num )
  687. {
  688. entity_t *pEntity = &entities[entity_num];
  689. if ( !pEntity->numbrushes )
  690. continue;
  691. qprintf ("############### model %i ###############\n", nummodels);
  692. BeginModel ();
  693. if (entity_num == 0)
  694. {
  695. ProcessWorldModel();
  696. }
  697. else
  698. {
  699. ProcessSubModel( );
  700. }
  701. EndModel ();
  702. if (!verboseentities)
  703. {
  704. verbose = false; // don't bother printing submodels
  705. }
  706. }
  707. // Turn the skybox into a cubemap in case we don't build env_cubemap textures.
  708. Cubemap_CreateDefaultCubemaps();
  709. EndBSPFile ();
  710. }
  711. void LoadPhysicsDLL( void )
  712. {
  713. PhysicsDLLPath( "vphysics.dll" );
  714. }
  715. void PrintCommandLine( int argc, char **argv )
  716. {
  717. Warning( "Command line: " );
  718. for ( int z=0; z < argc; z++ )
  719. {
  720. Warning( "\"%s\" ", argv[z] );
  721. }
  722. Warning( "\n\n" );
  723. }
  724. int RunVBSP( int argc, char **argv )
  725. {
  726. int i;
  727. double start, end;
  728. char path[1024];
  729. CommandLine()->CreateCmdLine( argc, argv );
  730. MathLib_Init( 2.2f, 2.2f, 0.0f, OVERBRIGHT, false, false, false, false );
  731. InstallSpewFunction();
  732. SpewActivate( "developer", 1 );
  733. CmdLib_InitFileSystem( argv[ argc-1 ] );
  734. Q_StripExtension( ExpandArg( argv[ argc-1 ] ), source, sizeof( source ) );
  735. Q_FileBase( source, mapbase, sizeof( mapbase ) );
  736. strlwr( mapbase );
  737. // Maintaining legacy behavior here to avoid breaking tools: regardless of the extension we are passed, we strip it
  738. // to get the "source" name, and append extensions as desired...
  739. char mapFile[1024];
  740. V_strncpy( mapFile, source, sizeof( mapFile ) );
  741. V_strncat( mapFile, ".bsp", sizeof( mapFile ) );
  742. LoadCmdLineFromFile( argc, argv, mapbase, "vbsp" );
  743. Msg( "Valve Software - vbsp.exe (%s)\n", __DATE__ );
  744. for (i=1 ; i<argc ; i++)
  745. {
  746. if (!stricmp(argv[i],"-threads"))
  747. {
  748. numthreads = atoi (argv[i+1]);
  749. i++;
  750. }
  751. else if (!Q_stricmp(argv[i],"-glview"))
  752. {
  753. glview = true;
  754. }
  755. else if ( !Q_stricmp(argv[i], "-v") || !Q_stricmp(argv[i], "-verbose") )
  756. {
  757. Msg("verbose = true\n");
  758. verbose = true;
  759. }
  760. else if (!Q_stricmp(argv[i], "-noweld"))
  761. {
  762. Msg ("noweld = true\n");
  763. noweld = true;
  764. }
  765. else if (!Q_stricmp(argv[i], "-nocsg"))
  766. {
  767. Msg ("nocsg = true\n");
  768. nocsg = true;
  769. }
  770. else if (!Q_stricmp(argv[i], "-noshare"))
  771. {
  772. Msg ("noshare = true\n");
  773. noshare = true;
  774. }
  775. else if (!Q_stricmp(argv[i], "-notjunc"))
  776. {
  777. Msg ("notjunc = true\n");
  778. notjunc = true;
  779. }
  780. else if (!Q_stricmp(argv[i], "-nowater"))
  781. {
  782. Msg ("nowater = true\n");
  783. nowater = true;
  784. }
  785. else if (!Q_stricmp(argv[i], "-noopt"))
  786. {
  787. Msg ("noopt = true\n");
  788. noopt = true;
  789. }
  790. else if (!Q_stricmp(argv[i], "-noprune"))
  791. {
  792. Msg ("noprune = true\n");
  793. noprune = true;
  794. }
  795. else if (!Q_stricmp(argv[i], "-nomerge"))
  796. {
  797. Msg ("nomerge = true\n");
  798. nomerge = true;
  799. }
  800. else if (!Q_stricmp(argv[i], "-nomergewater"))
  801. {
  802. Msg ("nomergewater = true\n");
  803. nomergewater = true;
  804. }
  805. else if (!Q_stricmp(argv[i], "-nosubdiv"))
  806. {
  807. Msg ("nosubdiv = true\n");
  808. nosubdiv = true;
  809. }
  810. else if (!Q_stricmp(argv[i], "-nodetail"))
  811. {
  812. Msg ("nodetail = true\n");
  813. nodetail = true;
  814. }
  815. else if (!Q_stricmp(argv[i], "-fulldetail"))
  816. {
  817. Msg ("fulldetail = true\n");
  818. fulldetail = true;
  819. }
  820. else if (!Q_stricmp(argv[i], "-onlyents"))
  821. {
  822. Msg ("onlyents = true\n");
  823. onlyents = true;
  824. }
  825. else if (!Q_stricmp(argv[i], "-onlyprops"))
  826. {
  827. Msg ("onlyprops = true\n");
  828. onlyprops = true;
  829. }
  830. else if (!Q_stricmp(argv[i], "-micro"))
  831. {
  832. microvolume = atof(argv[i+1]);
  833. Msg ("microvolume = %f\n", microvolume);
  834. i++;
  835. }
  836. else if (!Q_stricmp(argv[i], "-leaktest"))
  837. {
  838. Msg ("leaktest = true\n");
  839. leaktest = true;
  840. }
  841. else if (!Q_stricmp(argv[i], "-verboseentities"))
  842. {
  843. Msg ("verboseentities = true\n");
  844. verboseentities = true;
  845. }
  846. else if (!Q_stricmp(argv[i], "-snapaxial"))
  847. {
  848. Msg ("snap axial = true\n");
  849. g_snapAxialPlanes = true;
  850. }
  851. #if 0
  852. else if (!Q_stricmp(argv[i], "-maxlightmapdim"))
  853. {
  854. g_maxLightmapDimension = atof(argv[i+1]);
  855. Msg ("g_maxLightmapDimension = %f\n", g_maxLightmapDimension);
  856. i++;
  857. }
  858. #endif
  859. else if (!Q_stricmp(argv[i], "-block"))
  860. {
  861. block_xl = block_xh = atoi(argv[i+1]);
  862. block_yl = block_yh = atoi(argv[i+2]);
  863. Msg ("block: %i,%i\n", block_xl, block_yl);
  864. i+=2;
  865. }
  866. else if (!Q_stricmp(argv[i], "-blocks"))
  867. {
  868. block_xl = atoi(argv[i+1]);
  869. block_yl = atoi(argv[i+2]);
  870. block_xh = atoi(argv[i+3]);
  871. block_yh = atoi(argv[i+4]);
  872. Msg ("blocks: %i,%i to %i,%i\n",
  873. block_xl, block_yl, block_xh, block_yh);
  874. i+=4;
  875. }
  876. else if ( !Q_stricmp( argv[i], "-dumpcollide" ) )
  877. {
  878. Msg("Dumping collision models to collideXXX.txt\n" );
  879. dumpcollide = true;
  880. }
  881. else if ( !Q_stricmp( argv[i], "-dumpstaticprop" ) )
  882. {
  883. Msg("Dumping static props to staticpropXXX.txt\n" );
  884. g_DumpStaticProps = true;
  885. }
  886. else if ( !Q_stricmp( argv[i], "-forceskyvis" ) )
  887. {
  888. Msg("Enabled vis in 3d skybox\n" );
  889. g_bSkyVis = true;
  890. }
  891. else if (!Q_stricmp (argv[i],"-tmpout"))
  892. {
  893. strcpy (outbase, "/tmp");
  894. }
  895. #if 0
  896. else if( !Q_stricmp( argv[i], "-defaultluxelsize" ) )
  897. {
  898. g_defaultLuxelSize = atof( argv[i+1] );
  899. i++;
  900. }
  901. #endif
  902. else if( !Q_stricmp( argv[i], "-luxelscale" ) )
  903. {
  904. g_luxelScale = atof( argv[i+1] );
  905. i++;
  906. }
  907. else if( !strcmp( argv[i], "-minluxelscale" ) )
  908. {
  909. g_minLuxelScale = atof( argv[i+1] );
  910. if (g_minLuxelScale < 1)
  911. g_minLuxelScale = 1;
  912. i++;
  913. }
  914. else if( !Q_stricmp( argv[i], "-dxlevel" ) )
  915. {
  916. g_nDXLevel = atoi( argv[i+1] );
  917. Msg( "DXLevel = %d\n", g_nDXLevel );
  918. i++;
  919. }
  920. else if( !Q_stricmp( argv[i], "-bumpall" ) )
  921. {
  922. g_BumpAll = true;
  923. }
  924. else if( !Q_stricmp( argv[i], "-low" ) )
  925. {
  926. g_bLowPriority = true;
  927. }
  928. else if( !Q_stricmp( argv[i], "-lightifmissing" ) )
  929. {
  930. g_bLightIfMissing = true;
  931. }
  932. else if ( !Q_stricmp( argv[i], CMDLINEOPTION_NOVCONFIG ) )
  933. {
  934. }
  935. else if ( !Q_stricmp( argv[i], "-allowdebug" ) || !Q_stricmp( argv[i], "-steam" ) )
  936. {
  937. // nothing to do here, but don't bail on this option
  938. }
  939. else if ( !Q_stricmp( argv[i], "-vproject" ) || !Q_stricmp( argv[i], "-game" ) || !Q_stricmp( argv[i], "-insert_search_path" ) )
  940. {
  941. ++i;
  942. }
  943. else if ( !Q_stricmp( argv[i], "-keepstalezip" ) )
  944. {
  945. g_bKeepStaleZip = true;
  946. }
  947. else if ( !Q_stricmp( argv[i], "-xbox" ) )
  948. {
  949. // enable mandatory xbox extensions
  950. g_NodrawTriggers = true;
  951. g_DisableWaterLighting = true;
  952. }
  953. else if ( !Q_stricmp( argv[i], "-allowdetailcracks"))
  954. {
  955. g_bAllowDetailCracks = true;
  956. }
  957. else if ( !Q_stricmp( argv[i], "-novirtualmesh"))
  958. {
  959. g_bNoVirtualMesh = true;
  960. }
  961. else if ( !Q_stricmp( argv[i], "-replacematerials" ) )
  962. {
  963. g_ReplaceMaterials = true;
  964. }
  965. else if ( !Q_stricmp(argv[i], "-nodrawtriggers") )
  966. {
  967. g_NodrawTriggers = true;
  968. }
  969. else if ( !Q_stricmp( argv[i], "-FullMinidumps" ) )
  970. {
  971. EnableFullMinidumps( true );
  972. }
  973. else if ( !Q_stricmp( argv[i], "-embed" ) && i < argc - 1 )
  974. {
  975. V_MakeAbsolutePath( g_szEmbedDir, sizeof( g_szEmbedDir ), argv[++i], "." );
  976. V_FixSlashes( g_szEmbedDir );
  977. if ( !V_RemoveDotSlashes( g_szEmbedDir ) )
  978. {
  979. Error( "Bad -embed - Can't resolve pathname for '%s'", g_szEmbedDir );
  980. break;
  981. }
  982. V_StripTrailingSlash( g_szEmbedDir );
  983. g_pFullFileSystem->AddSearchPath( g_szEmbedDir, "GAME", PATH_ADD_TO_TAIL );
  984. g_pFullFileSystem->AddSearchPath( g_szEmbedDir, "MOD", PATH_ADD_TO_TAIL );
  985. }
  986. else if (argv[i][0] == '-')
  987. {
  988. Warning("VBSP: Unknown option \"%s\"\n\n", argv[i]);
  989. i = 100000; // force it to print the usage
  990. break;
  991. }
  992. else
  993. break;
  994. }
  995. if (i != argc - 1)
  996. {
  997. PrintCommandLine( argc, argv );
  998. Warning(
  999. "usage : vbsp [options...] mapfile\n"
  1000. "example: vbsp -onlyents c:\\hl2\\hl2\\maps\\test\n"
  1001. "\n"
  1002. "Common options (use -v to see all options):\n"
  1003. "\n"
  1004. " -v (or -verbose): Turn on verbose output (also shows more command\n"
  1005. " line options).\n"
  1006. "\n"
  1007. " -onlyents : This option causes vbsp only import the entities from the .vmf\n"
  1008. " file. -onlyents won't reimport brush models.\n"
  1009. " -onlyprops : Only update the static props and detail props.\n"
  1010. " -glview : Writes .gl files in the current directory that can be viewed\n"
  1011. " with glview.exe. If you use -tmpout, it will write the files\n"
  1012. " into the \\tmp folder.\n"
  1013. " -nodetail : Get rid of all detail geometry. The geometry left over is\n"
  1014. " what affects visibility.\n"
  1015. " -nowater : Get rid of water brushes.\n"
  1016. " -low : Run as an idle-priority process.\n"
  1017. " -embed <directory> : Use <directory> as an additional search path for assets\n"
  1018. " and embed all assets in this directory into the compiled\n"
  1019. " map\n"
  1020. "\n"
  1021. " -vproject <directory> : Override the VPROJECT environment variable.\n"
  1022. " -game <directory> : Same as -vproject.\n"
  1023. "\n" );
  1024. if ( verbose )
  1025. {
  1026. Warning(
  1027. "Other options :\n"
  1028. " -novconfig : Don't bring up graphical UI on vproject errors.\n"
  1029. " -threads : Control the number of threads vbsp uses (defaults to the # of\n"
  1030. " processors on your machine).\n"
  1031. " -verboseentities: If -v is on, this disables verbose output for submodels.\n"
  1032. " -noweld : Don't join face vertices together.\n"
  1033. " -nocsg : Don't chop out intersecting brush areas.\n"
  1034. " -noshare : Emit unique face edges instead of sharing them.\n"
  1035. " -notjunc : Don't fixup t-junctions.\n"
  1036. " -noopt : By default, vbsp removes the 'outer shell' of the map, which\n"
  1037. " are all the faces you can't see because you can never get\n"
  1038. " outside the map. -noopt disables this behaviour.\n"
  1039. " -noprune : Don't prune neighboring solid nodes.\n"
  1040. " -nomerge : Don't merge together chopped faces on nodes.\n"
  1041. " -nomergewater: Don't merge together chopped faces on water.\n"
  1042. " -nosubdiv : Don't subdivide faces for lightmapping.\n"
  1043. " -micro <#> : vbsp will warn when brushes are output with a volume less\n"
  1044. " than this number (default: 1.0).\n"
  1045. " -fulldetail : Mark all detail geometry as normal geometry (so all detail\n"
  1046. " geometry will affect visibility).\n"
  1047. " -leaktest : Stop processing the map if a leak is detected. Whether or not\n"
  1048. " this flag is set, a leak file will be written out at\n"
  1049. " <vmf filename>.lin, and it can be imported into Hammer.\n"
  1050. " -bumpall : Force all surfaces to be bump mapped.\n"
  1051. " -snapaxial : Snap axial planes to integer coordinates.\n"
  1052. " -block # # : Control the grid size mins that vbsp chops the level on.\n"
  1053. " -blocks # # # # : Enter the mins and maxs for the grid size vbsp uses.\n"
  1054. " -dumpstaticprops: Dump static props to staticprop*.txt\n"
  1055. " -dumpcollide : Write files with collision info.\n"
  1056. " -forceskyvis : Enable vis calculations in 3d skybox leaves\n"
  1057. " -luxelscale # : Scale all lightmaps by this amount (default: 1.0).\n"
  1058. " -minluxelscale #: No luxel scale will be lower than this amount (default: 1.0).\n"
  1059. " -lightifmissing : Force lightmaps to be generated for all surfaces even if\n"
  1060. " they don't need lightmaps.\n"
  1061. " -keepstalezip : Keep the BSP's zip files intact but regenerate everything\n"
  1062. " else.\n"
  1063. " -virtualdispphysics : Use virtual (not precomputed) displacement collision models\n"
  1064. " -xbox : Enable mandatory xbox options\n"
  1065. " -x360 : Generate Xbox360 version of vsp\n"
  1066. " -nox360 : Disable generation Xbox360 version of vsp (default)\n"
  1067. " -replacematerials : Substitute materials according to materialsub.txt in content\\maps\n"
  1068. " -FullMinidumps : Write large minidumps on crash.\n"
  1069. );
  1070. }
  1071. DeleteCmdLine( argc, argv );
  1072. CmdLib_Cleanup();
  1073. CmdLib_Exit( 1 );
  1074. }
  1075. // Sanity check
  1076. if ( *g_szEmbedDir && ( onlyents || onlyprops ) )
  1077. {
  1078. Warning( "-embed only makes sense alongside full BSP compiles.\n"
  1079. "\n"
  1080. "Use the bspzip utility to update embedded files.\n" );
  1081. DeleteCmdLine( argc, argv );
  1082. CmdLib_Cleanup();
  1083. CmdLib_Exit( 1 );
  1084. }
  1085. start = Plat_FloatTime();
  1086. // Run in the background?
  1087. if( g_bLowPriority )
  1088. {
  1089. SetLowPriority();
  1090. }
  1091. if( ( g_nDXLevel != 0 ) && ( g_nDXLevel < 80 ) )
  1092. {
  1093. g_BumpAll = false;
  1094. }
  1095. if( g_luxelScale == 1.0f )
  1096. {
  1097. if ( g_nDXLevel == 70 )
  1098. {
  1099. g_luxelScale = 4.0f;
  1100. }
  1101. }
  1102. ThreadSetDefault ();
  1103. numthreads = 1; // multiple threads aren't helping...
  1104. // Setup the logfile.
  1105. char logFile[512];
  1106. _snprintf( logFile, sizeof(logFile), "%s.log", source );
  1107. SetSpewFunctionLogFile( logFile );
  1108. LoadPhysicsDLL();
  1109. LoadSurfaceProperties();
  1110. #if 0
  1111. Msg( "qdir: %s This is the the path of the initial source file \n", qdir );
  1112. Msg( "gamedir: %s This is the base engine + mod-specific game dir (e.g. d:/tf2/mytfmod/) \n", gamedir );
  1113. Msg( "basegamedir: %s This is the base engine + base game directory (e.g. e:/hl2/hl2/, or d:/tf2/tf2/ )\n", basegamedir );
  1114. #endif
  1115. sprintf( materialPath, "%smaterials", gamedir );
  1116. InitMaterialSystem( materialPath, CmdLib_GetFileSystemFactory() );
  1117. Msg( "materialPath: %s\n", materialPath );
  1118. // delete portal and line files
  1119. sprintf (path, "%s.prt", source);
  1120. remove (path);
  1121. sprintf (path, "%s.lin", source);
  1122. remove (path);
  1123. strcpy (name, ExpandArg (argv[i]));
  1124. const char *pszExtension = V_GetFileExtension( name );
  1125. if ( !pszExtension )
  1126. {
  1127. V_SetExtension( name, ".vmm", sizeof( name ) );
  1128. if ( !FileExists( name ) )
  1129. {
  1130. V_SetExtension( name, ".vmf", sizeof( name ) );
  1131. }
  1132. }
  1133. // if we're combining materials, load the script file
  1134. if ( g_ReplaceMaterials )
  1135. {
  1136. LoadMaterialReplacementKeys( gamedir, mapbase );
  1137. }
  1138. //
  1139. // if onlyents, just grab the entites and resave
  1140. //
  1141. if (onlyents)
  1142. {
  1143. LoadBSPFile (mapFile);
  1144. num_entities = 0;
  1145. // Clear out the cubemap samples since they will be reparsed even with -onlyents
  1146. g_nCubemapSamples = 0;
  1147. // Mark as stale since the lighting could be screwed with new ents.
  1148. AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
  1149. LoadMapFile (name);
  1150. SetModelNumbers ();
  1151. SetLightStyles ();
  1152. // NOTE: If we ever precompute lighting for static props in
  1153. // vrad, EmitStaticProps should be removed here
  1154. // Emit static props found in the .vmf file
  1155. EmitStaticProps();
  1156. // NOTE: Don't deal with detail props here, it blows away lighting
  1157. // Recompute the skybox
  1158. ComputeBoundsNoSkybox();
  1159. // Make sure that we have a water lod control eneity if we have water in the map.
  1160. EnsurePresenceOfWaterLODControlEntity();
  1161. // Make sure the func_occluders have the appropriate data set
  1162. FixupOnlyEntsOccluderEntities();
  1163. // Doing this here because stuff abov may filter out entities
  1164. UnparseEntities ();
  1165. WriteBSPFile (mapFile);
  1166. }
  1167. else if (onlyprops)
  1168. {
  1169. // In the only props case, deal with static + detail props only
  1170. LoadBSPFile (mapFile);
  1171. LoadMapFile(name);
  1172. SetModelNumbers();
  1173. SetLightStyles();
  1174. // Emit static props found in the .vmf file
  1175. EmitStaticProps();
  1176. // Place detail props found in .vmf and based on material properties
  1177. LoadEmitDetailObjectDictionary( gamedir );
  1178. EmitDetailObjects();
  1179. WriteBSPFile (mapFile);
  1180. }
  1181. else
  1182. {
  1183. //
  1184. // start from scratch
  1185. //
  1186. // Load just the file system from the bsp
  1187. if( g_bKeepStaleZip && FileExists( mapFile ) )
  1188. {
  1189. LoadBSPFile_FileSystemOnly (mapFile);
  1190. // Mark as stale since the lighting could be screwed with new ents.
  1191. AddBufferToPak( GetPakFile(), "stale.txt", "stale", strlen( "stale" ) + 1, false );
  1192. }
  1193. LoadMapFile (name);
  1194. WorldVertexTransitionFixup();
  1195. if( ( g_nDXLevel == 0 ) || ( g_nDXLevel >= 70 ) )
  1196. {
  1197. Cubemap_FixupBrushSidesMaterials();
  1198. Cubemap_AttachDefaultCubemapToSpecularSides();
  1199. Cubemap_AddUnreferencedCubemaps();
  1200. }
  1201. SetModelNumbers ();
  1202. SetLightStyles ();
  1203. LoadEmitDetailObjectDictionary( gamedir );
  1204. ProcessModels ();
  1205. // Add embed dir if provided
  1206. if ( *g_szEmbedDir )
  1207. {
  1208. AddDirToPak( GetPakFile(), g_szEmbedDir );
  1209. WriteBSPFile( mapFile );
  1210. }
  1211. }
  1212. end = Plat_FloatTime();
  1213. char str[512];
  1214. GetHourMinuteSecondsString( (int)( end - start ), str, sizeof( str ) );
  1215. Msg( "%s elapsed\n", str );
  1216. DeleteCmdLine( argc, argv );
  1217. ReleasePakFileLumps();
  1218. DeleteMaterialReplacementKeys();
  1219. ShutdownMaterialSystem();
  1220. CmdLib_Cleanup();
  1221. return 0;
  1222. }
  1223. /*
  1224. =============
  1225. main
  1226. ============
  1227. */
  1228. int main (int argc, char **argv)
  1229. {
  1230. // Install an exception handler.
  1231. SetupDefaultToolsMinidumpHandler();
  1232. return RunVBSP( argc, argv );
  1233. }