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.

1553 lines
36 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "vbsp.h"
  9. #include "disp_vbsp.h"
  10. #include "utlvector.h"
  11. #include "faces.h"
  12. #include "builddisp.h"
  13. #include "tier1/strtools.h"
  14. #include "utilmatlib.h"
  15. #include "utldict.h"
  16. #include "map.h"
  17. int c_nofaces;
  18. int c_facenodes;
  19. // NOTE: This is a global used to link faces back to the tree node/portals they came from
  20. // it's used when filling water volumes
  21. node_t *dfacenodes[MAX_MAP_FACES];
  22. /*
  23. =========================================================
  24. ONLY SAVE OUT PLANES THAT ARE ACTUALLY USED AS NODES
  25. =========================================================
  26. */
  27. void EmitFaceVertexes (face_t **list, face_t *f);
  28. void AssignOccluderAreas();
  29. /*
  30. ============
  31. EmitPlanes
  32. There is no oportunity to discard planes, because all of the original
  33. brushes will be saved in the map.
  34. ============
  35. */
  36. void EmitPlanes (void)
  37. {
  38. int i;
  39. dplane_t *dp;
  40. plane_t *mp;
  41. int planetranslate[MAX_MAP_PLANES];
  42. mp = g_MainMap->mapplanes;
  43. for (i=0 ; i<g_MainMap->nummapplanes ; i++, mp++)
  44. {
  45. dp = &dplanes[numplanes];
  46. planetranslate[i] = numplanes;
  47. VectorCopy ( mp->normal, dp->normal);
  48. dp->dist = mp->dist;
  49. dp->type = mp->type;
  50. numplanes++;
  51. }
  52. }
  53. //========================================================
  54. void EmitMarkFace (dleaf_t *leaf_p, face_t *f)
  55. {
  56. int i;
  57. int facenum;
  58. while (f->merged)
  59. f = f->merged;
  60. if (f->split[0])
  61. {
  62. EmitMarkFace (leaf_p, f->split[0]);
  63. EmitMarkFace (leaf_p, f->split[1]);
  64. return;
  65. }
  66. facenum = f->outputnumber;
  67. if (facenum == -1)
  68. return; // degenerate face
  69. if (facenum < 0 || facenum >= numfaces)
  70. Error ("Bad leafface");
  71. for (i=leaf_p->firstleafface ; i<numleaffaces ; i++)
  72. if (dleaffaces[i] == facenum)
  73. break; // merged out face
  74. if (i == numleaffaces)
  75. {
  76. if (numleaffaces >= MAX_MAP_LEAFFACES)
  77. Error ("Too many detail brush faces, max = %d\n", MAX_MAP_LEAFFACES);
  78. dleaffaces[numleaffaces] = facenum;
  79. numleaffaces++;
  80. }
  81. }
  82. /*
  83. ==================
  84. EmitLeaf
  85. ==================
  86. */
  87. void EmitLeaf (node_t *node)
  88. {
  89. dleaf_t *leaf_p;
  90. portal_t *p;
  91. int s;
  92. face_t *f;
  93. bspbrush_t *b;
  94. int i;
  95. int brushnum;
  96. leafface_t *pList;
  97. // emit a leaf
  98. if (numleafs >= MAX_MAP_LEAFS)
  99. Error ("Too many BSP leaves, max = %d", MAX_MAP_LEAFS);
  100. node->diskId = numleafs;
  101. leaf_p = &dleafs[numleafs];
  102. numleafs++;
  103. if( nummodels == 0 )
  104. {
  105. leaf_p->cluster = node->cluster;
  106. }
  107. else
  108. {
  109. // Submodels don't have clusters. If this isn't set to -1 here, then there
  110. // will be multiple leaves (albeit from different models) that reference
  111. // the same cluster and parts of the code like ivp.cpp's ConvertWaterModelToPhysCollide
  112. // won't work.
  113. leaf_p->cluster = -1;
  114. }
  115. leaf_p->contents = node->contents;
  116. leaf_p->area = node->area;
  117. // By default, assume the leaf can see the skybox.
  118. // VRAD will do the actual computation to see if it really can see the skybox
  119. leaf_p->flags = LEAF_FLAGS_SKY;
  120. //
  121. // write bounding box info
  122. //
  123. VECTOR_COPY (node->mins, leaf_p->mins);
  124. VECTOR_COPY (node->maxs, leaf_p->maxs);
  125. //
  126. // write the leafbrushes
  127. //
  128. leaf_p->firstleafbrush = numleafbrushes;
  129. for (b=node->brushlist ; b ; b=b->next)
  130. {
  131. if (numleafbrushes >= MAX_MAP_LEAFBRUSHES)
  132. Error ("Too many brushes in one leaf, max = %d", MAX_MAP_LEAFBRUSHES);
  133. brushnum = b->original - g_MainMap->mapbrushes;
  134. for (i=leaf_p->firstleafbrush ; i<numleafbrushes ; i++)
  135. {
  136. if (dleafbrushes[i] == brushnum)
  137. break;
  138. }
  139. if (i == numleafbrushes)
  140. {
  141. dleafbrushes[numleafbrushes] = brushnum;
  142. numleafbrushes++;
  143. }
  144. }
  145. leaf_p->numleafbrushes = numleafbrushes - leaf_p->firstleafbrush;
  146. //
  147. // write the leaffaces
  148. //
  149. if (leaf_p->contents & CONTENTS_SOLID)
  150. return; // no leaffaces in solids
  151. leaf_p->firstleafface = numleaffaces;
  152. for (p = node->portals ; p ; p = p->next[s])
  153. {
  154. s = (p->nodes[1] == node);
  155. f = p->face[s];
  156. if (!f)
  157. continue; // not a visible portal
  158. EmitMarkFace (leaf_p, f);
  159. }
  160. // emit the detail faces
  161. for ( pList = node->leaffacelist; pList; pList = pList->pNext )
  162. {
  163. EmitMarkFace( leaf_p, pList->pFace );
  164. }
  165. leaf_p->numleaffaces = numleaffaces - leaf_p->firstleafface;
  166. }
  167. // per face plane - original face "side" list
  168. side_t *pOrigFaceSideList[MAX_MAP_PLANES];
  169. //-----------------------------------------------------------------------------
  170. //-----------------------------------------------------------------------------
  171. int CreateOrigFace( face_t *f )
  172. {
  173. int i, j;
  174. dface_t *of;
  175. side_t *side;
  176. int vIndices[128];
  177. int eIndex[2];
  178. winding_t *pWinding;
  179. // not a real face!
  180. if( !f->w )
  181. return -1;
  182. // get the original face -- the "side"
  183. side = f->originalface;
  184. // get the original face winding
  185. if( !side->winding )
  186. {
  187. return -1;
  188. }
  189. //
  190. // get the next original face
  191. //
  192. if( numorigfaces >= MAX_MAP_FACES )
  193. Error( "Too many faces in map, max = %d", MAX_MAP_FACES );
  194. of = &dorigfaces[numorigfaces];
  195. numorigfaces++;
  196. // set original face to -1 -- it is an origianl face!
  197. of->origFace = -1;
  198. //
  199. // add side to plane list
  200. //
  201. side->next = pOrigFaceSideList[f->planenum];
  202. pOrigFaceSideList[f->planenum] = side;
  203. side->origIndex = numorigfaces - 1;
  204. pWinding = CopyWinding( side->winding );
  205. //
  206. // plane info
  207. //
  208. of->planenum = side->planenum;
  209. if ( side->contents & CONTENTS_DETAIL )
  210. of->onNode = 0;
  211. else
  212. of->onNode = 1;
  213. of->side = side->planenum & 1;
  214. //
  215. // edge info
  216. //
  217. of->firstedge = numsurfedges;
  218. of->numedges = side->winding->numpoints;
  219. //
  220. // material info
  221. //
  222. of->texinfo = side->texinfo;
  223. of->dispinfo = f->dispinfo;
  224. //
  225. // save the vertices
  226. //
  227. for( i = 0; i < pWinding->numpoints; i++ )
  228. {
  229. //
  230. // compare vertices
  231. //
  232. vIndices[i] = GetVertexnum( pWinding->p[i] );
  233. }
  234. //
  235. // save off points -- as edges
  236. //
  237. for( i = 0; i < pWinding->numpoints; i++ )
  238. {
  239. //
  240. // look for matching edges first
  241. //
  242. eIndex[0] = vIndices[i];
  243. eIndex[1] = vIndices[(i+1)%pWinding->numpoints];
  244. for( j = firstmodeledge; j < numedges; j++ )
  245. {
  246. if( ( eIndex[0] == dedges[j].v[1] ) &&
  247. ( eIndex[1] == dedges[j].v[0] ) &&
  248. ( edgefaces[j][0]->contents == f->contents ) )
  249. {
  250. // check for multiple backward edges!! -- shouldn't have
  251. if( edgefaces[j][1] )
  252. continue;
  253. // set back edge
  254. edgefaces[j][1] = f;
  255. //
  256. // get next surface edge
  257. //
  258. if( numsurfedges >= MAX_MAP_SURFEDGES )
  259. Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
  260. dsurfedges[numsurfedges] = -j;
  261. numsurfedges++;
  262. break;
  263. }
  264. }
  265. if( j == numedges )
  266. {
  267. //
  268. // get next edge
  269. //
  270. AddEdge( eIndex[0], eIndex[1], f );
  271. //
  272. // get next surface edge
  273. //
  274. if( numsurfedges >= MAX_MAP_SURFEDGES )
  275. Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
  276. dsurfedges[numsurfedges] = ( numedges - 1 );
  277. numsurfedges++;
  278. }
  279. }
  280. // return the index
  281. return ( numorigfaces - 1 );
  282. }
  283. //-----------------------------------------------------------------------------
  284. // Purpose: search for a face within the origface list and return the index if
  285. // found
  286. // Input: f - the face to compare
  287. // Output: the index of the face it found, -1 if not found
  288. //-----------------------------------------------------------------------------
  289. int FindOrigFace( face_t *f )
  290. {
  291. int i;
  292. static int bClear = 0;
  293. side_t *pSide;
  294. //
  295. // initially clear the face side lists (per face plane)
  296. //
  297. if( !bClear )
  298. {
  299. for( i = 0; i < MAX_MAP_PLANES; i++ )
  300. {
  301. pOrigFaceSideList[i] = NULL;
  302. }
  303. bClear = 1;
  304. }
  305. //
  306. // compare the sides
  307. //
  308. for( pSide = pOrigFaceSideList[f->planenum]; pSide; pSide = pSide->next )
  309. {
  310. if( pSide == f->originalface )
  311. return pSide->origIndex;
  312. }
  313. // original face not found in list
  314. return -1;
  315. }
  316. //-----------------------------------------------------------------------------
  317. // Purpose: to find an the original face within the list of original faces, if
  318. // a match is not found then create a new origFace -- either way pass
  319. // back the index of the origface in the list
  320. // Input: f - face containing the original face information
  321. // Output: the index of the origface in the origface list
  322. //-----------------------------------------------------------------------------
  323. int FindOrCreateOrigFace( face_t *f )
  324. {
  325. int index;
  326. // check for an original face
  327. if( !f->originalface )
  328. return -1;
  329. //
  330. // find or create a orig face and return the index
  331. //
  332. index = FindOrigFace( f );
  333. if( index == -1 )
  334. return CreateOrigFace( f );
  335. else if( index == -2 )
  336. return -1;
  337. return index;
  338. }
  339. /*
  340. ==================
  341. EmitFace
  342. ==================
  343. */
  344. void EmitFace( face_t *f, qboolean onNode )
  345. {
  346. dface_t *df;
  347. int i;
  348. int e;
  349. // void SubdivideFaceBySubdivSize( face_t *f ); // garymcthack
  350. // SubdivideFaceBySubdivSize( f );
  351. // set initial output number
  352. f->outputnumber = -1;
  353. // degenerated
  354. if( f->numpoints < 3 )
  355. return;
  356. // not a final face
  357. if( f->merged || f->split[0] || f->split[1] )
  358. return;
  359. // don't emit NODRAW faces for runtime
  360. if ( texinfo[f->texinfo].flags & SURF_NODRAW )
  361. {
  362. // keep NODRAW terrain surfaces though
  363. if ( f->dispinfo == -1 )
  364. return;
  365. Warning("NODRAW on terrain surface!\n");
  366. }
  367. // save output number so leaffaces can use
  368. f->outputnumber = numfaces;
  369. //
  370. // get the next available .bsp face slot
  371. //
  372. if (numfaces >= MAX_MAP_FACES)
  373. Error( "Too many faces in map, max = %d", MAX_MAP_FACES );
  374. df = &dfaces[numfaces];
  375. // Save the correlation between dfaces and faces -- since dfaces doesnt have worldcraft face id
  376. dfaceids.AddToTail();
  377. dfaceids[numfaces].hammerfaceid = f->originalface->id;
  378. numfaces++;
  379. //
  380. // plane info - planenum is used by qlight, but not quake
  381. //
  382. df->planenum = f->planenum;
  383. df->onNode = onNode;
  384. df->side = f->planenum & 1;
  385. //
  386. // material info
  387. //
  388. df->texinfo = f->texinfo;
  389. df->dispinfo = f->dispinfo;
  390. df->smoothingGroups = f->smoothingGroups;
  391. // save the original "side"/face data
  392. df->origFace = FindOrCreateOrigFace( f );
  393. df->surfaceFogVolumeID = -1;
  394. dfacenodes[numfaces-1] = f->fogVolumeLeaf;
  395. if ( f->fogVolumeLeaf )
  396. {
  397. Assert( f->fogVolumeLeaf->planenum == PLANENUM_LEAF );
  398. }
  399. //
  400. // edge info
  401. //
  402. df->firstedge = numsurfedges;
  403. df->numedges = f->numpoints;
  404. // UNDONE: Nodraw faces have no winding - revisit to see if this is necessary
  405. if ( f->w )
  406. {
  407. df->area = WindingArea( f->w );
  408. }
  409. else
  410. {
  411. df->area = 0;
  412. }
  413. df->firstPrimID = f->firstPrimID;
  414. df->SetNumPrims( f->numPrims );
  415. df->SetDynamicShadowsEnabled( f->originalface->m_bDynamicShadowsEnabled );
  416. //
  417. // save off points -- as edges
  418. //
  419. for( i = 0; i < f->numpoints; i++ )
  420. {
  421. //e = GetEdge (f->pts[i], f->pts[(i+1)%f->numpoints], f);
  422. e = GetEdge2 (f->vertexnums[i], f->vertexnums[(i+1)%f->numpoints], f);
  423. if (numsurfedges >= MAX_MAP_SURFEDGES)
  424. Error( "Too much brush geometry in bsp, numsurfedges == MAX_MAP_SURFEDGES" );
  425. dsurfedges[numsurfedges] = e;
  426. numsurfedges++;
  427. }
  428. // Create overlay face lists.
  429. side_t *pSide = f->originalface;
  430. if ( pSide )
  431. {
  432. int nOverlayCount = pSide->aOverlayIds.Count();
  433. if ( nOverlayCount > 0 )
  434. {
  435. Overlay_AddFaceToLists( ( numfaces - 1 ), pSide );
  436. }
  437. nOverlayCount = pSide->aWaterOverlayIds.Count();
  438. if ( nOverlayCount > 0 )
  439. {
  440. OverlayTransition_AddFaceToLists( ( numfaces - 1 ), pSide );
  441. }
  442. }
  443. }
  444. //-----------------------------------------------------------------------------
  445. // Purpose: Emit all of the faces stored at the leaves (faces from detail brushes)
  446. //-----------------------------------------------------------------------------
  447. void EmitLeafFaces( face_t *pLeafFaceList )
  448. {
  449. face_t *f = pLeafFaceList;
  450. while ( f )
  451. {
  452. EmitFace( f, false );
  453. f = f->next;
  454. }
  455. }
  456. //-----------------------------------------------------------------------------
  457. // Purpose: Free the list of faces stored at the leaves
  458. //-----------------------------------------------------------------------------
  459. void FreeLeafFaces( face_t *pLeafFaceList )
  460. {
  461. int count = 0;
  462. face_t *f, *next;
  463. f = pLeafFaceList;
  464. while ( f )
  465. {
  466. next = f->next;
  467. FreeFace( f );
  468. f = next;
  469. count++;
  470. }
  471. }
  472. /*
  473. ============
  474. EmitDrawingNode_r
  475. ============
  476. */
  477. int EmitDrawNode_r (node_t *node)
  478. {
  479. dnode_t *n;
  480. face_t *f;
  481. int i;
  482. if (node->planenum == PLANENUM_LEAF)
  483. {
  484. EmitLeaf (node);
  485. return -numleafs;
  486. }
  487. // emit a node
  488. if (numnodes == MAX_MAP_NODES)
  489. Error ("MAX_MAP_NODES");
  490. node->diskId = numnodes;
  491. n = &dnodes[numnodes];
  492. numnodes++;
  493. VECTOR_COPY (node->mins, n->mins);
  494. VECTOR_COPY (node->maxs, n->maxs);
  495. if (node->planenum & 1)
  496. Error ("WriteDrawNodes_r: odd planenum");
  497. n->planenum = node->planenum;
  498. n->firstface = numfaces;
  499. n->area = node->area;
  500. if (!node->faces)
  501. c_nofaces++;
  502. else
  503. c_facenodes++;
  504. for (f=node->faces ; f ; f=f->next)
  505. EmitFace (f, true);
  506. n->numfaces = numfaces - n->firstface;
  507. //
  508. // recursively output the other nodes
  509. //
  510. for (i=0 ; i<2 ; i++)
  511. {
  512. if (node->children[i]->planenum == PLANENUM_LEAF)
  513. {
  514. n->children[i] = -(numleafs + 1);
  515. EmitLeaf (node->children[i]);
  516. }
  517. else
  518. {
  519. n->children[i] = numnodes;
  520. EmitDrawNode_r (node->children[i]);
  521. }
  522. }
  523. return n - dnodes;
  524. }
  525. //=========================================================
  526. // This will generate a scratchpad file with the level's geometry in it and the noshadow faces drawn red.
  527. // #define SCRATCHPAD_NO_SHADOW_FACES
  528. #if defined( SCRATCHPAD_NO_SHADOW_FACES )
  529. #include "scratchpad_helpers.h"
  530. IScratchPad3D *g_pPad;
  531. #endif
  532. void MarkNoShadowFaces()
  533. {
  534. #if defined( SCRATCHPAD_NO_SHADOW_FACES )
  535. g_pPad = ScratchPad3D_Create();
  536. ScratchPad_DrawWorld( g_pPad, false, CSPColor(1,1,1,0.3) );
  537. for ( int iFace=0; iFace < numfaces; iFace++ )
  538. {
  539. dface_t *pFace = &dfaces[iFace];
  540. if ( !pFace->AreDynamicShadowsEnabled() )
  541. {
  542. ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(1,0,0) );
  543. ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(-1,0,0) );
  544. ScratchPad_DrawFace( g_pPad, pFace, iFace, CSPColor(1,0,0,1), Vector(0,1,0) );
  545. }
  546. }
  547. g_pPad->Release();
  548. #endif
  549. }
  550. struct texinfomap_t
  551. {
  552. int refCount;
  553. int outputIndex;
  554. };
  555. struct texdatamap_t
  556. {
  557. int refCount;
  558. int outputIndex;
  559. };
  560. // Find the best used texinfo to remap this brush side
  561. int FindMatchingBrushSideTexinfo( int sideIndex, const texinfomap_t *pMap )
  562. {
  563. dbrushside_t &side = dbrushsides[sideIndex];
  564. // find one with the same flags & surfaceprops (even if the texture name is different)
  565. int sideTexFlags = texinfo[side.texinfo].flags;
  566. int sideTexData = texinfo[side.texinfo].texdata;
  567. int sideSurfaceProp = g_SurfaceProperties[sideTexData];
  568. for ( int j = 0; j < texinfo.Count(); j++ )
  569. {
  570. if ( pMap[j].refCount > 0 &&
  571. texinfo[j].flags == sideTexFlags &&
  572. g_SurfaceProperties[texinfo[j].texdata] == sideSurfaceProp )
  573. {
  574. // found one
  575. return j;
  576. }
  577. }
  578. // can't find a better match
  579. return side.texinfo;
  580. }
  581. // Remove all unused texinfos and rebuild array
  582. void ComapctTexinfoArray( texinfomap_t *pMap )
  583. {
  584. CUtlVector<texinfo_t> old;
  585. old.CopyArray( texinfo.Base(), texinfo.Count() );
  586. texinfo.RemoveAll();
  587. int firstSky = -1;
  588. int first2DSky = -1;
  589. for ( int i = 0; i < old.Count(); i++ )
  590. {
  591. if ( !pMap[i].refCount )
  592. {
  593. pMap[i].outputIndex = -1;
  594. continue;
  595. }
  596. // only add one sky texinfo + one 2D sky texinfo
  597. if ( old[i].flags & SURF_SKY2D )
  598. {
  599. if ( first2DSky < 0 )
  600. {
  601. first2DSky = texinfo.AddToTail( old[i] );
  602. }
  603. pMap[i].outputIndex = first2DSky;
  604. continue;
  605. }
  606. if ( old[i].flags & SURF_SKY )
  607. {
  608. if ( firstSky < 0 )
  609. {
  610. firstSky = texinfo.AddToTail( old[i] );
  611. }
  612. pMap[i].outputIndex = firstSky;
  613. continue;
  614. }
  615. pMap[i].outputIndex = texinfo.AddToTail( old[i] );
  616. }
  617. }
  618. void CompactTexdataArray( texdatamap_t *pMap )
  619. {
  620. CUtlVector<char> oldStringData;
  621. oldStringData.CopyArray( g_TexDataStringData.Base(), g_TexDataStringData.Count() );
  622. g_TexDataStringData.RemoveAll();
  623. CUtlVector<int> oldStringTable;
  624. oldStringTable.CopyArray( g_TexDataStringTable.Base(), g_TexDataStringTable.Count() );
  625. g_TexDataStringTable.RemoveAll();
  626. CUtlVector<dtexdata_t> oldTexData;
  627. oldTexData.CopyArray( dtexdata, numtexdata );
  628. // clear current table and rebuild
  629. numtexdata = 0;
  630. for ( int i = 0; i < oldTexData.Count(); i++ )
  631. {
  632. // unreferenced, note in map and skip
  633. if ( !pMap[i].refCount )
  634. {
  635. pMap[i].outputIndex = -1;
  636. continue;
  637. }
  638. pMap[i].outputIndex = numtexdata;
  639. // get old string and re-add to table
  640. const char *pString = &oldStringData[oldStringTable[oldTexData[i].nameStringTableID]];
  641. int nameIndex = TexDataStringTable_AddOrFindString( pString );
  642. // copy old texdata and fixup with new name in compacted table
  643. dtexdata[numtexdata] = oldTexData[i];
  644. dtexdata[numtexdata].nameStringTableID = nameIndex;
  645. numtexdata++;
  646. }
  647. }
  648. void CompactTexinfos()
  649. {
  650. Msg("Compacting texture/material tables...\n");
  651. texinfomap_t *texinfoMap = new texinfomap_t[texinfo.Count()];
  652. texdatamap_t *texdataMap = new texdatamap_t[numtexdata];
  653. memset( texinfoMap, 0, sizeof(texinfoMap[0])*texinfo.Count() );
  654. memset( texdataMap, 0, sizeof(texdataMap[0])*numtexdata );
  655. int i;
  656. // get texinfos referenced by faces
  657. for ( i = 0; i < numfaces; i++ )
  658. {
  659. texinfoMap[dfaces[i].texinfo].refCount++;
  660. }
  661. // get texinfos referenced by brush sides
  662. for ( i = 0; i < numbrushsides; i++ )
  663. {
  664. // not referenced by any visible geometry
  665. Assert( dbrushsides[i].texinfo >= 0 );
  666. if ( !texinfoMap[dbrushsides[i].texinfo].refCount )
  667. {
  668. dbrushsides[i].texinfo = FindMatchingBrushSideTexinfo( i, texinfoMap );
  669. // didn't find anything suitable, go ahead and reference it
  670. if ( !texinfoMap[dbrushsides[i].texinfo].refCount )
  671. {
  672. texinfoMap[dbrushsides[i].texinfo].refCount++;
  673. }
  674. }
  675. }
  676. // get texinfos referenced by overlays
  677. for ( i = 0; i < g_nOverlayCount; i++ )
  678. {
  679. texinfoMap[g_Overlays[i].nTexInfo].refCount++;
  680. }
  681. for ( i = 0; i < numleafwaterdata; i++ )
  682. {
  683. if ( dleafwaterdata[i].surfaceTexInfoID >= 0 )
  684. {
  685. texinfoMap[dleafwaterdata[i].surfaceTexInfoID].refCount++;
  686. }
  687. }
  688. for ( i = 0; i < *pNumworldlights; i++ )
  689. {
  690. if ( dworldlights[i].texinfo >= 0 )
  691. {
  692. texinfoMap[dworldlights[i].texinfo].refCount++;
  693. }
  694. }
  695. for ( i = 0; i < g_nWaterOverlayCount; i++ )
  696. {
  697. if ( g_WaterOverlays[i].nTexInfo >= 0 )
  698. {
  699. texinfoMap[g_WaterOverlays[i].nTexInfo].refCount++;
  700. }
  701. }
  702. // reference all used texdatas
  703. for ( i = 0; i < texinfo.Count(); i++ )
  704. {
  705. if ( texinfoMap[i].refCount > 0 )
  706. {
  707. texdataMap[texinfo[i].texdata].refCount++;
  708. }
  709. }
  710. int oldCount = texinfo.Count();
  711. int oldTexdataCount = numtexdata;
  712. int oldTexdataString = g_TexDataStringData.Count();
  713. ComapctTexinfoArray( texinfoMap );
  714. CompactTexdataArray( texdataMap );
  715. for ( i = 0; i < texinfo.Count(); i++ )
  716. {
  717. int mapIndex = texdataMap[texinfo[i].texdata].outputIndex;
  718. Assert( mapIndex >= 0 );
  719. texinfo[i].texdata = mapIndex;
  720. //const char *pName = TexDataStringTable_GetString( dtexdata[texinfo[i].texdata].nameStringTableID );
  721. }
  722. // remap texinfos on faces
  723. for ( i = 0; i < numfaces; i++ )
  724. {
  725. Assert( texinfoMap[dfaces[i].texinfo].outputIndex >= 0 );
  726. dfaces[i].texinfo = texinfoMap[dfaces[i].texinfo].outputIndex;
  727. }
  728. // remap texinfos on brushsides
  729. for ( i = 0; i < numbrushsides; i++ )
  730. {
  731. Assert( texinfoMap[dbrushsides[i].texinfo].outputIndex >= 0 );
  732. dbrushsides[i].texinfo = texinfoMap[dbrushsides[i].texinfo].outputIndex;
  733. }
  734. // remap texinfos on overlays
  735. for ( i = 0; i < g_nOverlayCount; i++ )
  736. {
  737. g_Overlays[i].nTexInfo = texinfoMap[g_Overlays[i].nTexInfo].outputIndex;
  738. }
  739. // remap leaf water data
  740. for ( i = 0; i < numleafwaterdata; i++ )
  741. {
  742. if ( dleafwaterdata[i].surfaceTexInfoID >= 0 )
  743. {
  744. dleafwaterdata[i].surfaceTexInfoID = texinfoMap[dleafwaterdata[i].surfaceTexInfoID].outputIndex;
  745. }
  746. }
  747. // remap world lights
  748. for ( i = 0; i < *pNumworldlights; i++ )
  749. {
  750. if ( dworldlights[i].texinfo >= 0 )
  751. {
  752. dworldlights[i].texinfo = texinfoMap[dworldlights[i].texinfo].outputIndex;
  753. }
  754. }
  755. // remap water overlays
  756. for ( i = 0; i < g_nWaterOverlayCount; i++ )
  757. {
  758. if ( g_WaterOverlays[i].nTexInfo >= 0 )
  759. {
  760. g_WaterOverlays[i].nTexInfo = texinfoMap[g_WaterOverlays[i].nTexInfo].outputIndex;
  761. }
  762. }
  763. Msg("Reduced %d texinfos to %d\n", oldCount, texinfo.Count() );
  764. Msg("Reduced %d texdatas to %d (%d bytes to %d)\n", oldTexdataCount, numtexdata, oldTexdataString, g_TexDataStringData.Count() );
  765. delete[] texinfoMap;
  766. delete[] texdataMap;
  767. }
  768. /*
  769. ============
  770. WriteBSP
  771. ============
  772. */
  773. void WriteBSP (node_t *headnode, face_t *pLeafFaceList )
  774. {
  775. int i;
  776. int oldfaces;
  777. int oldorigfaces;
  778. c_nofaces = 0;
  779. c_facenodes = 0;
  780. qprintf ("--- WriteBSP ---\n");
  781. oldfaces = numfaces;
  782. oldorigfaces = numorigfaces;
  783. GetEdge2_InitOptimizedList();
  784. EmitLeafFaces( pLeafFaceList );
  785. dmodels[nummodels].headnode = EmitDrawNode_r (headnode);
  786. // Only emit area portals for the main world.
  787. if( nummodels == 0 )
  788. {
  789. EmitAreaPortals (headnode);
  790. }
  791. //
  792. // add all displacement faces for the particular model
  793. //
  794. for( i = 0; i < nummapdispinfo; i++ )
  795. {
  796. int entityIndex = GetDispInfoEntityNum( &mapdispinfo[i] );
  797. if( entityIndex == entity_num )
  798. {
  799. EmitFaceVertexes( NULL, &mapdispinfo[i].face );
  800. EmitFace( &mapdispinfo[i].face, FALSE );
  801. }
  802. }
  803. EmitWaterVolumesForBSP( &dmodels[nummodels], headnode );
  804. qprintf ("%5i nodes with faces\n", c_facenodes);
  805. qprintf ("%5i nodes without faces\n", c_nofaces);
  806. qprintf ("%5i faces\n", numfaces-oldfaces);
  807. qprintf( "%5i original faces\n", numorigfaces-oldorigfaces );
  808. }
  809. //===========================================================
  810. /*
  811. ============
  812. SetModelNumbers
  813. ============
  814. */
  815. void SetModelNumbers (void)
  816. {
  817. int i;
  818. int models;
  819. char value[10];
  820. models = 1;
  821. for (i=1 ; i<num_entities ; i++)
  822. {
  823. if (!entities[i].numbrushes)
  824. continue;
  825. if ( !IsFuncOccluder(i) )
  826. {
  827. sprintf (value, "*%i", models);
  828. models++;
  829. }
  830. else
  831. {
  832. sprintf (value, "");
  833. }
  834. SetKeyValue (&entities[i], "model", value);
  835. }
  836. }
  837. /*
  838. ============
  839. SetLightStyles
  840. ============
  841. */
  842. #define MAX_SWITCHED_LIGHTS 32
  843. void SetLightStyles (void)
  844. {
  845. int stylenum;
  846. char *t;
  847. entity_t *e;
  848. int i, j;
  849. char value[10];
  850. char lighttargets[MAX_SWITCHED_LIGHTS][64];
  851. // any light that is controlled (has a targetname)
  852. // must have a unique style number generated for it
  853. stylenum = 0;
  854. for (i=1 ; i<num_entities ; i++)
  855. {
  856. e = &entities[i];
  857. t = ValueForKey (e, "classname");
  858. if (Q_strncasecmp (t, "light", 5))
  859. continue;
  860. // This is not true for dynamic lights
  861. if (!Q_strcasecmp (t, "light_dynamic"))
  862. continue;
  863. t = ValueForKey (e, "targetname");
  864. if (!t[0])
  865. continue;
  866. // find this targetname
  867. for (j=0 ; j<stylenum ; j++)
  868. if (!strcmp (lighttargets[j], t))
  869. break;
  870. if (j == stylenum)
  871. {
  872. if (stylenum == MAX_SWITCHED_LIGHTS)
  873. Error ("Too many switched lights (error at light %s), max = %d", t, MAX_SWITCHED_LIGHTS);
  874. strcpy (lighttargets[j], t);
  875. stylenum++;
  876. }
  877. sprintf (value, "%i", 32 + j);
  878. char *pCurrentStyle = ValueForKey( e, "style" );
  879. // the designer has set a default lightstyle as well as making the light switchable
  880. if ( pCurrentStyle )
  881. {
  882. int oldStyle = atoi(pCurrentStyle);
  883. if ( oldStyle != 0 )
  884. {
  885. // save off the default style so the game code can make a switchable copy of it
  886. SetKeyValue( e, "defaultstyle", pCurrentStyle );
  887. }
  888. }
  889. SetKeyValue (e, "style", value);
  890. }
  891. }
  892. /*
  893. ============
  894. EmitBrushes
  895. ============
  896. */
  897. void EmitBrushes (void)
  898. {
  899. int i, j, bnum, s, x;
  900. dbrush_t *db;
  901. mapbrush_t *b;
  902. dbrushside_t *cp;
  903. Vector normal;
  904. vec_t dist;
  905. int planenum;
  906. numbrushsides = 0;
  907. numbrushes = g_MainMap->nummapbrushes;
  908. for (bnum=0 ; bnum<g_MainMap->nummapbrushes ; bnum++)
  909. {
  910. b = &g_MainMap->mapbrushes[bnum];
  911. db = &dbrushes[bnum];
  912. db->contents = b->contents;
  913. db->firstside = numbrushsides;
  914. db->numsides = b->numsides;
  915. for (j=0 ; j<b->numsides ; j++)
  916. {
  917. if (numbrushsides == MAX_MAP_BRUSHSIDES)
  918. Error ("MAX_MAP_BRUSHSIDES");
  919. cp = &dbrushsides[numbrushsides];
  920. numbrushsides++;
  921. cp->planenum = b->original_sides[j].planenum;
  922. cp->texinfo = b->original_sides[j].texinfo;
  923. if ( cp->texinfo == -1 )
  924. {
  925. cp->texinfo = g_MainMap->g_ClipTexinfo;
  926. }
  927. cp->bevel = b->original_sides[j].bevel;
  928. }
  929. // add any axis planes not contained in the brush to bevel off corners
  930. for (x=0 ; x<3 ; x++)
  931. for (s=-1 ; s<=1 ; s+=2)
  932. {
  933. // add the plane
  934. VectorCopy (vec3_origin, normal);
  935. normal[x] = s;
  936. if (s == -1)
  937. dist = -b->mins[x];
  938. else
  939. dist = b->maxs[x];
  940. planenum = g_MainMap->FindFloatPlane (normal, dist);
  941. for (i=0 ; i<b->numsides ; i++)
  942. if (b->original_sides[i].planenum == planenum)
  943. break;
  944. if (i == b->numsides)
  945. {
  946. if (numbrushsides >= MAX_MAP_BRUSHSIDES)
  947. Error ("MAX_MAP_BRUSHSIDES");
  948. dbrushsides[numbrushsides].planenum = planenum;
  949. dbrushsides[numbrushsides].texinfo =
  950. dbrushsides[numbrushsides-1].texinfo;
  951. numbrushsides++;
  952. db->numsides++;
  953. }
  954. }
  955. }
  956. }
  957. /*
  958. ==================
  959. BeginBSPFile
  960. ==================
  961. */
  962. void BeginBSPFile (void)
  963. {
  964. // these values may actually be initialized
  965. // if the file existed when loaded, so clear them explicitly
  966. nummodels = 0;
  967. numfaces = 0;
  968. numnodes = 0;
  969. numbrushsides = 0;
  970. numvertexes = 0;
  971. numleaffaces = 0;
  972. numleafbrushes = 0;
  973. numsurfedges = 0;
  974. // edge 0 is not used, because 0 can't be negated
  975. numedges = 1;
  976. // leave vertex 0 as an error
  977. numvertexes = 1;
  978. // leave leaf 0 as an error
  979. numleafs = 1;
  980. dleafs[0].contents = CONTENTS_SOLID;
  981. // BUGBUG: This doesn't work!
  982. #if 0
  983. // make a default empty leaf for the tracing code
  984. memset( &dleafs[1], 0, sizeof(dleafs[1]) );
  985. dleafs[1].contents = CONTENTS_EMPTY;
  986. #endif
  987. }
  988. // We can't calculate this properly until vvis (since we need vis to do this), so we set
  989. // to zero everywhere by default.
  990. static void ClearDistToClosestWater( void )
  991. {
  992. int i;
  993. for( i = 0; i < numleafs; i++ )
  994. {
  995. g_LeafMinDistToWater[i] = 0;
  996. }
  997. }
  998. void DiscoverMacroTextures()
  999. {
  1000. CUtlDict<int,int> tempDict;
  1001. g_FaceMacroTextureInfos.SetSize( numfaces );
  1002. for ( int iFace=0; iFace < numfaces; iFace++ )
  1003. {
  1004. texinfo_t *pTexInfo = &texinfo[dfaces[iFace].texinfo];
  1005. if ( pTexInfo->texdata < 0 )
  1006. continue;
  1007. dtexdata_t *pTexData = &dtexdata[pTexInfo->texdata];
  1008. const char *pMaterialName = &g_TexDataStringData[ g_TexDataStringTable[pTexData->nameStringTableID] ];
  1009. MaterialSystemMaterial_t hMaterial = FindMaterial( pMaterialName, NULL, false );
  1010. const char *pMacroTextureName = GetMaterialVar( hMaterial, "$macro_texture" );
  1011. if ( pMacroTextureName )
  1012. {
  1013. if ( tempDict.Find( pMacroTextureName ) == tempDict.InvalidIndex() )
  1014. {
  1015. Msg( "-- DiscoverMacroTextures: %s\n", pMacroTextureName );
  1016. tempDict.Insert( pMacroTextureName, 0 );
  1017. }
  1018. int stringID = TexDataStringTable_AddOrFindString( pMacroTextureName );
  1019. g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = (unsigned short)stringID;
  1020. }
  1021. else
  1022. {
  1023. g_FaceMacroTextureInfos[iFace].m_MacroTextureNameID = 0xFFFF;
  1024. }
  1025. }
  1026. }
  1027. // Make sure that we have a water lod control entity if we have water in the map.
  1028. void EnsurePresenceOfWaterLODControlEntity( void )
  1029. {
  1030. extern bool g_bHasWater;
  1031. if( !g_bHasWater )
  1032. {
  1033. // Don't bother if there isn't any water in the map.
  1034. return;
  1035. }
  1036. for( int i=0; i < num_entities; i++ )
  1037. {
  1038. entity_t *e = &entities[i];
  1039. const char *pClassName = ValueForKey( e, "classname" );
  1040. if( !Q_stricmp( pClassName, "water_lod_control" ) )
  1041. {
  1042. // Found one!!!!
  1043. return;
  1044. }
  1045. }
  1046. // None found, add one.
  1047. Warning( "Water found with no water_lod_control entity, creating a default one.\n" );
  1048. entity_t *mapent = &entities[num_entities];
  1049. num_entities++;
  1050. memset(mapent, 0, sizeof(*mapent));
  1051. mapent->firstbrush = g_MainMap->nummapbrushes;
  1052. mapent->numbrushes = 0;
  1053. SetKeyValue( mapent, "classname", "water_lod_control" );
  1054. SetKeyValue( mapent, "cheapwaterstartdistance", "1000" );
  1055. SetKeyValue( mapent, "cheapwaterenddistance", "2000" );
  1056. }
  1057. /*
  1058. ============
  1059. EndBSPFile
  1060. ============
  1061. */
  1062. void EndBSPFile (void)
  1063. {
  1064. // Mark noshadow faces.
  1065. MarkNoShadowFaces();
  1066. EmitBrushes ();
  1067. EmitPlanes ();
  1068. // stick flat normals at the verts
  1069. SaveVertexNormals();
  1070. // Figure out lightmap extents for all faces.
  1071. UpdateAllFaceLightmapExtents();
  1072. // Generate geometry and lightmap alpha for displacements.
  1073. EmitDispLMAlphaAndNeighbors();
  1074. // Emit overlay data.
  1075. Overlay_EmitOverlayFaces();
  1076. OverlayTransition_EmitOverlayFaces();
  1077. // phys collision needs dispinfo to operate (needs to generate phys collision for displacement surfs)
  1078. EmitPhysCollision();
  1079. // We can't calculate this properly until vvis (since we need vis to do this), so we set
  1080. // to zero everywhere by default.
  1081. ClearDistToClosestWater();
  1082. // Emit static props found in the .vmf file
  1083. EmitStaticProps();
  1084. // Place detail props found in .vmf and based on material properties
  1085. EmitDetailObjects();
  1086. // Compute bounds after creating disp info because we need to reference it
  1087. ComputeBoundsNoSkybox();
  1088. // Make sure that we have a water lod control eneity if we have water in the map.
  1089. EnsurePresenceOfWaterLODControlEntity();
  1090. // Doing this here because stuff about may filter out entities
  1091. UnparseEntities ();
  1092. // remove unused texinfos
  1093. CompactTexinfos();
  1094. // Figure out which faces want macro textures.
  1095. DiscoverMacroTextures();
  1096. char fileName[1024];
  1097. V_strncpy( fileName, source, sizeof( fileName ) );
  1098. V_DefaultExtension( fileName, ".bsp", sizeof( fileName ) );
  1099. Msg ("Writing %s\n", fileName);
  1100. WriteBSPFile (fileName);
  1101. }
  1102. /*
  1103. ==================
  1104. BeginModel
  1105. ==================
  1106. */
  1107. int firstmodleaf;
  1108. void BeginModel (void)
  1109. {
  1110. dmodel_t *mod;
  1111. int start, end;
  1112. mapbrush_t *b;
  1113. int j;
  1114. entity_t *e;
  1115. Vector mins, maxs;
  1116. if (nummodels == MAX_MAP_MODELS)
  1117. Error ("Too many brush models in map, max = %d", MAX_MAP_MODELS);
  1118. mod = &dmodels[nummodels];
  1119. mod->firstface = numfaces;
  1120. firstmodleaf = numleafs;
  1121. firstmodeledge = numedges;
  1122. firstmodelface = numfaces;
  1123. //
  1124. // bound the brushes
  1125. //
  1126. e = &entities[entity_num];
  1127. start = e->firstbrush;
  1128. end = start + e->numbrushes;
  1129. ClearBounds (mins, maxs);
  1130. for (j=start ; j<end ; j++)
  1131. {
  1132. b = &g_MainMap->mapbrushes[j];
  1133. if (!b->numsides)
  1134. continue; // not a real brush (origin brush)
  1135. AddPointToBounds (b->mins, mins, maxs);
  1136. AddPointToBounds (b->maxs, mins, maxs);
  1137. }
  1138. VectorCopy (mins, mod->mins);
  1139. VectorCopy (maxs, mod->maxs);
  1140. }
  1141. /*
  1142. ==================
  1143. EndModel
  1144. ==================
  1145. */
  1146. void EndModel (void)
  1147. {
  1148. dmodel_t *mod;
  1149. mod = &dmodels[nummodels];
  1150. mod->numfaces = numfaces - mod->firstface;
  1151. nummodels++;
  1152. }
  1153. //-----------------------------------------------------------------------------
  1154. // figure out which leaf a point is in
  1155. //-----------------------------------------------------------------------------
  1156. static int PointLeafnum_r (const Vector& p, int num)
  1157. {
  1158. float d;
  1159. while (num >= 0)
  1160. {
  1161. dnode_t* node = dnodes + num;
  1162. dplane_t* plane = dplanes + node->planenum;
  1163. if (plane->type < 3)
  1164. d = p[plane->type] - plane->dist;
  1165. else
  1166. d = DotProduct (plane->normal, p) - plane->dist;
  1167. if (d < 0)
  1168. num = node->children[1];
  1169. else
  1170. num = node->children[0];
  1171. }
  1172. return -1 - num;
  1173. }
  1174. int PointLeafnum ( dmodel_t* pModel, const Vector& p )
  1175. {
  1176. return PointLeafnum_r (p, pModel->headnode);
  1177. }
  1178. //-----------------------------------------------------------------------------
  1179. // Adds a noew to the bounding box
  1180. //-----------------------------------------------------------------------------
  1181. static void AddNodeToBounds(int node, CUtlVector<int>& skipAreas, Vector& mins, Vector& maxs)
  1182. {
  1183. // not a leaf
  1184. if (node >= 0)
  1185. {
  1186. AddNodeToBounds( dnodes[node].children[0], skipAreas, mins, maxs );
  1187. AddNodeToBounds( dnodes[node].children[1], skipAreas, mins, maxs );
  1188. }
  1189. else
  1190. {
  1191. int leaf = - 1 - node;
  1192. // Don't bother with solid leaves
  1193. if (dleafs[leaf].contents & CONTENTS_SOLID)
  1194. return;
  1195. // Skip 3D skybox
  1196. int i;
  1197. for ( i = skipAreas.Count(); --i >= 0; )
  1198. {
  1199. if (dleafs[leaf].area == skipAreas[i])
  1200. return;
  1201. }
  1202. unsigned int firstface = dleafs[leaf].firstleafface;
  1203. for ( i = 0; i < dleafs[leaf].numleaffaces; ++i )
  1204. {
  1205. unsigned int face = dleaffaces[ firstface + i ];
  1206. // Skip skyboxes + nodraw
  1207. texinfo_t& tex = texinfo[dfaces[face].texinfo];
  1208. if (tex.flags & (SURF_SKY | SURF_NODRAW))
  1209. continue;
  1210. unsigned int firstedge = dfaces[face].firstedge;
  1211. Assert( firstedge >= 0 );
  1212. for (int j = 0; j < dfaces[face].numedges; ++j)
  1213. {
  1214. Assert( firstedge+j < numsurfedges );
  1215. int edge = abs(dsurfedges[firstedge+j]);
  1216. dedge_t* pEdge = &dedges[edge];
  1217. Assert( pEdge->v[0] >= 0 );
  1218. Assert( pEdge->v[1] >= 0 );
  1219. AddPointToBounds (dvertexes[pEdge->v[0]].point, mins, maxs);
  1220. AddPointToBounds (dvertexes[pEdge->v[1]].point, mins, maxs);
  1221. }
  1222. }
  1223. }
  1224. }
  1225. //-----------------------------------------------------------------------------
  1226. // Check to see if a displacement lives in any leaves that are not
  1227. // in the 3d skybox
  1228. //-----------------------------------------------------------------------------
  1229. bool IsBoxInsideWorld( int node, CUtlVector<int> &skipAreas, const Vector &vecMins, const Vector &vecMaxs )
  1230. {
  1231. while( 1 )
  1232. {
  1233. // leaf
  1234. if (node < 0)
  1235. {
  1236. // get the leaf
  1237. int leaf = - 1 - node;
  1238. // Don't bother with solid leaves
  1239. if (dleafs[leaf].contents & CONTENTS_SOLID)
  1240. return false;
  1241. // Skip 3D skybox
  1242. int i;
  1243. for ( i = skipAreas.Count(); --i >= 0; )
  1244. {
  1245. if ( dleafs[leaf].area == skipAreas[i] )
  1246. return false;
  1247. }
  1248. return true;
  1249. }
  1250. //
  1251. // get displacement bounding box position relative to the node plane
  1252. //
  1253. dnode_t *pNode = &dnodes[ node ];
  1254. dplane_t *pPlane = &dplanes[ pNode->planenum ];
  1255. int sideResult = BrushBspBoxOnPlaneSide( vecMins, vecMaxs, pPlane );
  1256. // front side
  1257. if( sideResult == 1 )
  1258. {
  1259. node = pNode->children[0];
  1260. }
  1261. // back side
  1262. else if( sideResult == 2 )
  1263. {
  1264. node = pNode->children[1];
  1265. }
  1266. //split
  1267. else
  1268. {
  1269. if ( IsBoxInsideWorld( pNode->children[0], skipAreas, vecMins, vecMaxs ) )
  1270. return true;
  1271. node = pNode->children[1];
  1272. }
  1273. }
  1274. }
  1275. //-----------------------------------------------------------------------------
  1276. // Adds the displacement surfaces in the world to the bounds
  1277. //-----------------------------------------------------------------------------
  1278. void AddDispsToBounds( int nHeadNode, CUtlVector<int>& skipAreas, Vector &vecMins, Vector &vecMaxs )
  1279. {
  1280. Vector vecDispMins, vecDispMaxs;
  1281. // first determine how many displacement surfaces there will be per leaf
  1282. int i;
  1283. for ( i = 0; i < g_dispinfo.Count(); ++i )
  1284. {
  1285. ComputeDispInfoBounds( i, vecDispMins, vecDispMaxs );
  1286. if ( IsBoxInsideWorld( nHeadNode, skipAreas, vecDispMins, vecDispMaxs ) )
  1287. {
  1288. AddPointToBounds( vecDispMins, vecMins, vecMaxs );
  1289. AddPointToBounds( vecDispMaxs, vecMins, vecMaxs );
  1290. }
  1291. }
  1292. }
  1293. //-----------------------------------------------------------------------------
  1294. // Compute the bounding box, excluding 3D skybox + skybox, add it to keyvalues
  1295. //-----------------------------------------------------------------------------
  1296. void ComputeBoundsNoSkybox( )
  1297. {
  1298. // Iterate over all world leaves, skip those which are part of skybox
  1299. Vector mins, maxs;
  1300. ClearBounds (mins, maxs);
  1301. AddNodeToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs );
  1302. AddDispsToBounds( dmodels[0].headnode, g_SkyAreas, mins, maxs );
  1303. // Add the bounds to the worldspawn data
  1304. for (int i = 0; i < num_entities; ++i)
  1305. {
  1306. char* pEntity = ValueForKey(&entities[i], "classname");
  1307. if (!strcmp(pEntity, "worldspawn"))
  1308. {
  1309. char string[32];
  1310. sprintf (string, "%i %i %i", (int)mins[0], (int)mins[1], (int)mins[2]);
  1311. SetKeyValue (&entities[i], "world_mins", string);
  1312. sprintf (string, "%i %i %i", (int)maxs[0], (int)maxs[1], (int)maxs[2]);
  1313. SetKeyValue (&entities[i], "world_maxs", string);
  1314. break;
  1315. }
  1316. }
  1317. }