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.

374 lines
8.9 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "vbsp.h"
  9. #include "collisionutils.h"
  10. /*
  11. ==============================================================================
  12. PORTAL FILE GENERATION
  13. Save out name.prt for qvis to read
  14. ==============================================================================
  15. */
  16. #define PORTALFILE "PRT1"
  17. struct cluster_portals_t
  18. {
  19. CUtlVector<portal_t *> portals;
  20. };
  21. int num_visclusters; // clusters the player can be in
  22. int num_visportals;
  23. int g_SkyCluster = -1;
  24. void WriteFloat (FILE *f, vec_t v)
  25. {
  26. if ( fabs(v - RoundInt(v)) < 0.001 )
  27. fprintf (f,"%i ",(int)RoundInt(v));
  28. else
  29. fprintf (f,"%f ",v);
  30. }
  31. /*
  32. =================
  33. WritePortalFile_r
  34. =================
  35. */
  36. void WritePortalFile(FILE *pFile, const CUtlVector<cluster_portals_t> &list)
  37. {
  38. portal_t *p;
  39. winding_t *w;
  40. Vector normal;
  41. vec_t dist;
  42. for ( int clusterIndex = 0; clusterIndex < list.Count(); clusterIndex++ )
  43. {
  44. for ( int j = 0; j < list[clusterIndex].portals.Count(); j++ )
  45. {
  46. p = list[clusterIndex].portals[j];
  47. w = p->winding;
  48. // write out to the file
  49. // sometimes planes get turned around when they are very near
  50. // the changeover point between different axis. interpret the
  51. // plane the same way vis will, and flip the side orders if needed
  52. // FIXME: is this still relevent?
  53. WindingPlane (w, normal, &dist);
  54. if ( DotProduct (p->plane.normal, normal) < 0.99 )
  55. { // backwards...
  56. fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
  57. }
  58. else
  59. {
  60. fprintf (pFile,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
  61. }
  62. for (int i=0 ; i<w->numpoints ; i++)
  63. {
  64. fprintf (pFile,"(");
  65. WriteFloat (pFile, w->p[i][0]);
  66. WriteFloat (pFile, w->p[i][1]);
  67. WriteFloat (pFile, w->p[i][2]);
  68. fprintf (pFile,") ");
  69. }
  70. fprintf (pFile,"\n");
  71. }
  72. }
  73. }
  74. struct viscluster_t
  75. {
  76. bspbrush_t *pBrushes;
  77. int clusterIndex;
  78. int leafCount;
  79. int leafStart;
  80. };
  81. static CUtlVector<viscluster_t> g_VisClusters;
  82. // add to the list of brushes the merge leaves into single vis clusters
  83. void AddVisCluster( entity_t *pFuncVisCluster )
  84. {
  85. viscluster_t tmp;
  86. Vector clipMins, clipMaxs;
  87. clipMins[0] = clipMins[1] = clipMins[2] = MIN_COORD_INTEGER;
  88. clipMaxs[0] = clipMaxs[1] = clipMaxs[2] = MAX_COORD_INTEGER;
  89. // build the map brushes out into the minimum non-overlapping set of brushes
  90. bspbrush_t *pBSPBrush = MakeBspBrushList( pFuncVisCluster->firstbrush, pFuncVisCluster->firstbrush + pFuncVisCluster->numbrushes,
  91. clipMins, clipMaxs, NO_DETAIL);
  92. tmp.pBrushes = ChopBrushes( pBSPBrush );
  93. // store the entry in the list
  94. tmp.clusterIndex = -1;
  95. tmp.leafCount = 0;
  96. tmp.leafStart = 0;
  97. #if DEBUG_VISUALIZE_CLUSTERS
  98. int debug = atoi(ValueForKey(pFuncVisCluster,"debug"));
  99. if ( debug )
  100. {
  101. Msg("Debug vis cluster %d\n", g_VisClusters.Count() );
  102. }
  103. #endif
  104. g_VisClusters.AddToTail( tmp );
  105. // clear out this entity so it won't get written to the bsp
  106. pFuncVisCluster->epairs = NULL;
  107. pFuncVisCluster->numbrushes = 0;
  108. }
  109. // returns the total overlapping volume of intersection between the node and the brush list
  110. float VolumeOfIntersection( bspbrush_t *pBrushList, node_t *pNode )
  111. {
  112. float volume = 0.0f;
  113. for ( bspbrush_t *pBrush = pBrushList; pBrush; pBrush = pBrush->next )
  114. {
  115. if ( IsBoxIntersectingBox( pNode->mins, pNode->maxs, pBrush->mins, pBrush->maxs ) )
  116. {
  117. bspbrush_t *pIntersect = IntersectBrush( pNode->volume, pBrush );
  118. if ( pIntersect )
  119. {
  120. volume += BrushVolume( pIntersect );
  121. FreeBrush( pIntersect );
  122. }
  123. }
  124. }
  125. return volume;
  126. }
  127. // Search for a forced cluster that this node is within
  128. // NOTE: Returns the first one found, these won't merge themselves together
  129. int GetVisCluster( node_t *pNode )
  130. {
  131. float maxVolume = BrushVolume(pNode->volume) * 0.10f; // needs to cover at least 10% of the volume to overlap
  132. int maxIndex = -1;
  133. // UNDONE: This could get slow
  134. for ( int i = g_VisClusters.Count(); --i >= 0; )
  135. {
  136. float volume = VolumeOfIntersection( g_VisClusters[i].pBrushes, pNode );
  137. if ( volume > maxVolume )
  138. {
  139. volume = maxVolume;
  140. maxIndex = i;
  141. }
  142. }
  143. return maxIndex;
  144. }
  145. /*
  146. ================
  147. NumberLeafs_r
  148. ================
  149. */
  150. void BuildVisLeafList_r (node_t *node, CUtlVector<node_t *> &leaves)
  151. {
  152. if (node->planenum != PLANENUM_LEAF)
  153. { // decision node
  154. node->cluster = -99;
  155. BuildVisLeafList_r (node->children[0], leaves);
  156. BuildVisLeafList_r (node->children[1], leaves);
  157. return;
  158. }
  159. if ( node->contents & CONTENTS_SOLID )
  160. { // solid block, viewpoint never inside
  161. node->cluster = -1;
  162. return;
  163. }
  164. leaves.AddToTail(node);
  165. }
  166. // Give each leaf in the list of empty leaves a vis cluster number
  167. // some are within func_viscluster volumes and will be merged together
  168. // every other leaf gets its own unique number
  169. void NumberLeafs( const CUtlVector<node_t *> &leaves )
  170. {
  171. for ( int i = 0; i < leaves.Count(); i++ )
  172. {
  173. node_t *node = leaves[i];
  174. int visCluster = GetVisCluster( node );
  175. if ( visCluster >= 0 )
  176. {
  177. if ( g_VisClusters[visCluster].clusterIndex < 0 )
  178. {
  179. g_VisClusters[visCluster].clusterIndex = num_visclusters;
  180. num_visclusters++;
  181. }
  182. g_VisClusters[visCluster].leafCount++;
  183. node->cluster = g_VisClusters[visCluster].clusterIndex;
  184. }
  185. else
  186. {
  187. if ( !g_bSkyVis && Is3DSkyboxArea( node->area ) )
  188. {
  189. if ( g_SkyCluster < 0 )
  190. {
  191. // allocate a cluster for the sky
  192. g_SkyCluster = num_visclusters;
  193. num_visclusters++;
  194. }
  195. node->cluster = g_SkyCluster;
  196. }
  197. else
  198. {
  199. node->cluster = num_visclusters;
  200. num_visclusters++;
  201. }
  202. }
  203. }
  204. #if DEBUG_VISUALIZE_CLUSTERS
  205. for ( int i = 0; i < g_VisClusters.Count(); i++ )
  206. {
  207. char name[256];
  208. sprintf(name, "u:\\main\\game\\ep2\\maps\\vis_%02d.gl", i );
  209. FileHandle_t fp = g_pFileSystem->Open( name, "w" );
  210. Msg("Writing %s\n", name );
  211. for ( bspbrush_t *pBrush = g_VisClusters[i].pBrushes; pBrush; pBrush = pBrush->next )
  212. {
  213. for (int i = 0; i < pBrush->numsides; i++ )
  214. OutputWindingColor( pBrush->sides[i].winding, fp, 0, 255, 0 );
  215. }
  216. for ( int j = 0; j < leaves.Count(); j++ )
  217. {
  218. if ( leaves[j]->cluster == g_VisClusters[i].clusterIndex )
  219. {
  220. bspbrush_t *pBrush = leaves[j]->volume;
  221. for (int k = 0; k < pBrush->numsides; k++ )
  222. OutputWindingColor( pBrush->sides[k].winding, fp, 64 + (j&31), 64, 64 - (j&31) );
  223. }
  224. }
  225. g_pFileSystem->Close(fp);
  226. }
  227. #endif
  228. }
  229. // build a list of all vis portals that connect clusters
  230. int BuildPortalList( CUtlVector<cluster_portals_t> &portalList, const CUtlVector<node_t *> &leaves )
  231. {
  232. int portalCount = 0;
  233. for ( int i = 0; i < leaves.Count(); i++ )
  234. {
  235. node_t *node = leaves[i];
  236. // count the portals
  237. for (portal_t *p = node->portals ; p ; )
  238. {
  239. if (p->nodes[0] == node) // only write out from first leaf
  240. {
  241. if ( p->nodes[0]->cluster != p->nodes[1]->cluster )
  242. {
  243. if (Portal_VisFlood (p))
  244. {
  245. portalCount++;
  246. portalList[node->cluster].portals.AddToTail(p);
  247. }
  248. }
  249. p = p->next[0];
  250. }
  251. else
  252. p = p->next[1];
  253. }
  254. }
  255. return portalCount;
  256. }
  257. /*
  258. ================
  259. CreateVisPortals_r
  260. ================
  261. */
  262. void CreateVisPortals_r (node_t *node)
  263. {
  264. // stop as soon as we get to a leaf
  265. if (node->planenum == PLANENUM_LEAF )
  266. return;
  267. MakeNodePortal (node);
  268. SplitNodePortals (node);
  269. CreateVisPortals_r (node->children[0]);
  270. CreateVisPortals_r (node->children[1]);
  271. }
  272. int clusterleaf;
  273. void SaveClusters_r (node_t *node)
  274. {
  275. if (node->planenum == PLANENUM_LEAF)
  276. {
  277. dleafs[clusterleaf++].cluster = node->cluster;
  278. return;
  279. }
  280. SaveClusters_r (node->children[0]);
  281. SaveClusters_r (node->children[1]);
  282. }
  283. /*
  284. ================
  285. WritePortalFile
  286. ================
  287. */
  288. void WritePortalFile (tree_t *tree)
  289. {
  290. char filename[1024];
  291. node_t *headnode;
  292. int start = Plat_FloatTime();
  293. qprintf ("--- WritePortalFile ---\n");
  294. sprintf (filename, "%s.prt", source);
  295. Msg ("writing %s...", filename);
  296. headnode = tree->headnode;
  297. FreeTreePortals_r (headnode);
  298. MakeHeadnodePortals (tree);
  299. CreateVisPortals_r (headnode);
  300. // set the cluster field in every leaf and count the total number of portals
  301. num_visclusters = 0;
  302. Msg("Building visibility clusters...\n");
  303. CUtlVector<node_t *> leaves;
  304. BuildVisLeafList_r( headnode, leaves );
  305. NumberLeafs (leaves);
  306. CUtlVector<cluster_portals_t> portalList;
  307. portalList.SetCount( num_visclusters );
  308. num_visportals = BuildPortalList( portalList, leaves );
  309. // write the file
  310. FILE *pf = fopen (filename, "w");
  311. if (!pf)
  312. Error ("Error opening %s", filename);
  313. fprintf (pf, "%s\n", PORTALFILE);
  314. fprintf (pf, "%i\n", num_visclusters);
  315. fprintf (pf, "%i\n", num_visportals);
  316. qprintf ("%5i visclusters\n", num_visclusters);
  317. qprintf ("%5i visportals\n", num_visportals);
  318. WritePortalFile(pf, portalList);
  319. fclose (pf);
  320. // we need to store the clusters out now because ordering
  321. // issues made us do this after writebsp...
  322. clusterleaf = 1;
  323. SaveClusters_r (headnode);
  324. Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
  325. }