Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1724 lines
37 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "vbsp.h"
  9. #include "utlvector.h"
  10. #include "mathlib/vmatrix.h"
  11. #include "iscratchpad3d.h"
  12. #include "csg.h"
  13. #include "utlmap.h"
  14. #include "fmtstr.h"
  15. int c_active_portals;
  16. int c_peak_portals;
  17. int c_boundary;
  18. int c_boundary_sides;
  19. /*
  20. ===========
  21. AllocPortal
  22. ===========
  23. */
  24. portal_t *AllocPortal (void)
  25. {
  26. static int s_PortalCount = 0;
  27. portal_t *p;
  28. if (numthreads == 1)
  29. c_active_portals++;
  30. if (c_active_portals > c_peak_portals)
  31. c_peak_portals = c_active_portals;
  32. p = (portal_t*)malloc (sizeof(portal_t));
  33. memset (p, 0, sizeof(portal_t));
  34. p->id = s_PortalCount;
  35. ++s_PortalCount;
  36. return p;
  37. }
  38. void FreePortal (portal_t *p)
  39. {
  40. if (p->winding)
  41. FreeWinding (p->winding);
  42. if (numthreads == 1)
  43. c_active_portals--;
  44. free (p);
  45. }
  46. //==============================================================
  47. /*
  48. ==============
  49. VisibleContents
  50. Returns the single content bit of the
  51. strongest visible content present
  52. ==============
  53. */
  54. int VisibleContents (int contents)
  55. {
  56. int i;
  57. for (i=1 ; i<=LAST_VISIBLE_CONTENTS ; i<<=1)
  58. {
  59. if (contents & i )
  60. {
  61. return i;
  62. }
  63. }
  64. return 0;
  65. }
  66. /*
  67. ===============
  68. ClusterContents
  69. ===============
  70. */
  71. int ClusterContents (node_t *node)
  72. {
  73. int c1, c2, c;
  74. if (node->planenum == PLANENUM_LEAF)
  75. return node->contents;
  76. c1 = ClusterContents(node->children[0]);
  77. c2 = ClusterContents(node->children[1]);
  78. c = c1|c2;
  79. // a cluster may include some solid detail areas, but
  80. // still be seen into
  81. if ( ! (c1&CONTENTS_SOLID) || ! (c2&CONTENTS_SOLID) )
  82. c &= ~CONTENTS_SOLID;
  83. return c;
  84. }
  85. /*
  86. =============
  87. Portal_VisFlood
  88. Returns true if the portal is empty or translucent, allowing
  89. the PVS calculation to see through it.
  90. The nodes on either side of the portal may actually be clusters,
  91. not leafs, so all contents should be ored together
  92. =============
  93. */
  94. qboolean Portal_VisFlood (portal_t *p)
  95. {
  96. int c1, c2;
  97. if (!p->onnode)
  98. return false; // to global outsideleaf
  99. c1 = ClusterContents(p->nodes[0]);
  100. c2 = ClusterContents(p->nodes[1]);
  101. if (!VisibleContents (c1^c2))
  102. return true;
  103. if (c1 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
  104. c1 = 0;
  105. if (c2 & (CONTENTS_TRANSLUCENT|CONTENTS_DETAIL))
  106. c2 = 0;
  107. if ( (c1|c2) & CONTENTS_SOLID )
  108. return false; // can't see through solid
  109. if (! (c1 ^ c2))
  110. return true; // identical on both sides
  111. if (!VisibleContents (c1^c2))
  112. return true;
  113. return false;
  114. }
  115. /*
  116. ===============
  117. Portal_EntityFlood
  118. The entity flood determines which areas are
  119. "outside" on the map, which are then filled in.
  120. Flowing from side s to side !s
  121. ===============
  122. */
  123. qboolean Portal_EntityFlood (portal_t *p, int s)
  124. {
  125. if (p->nodes[0]->planenum != PLANENUM_LEAF
  126. || p->nodes[1]->planenum != PLANENUM_LEAF)
  127. Error ("Portal_EntityFlood: not a leaf");
  128. // can never cross to a solid
  129. if ( (p->nodes[0]->contents & CONTENTS_SOLID)
  130. || (p->nodes[1]->contents & CONTENTS_SOLID) )
  131. return false;
  132. // can flood through everything else
  133. return true;
  134. }
  135. qboolean Portal_AreaLeakFlood (portal_t *p, int s)
  136. {
  137. if ( !Portal_EntityFlood( p, s ) )
  138. return false;
  139. // can never cross through areaportal
  140. if ( (p->nodes[0]->contents & CONTENTS_AREAPORTAL)
  141. || (p->nodes[1]->contents & CONTENTS_AREAPORTAL) )
  142. return false;
  143. // can flood through everything else
  144. return true;
  145. }
  146. //=============================================================================
  147. int c_tinyportals;
  148. /*
  149. =============
  150. AddPortalToNodes
  151. =============
  152. */
  153. void AddPortalToNodes (portal_t *p, node_t *front, node_t *back)
  154. {
  155. if (p->nodes[0] || p->nodes[1])
  156. Error ("AddPortalToNode: allready included");
  157. p->nodes[0] = front;
  158. p->next[0] = front->portals;
  159. front->portals = p;
  160. p->nodes[1] = back;
  161. p->next[1] = back->portals;
  162. back->portals = p;
  163. }
  164. /*
  165. =============
  166. RemovePortalFromNode
  167. =============
  168. */
  169. void RemovePortalFromNode (portal_t *portal, node_t *l)
  170. {
  171. portal_t **pp, *t;
  172. // remove reference to the current portal
  173. pp = &l->portals;
  174. while (1)
  175. {
  176. t = *pp;
  177. if (!t)
  178. Error ("RemovePortalFromNode: portal not in leaf");
  179. if ( t == portal )
  180. break;
  181. if (t->nodes[0] == l)
  182. pp = &t->next[0];
  183. else if (t->nodes[1] == l)
  184. pp = &t->next[1];
  185. else
  186. Error ("RemovePortalFromNode: portal not bounding leaf");
  187. }
  188. if (portal->nodes[0] == l)
  189. {
  190. *pp = portal->next[0];
  191. portal->nodes[0] = NULL;
  192. }
  193. else if (portal->nodes[1] == l)
  194. {
  195. *pp = portal->next[1];
  196. portal->nodes[1] = NULL;
  197. }
  198. }
  199. //============================================================================
  200. void PrintPortal (portal_t *p)
  201. {
  202. int i;
  203. winding_t *w;
  204. w = p->winding;
  205. for (i=0 ; i<w->numpoints ; i++)
  206. Msg ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0]
  207. , w->p[i][1], w->p[i][2]);
  208. }
  209. // because of water areaportals support, the areaportal may not be the only brush on this node
  210. bspbrush_t *AreaportalBrushForNode( node_t *node )
  211. {
  212. bspbrush_t *b = node->brushlist;
  213. while ( b && !(b->original->contents & CONTENTS_AREAPORTAL) )
  214. {
  215. b = b->next;
  216. }
  217. Assert( b->original->entitynum != 0 );
  218. return b;
  219. }
  220. /*
  221. ================
  222. MakeHeadnodePortals
  223. The created portals will face the global outside_node
  224. ================
  225. */
  226. // buffer space around sides of nodes
  227. #define SIDESPACE 8
  228. void MakeHeadnodePortals (tree_t *tree)
  229. {
  230. Vector bounds[2];
  231. int i, j, n;
  232. portal_t *p, *portals[6];
  233. plane_t bplanes[6], *pl;
  234. node_t *node;
  235. node = tree->headnode;
  236. // pad with some space so there will never be null volume leafs
  237. for (i=0 ; i<3 ; i++)
  238. {
  239. bounds[0][i] = tree->mins[i] - SIDESPACE;
  240. bounds[1][i] = tree->maxs[i] + SIDESPACE;
  241. }
  242. tree->outside_node.planenum = PLANENUM_LEAF;
  243. tree->outside_node.brushlist = NULL;
  244. tree->outside_node.portals = NULL;
  245. tree->outside_node.contents = 0;
  246. for (i=0 ; i<3 ; i++)
  247. for (j=0 ; j<2 ; j++)
  248. {
  249. n = j*3 + i;
  250. p = AllocPortal ();
  251. portals[n] = p;
  252. pl = &bplanes[n];
  253. memset (pl, 0, sizeof(*pl));
  254. if (j)
  255. {
  256. pl->normal[i] = -1;
  257. pl->dist = -bounds[j][i];
  258. }
  259. else
  260. {
  261. pl->normal[i] = 1;
  262. pl->dist = bounds[j][i];
  263. }
  264. p->plane = *pl;
  265. p->winding = BaseWindingForPlane (pl->normal, pl->dist);
  266. AddPortalToNodes (p, node, &tree->outside_node);
  267. }
  268. // clip the basewindings by all the other planes
  269. for (i=0 ; i<6 ; i++)
  270. {
  271. for (j=0 ; j<6 ; j++)
  272. {
  273. if (j == i)
  274. continue;
  275. ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON);
  276. }
  277. }
  278. }
  279. //===================================================
  280. /*
  281. ================
  282. BaseWindingForNode
  283. ================
  284. */
  285. #define BASE_WINDING_EPSILON 0.001
  286. #define SPLIT_WINDING_EPSILON 0.001
  287. winding_t *BaseWindingForNode (node_t *node)
  288. {
  289. winding_t *w;
  290. node_t *n;
  291. plane_t *plane;
  292. Vector normal;
  293. vec_t dist;
  294. w = BaseWindingForPlane (g_MainMap->mapplanes[node->planenum].normal, g_MainMap->mapplanes[node->planenum].dist);
  295. // clip by all the parents
  296. for (n=node->parent ; n && w ; )
  297. {
  298. plane = &g_MainMap->mapplanes[n->planenum];
  299. if (n->children[0] == node)
  300. { // take front
  301. ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON);
  302. }
  303. else
  304. { // take back
  305. VectorSubtract (vec3_origin, plane->normal, normal);
  306. dist = -plane->dist;
  307. ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON);
  308. }
  309. node = n;
  310. n = n->parent;
  311. }
  312. return w;
  313. }
  314. //============================================================
  315. /*
  316. ==================
  317. MakeNodePortal
  318. create the new portal by taking the full plane winding for the cutting plane
  319. and clipping it by all of parents of this node
  320. ==================
  321. */
  322. void MakeNodePortal (node_t *node)
  323. {
  324. portal_t *new_portal, *p;
  325. winding_t *w;
  326. Vector normal;
  327. float dist = 0.0f;
  328. int side = 0;
  329. w = BaseWindingForNode (node);
  330. // clip the portal by all the other portals in the node
  331. for (p = node->portals ; p && w; p = p->next[side])
  332. {
  333. if (p->nodes[0] == node)
  334. {
  335. side = 0;
  336. VectorCopy (p->plane.normal, normal);
  337. dist = p->plane.dist;
  338. }
  339. else if (p->nodes[1] == node)
  340. {
  341. side = 1;
  342. VectorSubtract (vec3_origin, p->plane.normal, normal);
  343. dist = -p->plane.dist;
  344. }
  345. else
  346. {
  347. Error ("CutNodePortals_r: mislinked portal");
  348. }
  349. ChopWindingInPlace (&w, normal, dist, 0.1);
  350. }
  351. if (!w)
  352. {
  353. return;
  354. }
  355. if (WindingIsTiny (w))
  356. {
  357. c_tinyportals++;
  358. FreeWinding (w);
  359. return;
  360. }
  361. new_portal = AllocPortal ();
  362. new_portal->plane = g_MainMap->mapplanes[node->planenum];
  363. new_portal->onnode = node;
  364. new_portal->winding = w;
  365. AddPortalToNodes (new_portal, node->children[0], node->children[1]);
  366. }
  367. /*
  368. ==============
  369. SplitNodePortals
  370. Move or split the portals that bound node so that the node's
  371. children have portals instead of node.
  372. ==============
  373. */
  374. void SplitNodePortals (node_t *node)
  375. {
  376. portal_t *p, *next_portal, *new_portal;
  377. node_t *f, *b, *other_node;
  378. int side = 0;
  379. plane_t *plane;
  380. winding_t *frontwinding, *backwinding;
  381. plane = &g_MainMap->mapplanes[node->planenum];
  382. f = node->children[0];
  383. b = node->children[1];
  384. for (p = node->portals ; p ; p = next_portal)
  385. {
  386. if (p->nodes[0] == node)
  387. side = 0;
  388. else if (p->nodes[1] == node)
  389. side = 1;
  390. else
  391. Error ("CutNodePortals_r: mislinked portal");
  392. next_portal = p->next[side];
  393. other_node = p->nodes[!side];
  394. RemovePortalFromNode (p, p->nodes[0]);
  395. RemovePortalFromNode (p, p->nodes[1]);
  396. //
  397. // cut the portal into two portals, one on each side of the cut plane
  398. //
  399. ClipWindingEpsilon (p->winding, plane->normal, plane->dist,
  400. SPLIT_WINDING_EPSILON, &frontwinding, &backwinding);
  401. if (frontwinding && WindingIsTiny(frontwinding))
  402. {
  403. FreeWinding (frontwinding);
  404. frontwinding = NULL;
  405. c_tinyportals++;
  406. }
  407. if (backwinding && WindingIsTiny(backwinding))
  408. {
  409. FreeWinding (backwinding);
  410. backwinding = NULL;
  411. c_tinyportals++;
  412. }
  413. if (!frontwinding && !backwinding)
  414. { // tiny windings on both sides
  415. continue;
  416. }
  417. if (!frontwinding)
  418. {
  419. FreeWinding (backwinding);
  420. if (side == 0)
  421. AddPortalToNodes (p, b, other_node);
  422. else
  423. AddPortalToNodes (p, other_node, b);
  424. continue;
  425. }
  426. if (!backwinding)
  427. {
  428. FreeWinding (frontwinding);
  429. if (side == 0)
  430. AddPortalToNodes (p, f, other_node);
  431. else
  432. AddPortalToNodes (p, other_node, f);
  433. continue;
  434. }
  435. // the winding is split
  436. new_portal = AllocPortal ();
  437. *new_portal = *p;
  438. new_portal->winding = backwinding;
  439. FreeWinding (p->winding);
  440. p->winding = frontwinding;
  441. if (side == 0)
  442. {
  443. AddPortalToNodes (p, f, other_node);
  444. AddPortalToNodes (new_portal, b, other_node);
  445. }
  446. else
  447. {
  448. AddPortalToNodes (p, other_node, f);
  449. AddPortalToNodes (new_portal, other_node, b);
  450. }
  451. }
  452. node->portals = NULL;
  453. }
  454. /*
  455. ================
  456. CalcNodeBounds
  457. ================
  458. */
  459. void CalcNodeBounds (node_t *node)
  460. {
  461. portal_t *p;
  462. int s;
  463. int i;
  464. // calc mins/maxs for both leafs and nodes
  465. ClearBounds (node->mins, node->maxs);
  466. for (p = node->portals ; p ; p = p->next[s])
  467. {
  468. s = (p->nodes[1] == node);
  469. for (i=0 ; i<p->winding->numpoints ; i++)
  470. AddPointToBounds (p->winding->p[i], node->mins, node->maxs);
  471. }
  472. }
  473. /*
  474. ==================
  475. MakeTreePortals_r
  476. ==================
  477. */
  478. void MakeTreePortals_r (node_t *node)
  479. {
  480. int i;
  481. CalcNodeBounds (node);
  482. if (node->mins[0] >= node->maxs[0])
  483. {
  484. Warning("WARNING: node without a volume\n");
  485. }
  486. for (i=0 ; i<3 ; i++)
  487. {
  488. if (node->mins[i] < (MIN_COORD_INTEGER-SIDESPACE) || node->maxs[i] > (MAX_COORD_INTEGER+SIDESPACE))
  489. {
  490. const char *pMatName = "<NO BRUSH>";
  491. // split by brush side
  492. if ( node->side )
  493. {
  494. texinfo_t *pTexInfo = &texinfo[node->side->texinfo];
  495. dtexdata_t *pTexData = GetTexData( pTexInfo->texdata );
  496. pMatName = TexDataStringTable_GetString( pTexData->nameStringTableID );
  497. }
  498. Vector point = node->portals->winding->p[0];
  499. Warning("WARNING: BSP node with unbounded volume (material: %s, near %s)\n", pMatName, VecToString(point) );
  500. break;
  501. }
  502. }
  503. if (node->planenum == PLANENUM_LEAF)
  504. return;
  505. MakeNodePortal (node);
  506. SplitNodePortals (node);
  507. MakeTreePortals_r (node->children[0]);
  508. MakeTreePortals_r (node->children[1]);
  509. }
  510. /*
  511. ==================
  512. MakeTreePortals
  513. ==================
  514. */
  515. void MakeTreePortals (tree_t *tree)
  516. {
  517. MakeHeadnodePortals (tree);
  518. MakeTreePortals_r (tree->headnode);
  519. }
  520. /*
  521. =========================================================
  522. FLOOD ENTITIES
  523. =========================================================
  524. */
  525. //-----------------------------------------------------------------------------
  526. // Purpose: Floods outward from the given node, marking visited nodes with
  527. // the number of hops from a node with an entity. If we ever mark
  528. // the outside_node for this tree, we've leaked.
  529. // Input : node -
  530. // dist -
  531. //-----------------------------------------------------------------------------
  532. #define VBSP_COMPILE_FLOOD_PORTALS_NORECURSE 1
  533. #if VBSP_COMPILE_FLOOD_PORTALS_NORECURSE
  534. struct FloodPortalsParams_t
  535. {
  536. node_t *node;
  537. int dist;
  538. };
  539. void FloodPortals_r( node_t *node, int dist, CUtlVector< FloodPortalsParams_t > &arrPendingFlood )
  540. #else
  541. void FloodPortals_r( node_t *node, int dist )
  542. #endif
  543. {
  544. portal_t *p;
  545. int s;
  546. node->occupied = dist;
  547. for (p=node->portals ; p ; p = p->next[s])
  548. {
  549. s = (p->nodes[1] == node);
  550. // Skip nodes that have already been marked.
  551. if (p->nodes[!s]->occupied)
  552. continue;
  553. // Skip portals that lead to or from nodes with solid contents.
  554. if (!Portal_EntityFlood (p, s))
  555. continue;
  556. #if VBSP_COMPILE_FLOOD_PORTALS_NORECURSE
  557. bool bAlreadyPending = false;
  558. FOR_EACH_VEC( arrPendingFlood, idxPendingFlood )
  559. {
  560. if ( arrPendingFlood[idxPendingFlood].node == p->nodes[!s] )
  561. {
  562. bAlreadyPending = true;
  563. break;
  564. }
  565. }
  566. if ( bAlreadyPending )
  567. continue;
  568. FloodPortalsParams_t params = {};
  569. params.node = p->nodes[!s];
  570. params.dist = dist+1;
  571. arrPendingFlood.AddToTail( params );
  572. #else
  573. FloodPortals_r (p->nodes[!s], dist+1);
  574. #endif
  575. }
  576. }
  577. void FloodAreaLeak_r( node_t *node, int dist )
  578. {
  579. portal_t *p;
  580. int s;
  581. node->occupied = dist;
  582. for (p=node->portals ; p ; p = p->next[s])
  583. {
  584. s = (p->nodes[1] == node);
  585. if (p->nodes[!s]->occupied)
  586. continue;
  587. if (!Portal_AreaLeakFlood (p, s))
  588. continue;
  589. FloodAreaLeak_r( p->nodes[!s], dist+1 );
  590. }
  591. }
  592. void ClearOccupied_r( node_t *headnode )
  593. {
  594. if ( !headnode )
  595. return;
  596. headnode->occupied = 0;
  597. ClearOccupied_r( headnode->children[0] );
  598. ClearOccupied_r( headnode->children[1] );
  599. }
  600. void FloodAreaLeak( node_t *headnode, node_t *pFirstSide )
  601. {
  602. ClearOccupied_r( headnode );
  603. FloodAreaLeak_r( pFirstSide, 2 );
  604. }
  605. //-----------------------------------------------------------------------------
  606. // Purpose: For the given entity at the given origin, finds the leaf node in the
  607. // BSP tree that the entity occupies.
  608. //
  609. // We then flood outward from that leaf to see if the entity leaks.
  610. // Input : headnode -
  611. // origin -
  612. // occupant -
  613. // Output : Returns false if the entity is in solid, true if it is not.
  614. //-----------------------------------------------------------------------------
  615. qboolean PlaceOccupant (node_t *headnode, Vector& origin, entity_t *occupant)
  616. {
  617. node_t *node;
  618. vec_t d;
  619. plane_t *plane;
  620. // find the leaf to start in
  621. node = headnode;
  622. while (node->planenum != PLANENUM_LEAF)
  623. {
  624. plane = &g_MainMap->mapplanes[node->planenum];
  625. d = DotProduct (origin, plane->normal) - plane->dist;
  626. if (d >= 0)
  627. node = node->children[0];
  628. else
  629. node = node->children[1];
  630. }
  631. if (node->contents == CONTENTS_SOLID)
  632. return false;
  633. node->occupant = occupant;
  634. #if VBSP_COMPILE_FLOOD_PORTALS_NORECURSE
  635. CUtlVector< FloodPortalsParams_t > arrPendingFlood;
  636. FloodPortals_r( node, 1, arrPendingFlood );
  637. while ( arrPendingFlood.Count() )
  638. {
  639. FloodPortals_r( arrPendingFlood.Head().node, arrPendingFlood.Head().dist, arrPendingFlood );
  640. arrPendingFlood.RemoveMultipleFromHead( 1 );
  641. }
  642. #else
  643. // Flood outward from here to see if this entity leaks.
  644. FloodPortals_r (node, 1);
  645. #endif
  646. return true;
  647. }
  648. /*
  649. =============
  650. FloodEntities
  651. Marks all nodes that can be reached by entites
  652. =============
  653. */
  654. qboolean FloodEntities (tree_t *tree)
  655. {
  656. int i;
  657. Vector origin;
  658. char *cl;
  659. qboolean inside;
  660. node_t *headnode;
  661. headnode = tree->headnode;
  662. qprintf ("--- FloodEntities ---\n");
  663. inside = false;
  664. tree->outside_node.occupied = 0;
  665. for (i=1 ; i<num_entities ; i++)
  666. {
  667. GetVectorForKey (&entities[i], "origin", origin);
  668. if (VectorCompare(origin, vec3_origin))
  669. continue;
  670. cl = ValueForKey (&entities[i], "classname");
  671. origin[2] += 1; // so objects on floor are ok
  672. // nudge playerstart around if needed so clipping hulls allways
  673. // have a valid point
  674. if (!strcmp (cl, "info_player_start"))
  675. {
  676. int x, y;
  677. for (x=-16 ; x<=16 ; x += 16)
  678. {
  679. for (y=-16 ; y<=16 ; y += 16)
  680. {
  681. origin[0] += x;
  682. origin[1] += y;
  683. if (PlaceOccupant (headnode, origin, &entities[i]))
  684. {
  685. inside = true;
  686. goto gotit;
  687. }
  688. origin[0] -= x;
  689. origin[1] -= y;
  690. }
  691. }
  692. gotit: ;
  693. }
  694. else
  695. {
  696. if (PlaceOccupant (headnode, origin, &entities[i]))
  697. inside = true;
  698. }
  699. }
  700. if (!inside)
  701. {
  702. qprintf ("no entities in open -- no filling\n");
  703. }
  704. if (tree->outside_node.occupied)
  705. {
  706. qprintf ("entity reached from outside -- no filling\n" );
  707. }
  708. return (qboolean)(inside && !tree->outside_node.occupied);
  709. }
  710. /*
  711. =========================================================
  712. FLOOD AREAS
  713. =========================================================
  714. */
  715. int c_areas;
  716. bool IsAreaportalNode( node_t *node )
  717. {
  718. return ( node->contents & CONTENTS_AREAPORTAL ) ? true : false;
  719. }
  720. /*
  721. =============
  722. FloodAreas_r
  723. =============
  724. */
  725. void FloodAreas_r (node_t *node, portal_t *pSeeThrough)
  726. {
  727. portal_t *p;
  728. int s;
  729. bspbrush_t *b;
  730. entity_t *e;
  731. if ( IsAreaportalNode(node) )
  732. {
  733. // this node is part of an area portal
  734. b = AreaportalBrushForNode( node );
  735. e = &entities[b->original->entitynum];
  736. // if the current area has allready touched this
  737. // portal, we are done
  738. if (e->portalareas[0] == c_areas || e->portalareas[1] == c_areas)
  739. return;
  740. // note the current area as bounding the portal
  741. if (e->portalareas[1])
  742. {
  743. Warning("WARNING: areaportal entity %i (brush %i) touches > 2 areas\n", b->original->entitynum, b->original->id );
  744. return;
  745. }
  746. if (e->portalareas[0])
  747. {
  748. e->portalareas[1] = c_areas;
  749. e->m_pPortalsLeadingIntoAreas[1] = pSeeThrough;
  750. }
  751. else
  752. {
  753. e->portalareas[0] = c_areas;
  754. e->m_pPortalsLeadingIntoAreas[0] = pSeeThrough;
  755. }
  756. return;
  757. }
  758. if (node->area)
  759. return; // allready got it
  760. node->area = c_areas;
  761. for (p=node->portals ; p ; p = p->next[s])
  762. {
  763. s = (p->nodes[1] == node);
  764. #if 0
  765. if (p->nodes[!s]->occupied)
  766. continue;
  767. #endif
  768. if (!Portal_EntityFlood (p, s))
  769. continue;
  770. FloodAreas_r (p->nodes[!s], p);
  771. }
  772. }
  773. /*
  774. =============
  775. FindAreas_r
  776. Just decend the tree, and for each node that hasn't had an
  777. area set, flood fill out from there
  778. =============
  779. */
  780. void FindAreas_r (node_t *node)
  781. {
  782. if (node->planenum != PLANENUM_LEAF)
  783. {
  784. FindAreas_r (node->children[0]);
  785. FindAreas_r (node->children[1]);
  786. return;
  787. }
  788. if (node->area)
  789. return; // allready got it
  790. if (node->contents & CONTENTS_SOLID)
  791. return;
  792. if (!node->occupied)
  793. return; // not reachable by entities
  794. // area portals are allways only flooded into, never
  795. // out of
  796. if (IsAreaportalNode(node))
  797. return;
  798. c_areas++;
  799. FloodAreas_r (node, NULL);
  800. }
  801. void ReportAreaportalLeak( tree_t *tree, node_t *node )
  802. {
  803. portal_t *p, *pStart = NULL;
  804. int s;
  805. // Find a portal out of this areaportal into empty space
  806. for (p=node->portals ; p ; p = p->next[s])
  807. {
  808. s = (p->nodes[1] == node);
  809. if ( !Portal_EntityFlood( p, !s ) )
  810. continue;
  811. if ( p->nodes[!s]->contents & CONTENTS_AREAPORTAL )
  812. continue;
  813. pStart = p;
  814. break;
  815. }
  816. if ( pStart )
  817. {
  818. s = pStart->nodes[0] == node;
  819. Assert(!(pStart->nodes[s]->contents & CONTENTS_AREAPORTAL) );
  820. // flood fill the area outside this areaportal brush
  821. FloodAreaLeak( tree->headnode, pStart->nodes[s] );
  822. // find the portal into the longest path around the portal
  823. portal_t *pBest = NULL;
  824. int bestDist = 0;
  825. for (p=node->portals ; p ; p = p->next[s])
  826. {
  827. if ( p == pStart )
  828. continue;
  829. s = (p->nodes[1] == node);
  830. if ( p->nodes[!s]->occupied > bestDist )
  831. {
  832. pBest = p;
  833. bestDist = p->nodes[!s]->occupied;
  834. }
  835. }
  836. if ( pBest )
  837. {
  838. s = (pBest->nodes[0] == node);
  839. // write the linefile that goes from pBest to pStart
  840. AreaportalLeakFile( tree, pStart, pBest, pBest->nodes[s] );
  841. }
  842. }
  843. }
  844. /*
  845. =============
  846. SetAreaPortalAreas_r
  847. Just decend the tree, and for each node that hasn't had an
  848. area set, flood fill out from there
  849. =============
  850. */
  851. void SetAreaPortalAreas_r (tree_t *tree, node_t *node)
  852. {
  853. bspbrush_t *b;
  854. entity_t *e;
  855. if (node->planenum != PLANENUM_LEAF)
  856. {
  857. SetAreaPortalAreas_r (tree, node->children[0]);
  858. SetAreaPortalAreas_r (tree, node->children[1]);
  859. return;
  860. }
  861. if (IsAreaportalNode(node))
  862. {
  863. if (node->area)
  864. return; // already set
  865. b = AreaportalBrushForNode( node );
  866. e = &entities[b->original->entitynum];
  867. node->area = e->portalareas[0];
  868. if (!e->portalareas[1])
  869. {
  870. ReportAreaportalLeak( tree, node );
  871. Warning("\nBrush %i: areaportal brush doesn't touch two areas\n", b->original->id);
  872. return;
  873. }
  874. }
  875. }
  876. // Return a positive value between 0 and 2*PI telling the angle distance
  877. // from flBaseAngle to flTestAngle.
  878. float AngleOffset( float flBaseAngle, float flTestAngle )
  879. {
  880. while( flTestAngle > flBaseAngle )
  881. flTestAngle -= 2 * M_PI;
  882. return fmod( flBaseAngle - flTestAngle, (float) (2 * M_PI) );
  883. }
  884. int FindUniquePoints( const Vector2D *pPoints, int nPoints, int *indexMap, int nMaxIndexMapPoints, float flTolerance )
  885. {
  886. float flToleranceSqr = flTolerance * flTolerance;
  887. // This could be slightly more efficient.
  888. int nUniquePoints = 0;
  889. for ( int i=0; i < nPoints; i++ )
  890. {
  891. int j;
  892. for ( j=0; j < nUniquePoints; j++ )
  893. {
  894. if ( pPoints[i].DistToSqr( pPoints[indexMap[j]] ) < flToleranceSqr )
  895. break;
  896. }
  897. if ( j == nUniquePoints )
  898. {
  899. if ( nUniquePoints >= nMaxIndexMapPoints )
  900. Error( "FindUniquePoints: overflowed unique point list (size %d).", nMaxIndexMapPoints );
  901. indexMap[nUniquePoints++] = i;
  902. }
  903. }
  904. return nUniquePoints;
  905. }
  906. // Build a 2D convex hull of the set of points.
  907. // This essentially giftwraps the points as it walks around the perimeter.
  908. int Convex2D( Vector2D const *pPoints, int nPoints, int *indices, int nMaxIndices )
  909. {
  910. int nIndices = 0;
  911. bool touched[512];
  912. int indexMap[512];
  913. if( nPoints == 0 )
  914. return 0;
  915. // If we don't collapse the points into a unique set, we can loop around forever
  916. // and max out nMaxIndices.
  917. nPoints = FindUniquePoints( pPoints, nPoints, indexMap, ARRAYSIZE( indexMap ), 0.1f );
  918. memset( touched, 0, nPoints*sizeof(touched[0]) );
  919. // Find the (lower) left side.
  920. int i;
  921. int iBest = 0;
  922. for( i=1; i < nPoints; i++ )
  923. {
  924. if( pPoints[indexMap[i]].x < pPoints[indexMap[iBest]].x ||
  925. (pPoints[indexMap[i]].x == pPoints[indexMap[iBest]].x && pPoints[indexMap[i]].y < pPoints[indexMap[iBest]].y) )
  926. {
  927. iBest = i;
  928. }
  929. }
  930. touched[iBest] = true;
  931. indices[0] = indexMap[iBest];
  932. nIndices = 1;
  933. Vector2D curEdge( 0, 1 );
  934. // Wind around clockwise.
  935. while( 1 )
  936. {
  937. Vector2D const *pStartPoint = &pPoints[ indices[nIndices-1] ];
  938. float flEdgeAngle = atan2( curEdge.y, curEdge.x );
  939. int iMinAngle = -1;
  940. float flMinAngle = 5000;
  941. for( i=0; i < nPoints; i++ )
  942. {
  943. Vector2D vTo = pPoints[indexMap[i]] - *pStartPoint;
  944. float flDistToSqr = vTo.LengthSqr();
  945. if ( flDistToSqr <= 0.1f )
  946. continue;
  947. // Get the angle from the edge to this point.
  948. float flAngle = atan2( vTo.y, vTo.x );
  949. flAngle = AngleOffset( flEdgeAngle, flAngle );
  950. if( fabs( flAngle - flMinAngle ) < 0.00001f )
  951. {
  952. float flDistToTestSqr = pStartPoint->DistToSqr( pPoints[iMinAngle] );
  953. // If the angle is the same, pick the point farthest away.
  954. // unless the current one is closing the face loop
  955. if ( iMinAngle != indices[0] && flDistToSqr > flDistToTestSqr )
  956. {
  957. flMinAngle = flAngle;
  958. iMinAngle = indexMap[i];
  959. }
  960. }
  961. else if( flAngle < flMinAngle )
  962. {
  963. flMinAngle = flAngle;
  964. iMinAngle = indexMap[i];
  965. }
  966. }
  967. if( iMinAngle == -1 )
  968. {
  969. // Couldn't find a point?
  970. Assert( false );
  971. break;
  972. }
  973. else if( iMinAngle == indices[0] )
  974. {
  975. // Finished.
  976. break;
  977. }
  978. else
  979. {
  980. // Add this point.
  981. if( nIndices >= nMaxIndices )
  982. break;
  983. for ( int jj = 0; jj < nIndices; jj++ )
  984. {
  985. // if this assert hits, this routine is broken and is generating a spiral
  986. // rather than a closed polygon - basically an edge overlap of some kind
  987. Assert(indices[jj] != iMinAngle );
  988. }
  989. indices[nIndices] = iMinAngle;
  990. ++nIndices;
  991. }
  992. curEdge = pPoints[indices[nIndices-1]] - pPoints[indices[nIndices-2]];
  993. }
  994. return nIndices;
  995. }
  996. void FindPortalsLeadingToArea_R(
  997. node_t *pHeadNode,
  998. int iSrcArea,
  999. int iDestArea,
  1000. plane_t *pPlane,
  1001. CUtlVector<portal_t*> &portals )
  1002. {
  1003. if (pHeadNode->planenum != PLANENUM_LEAF)
  1004. {
  1005. FindPortalsLeadingToArea_R( pHeadNode->children[0], iSrcArea, iDestArea, pPlane, portals );
  1006. FindPortalsLeadingToArea_R( pHeadNode->children[1], iSrcArea, iDestArea, pPlane, portals );
  1007. return;
  1008. }
  1009. // Ok.. this is a leaf, check its portals.
  1010. int s;
  1011. for( portal_t *p = pHeadNode->portals; p ;p = p->next[!s] )
  1012. {
  1013. s = (p->nodes[0] == pHeadNode);
  1014. if( !p->nodes[0]->occupied || !p->nodes[1]->occupied )
  1015. continue;
  1016. if( p->nodes[1]->area == iDestArea && p->nodes[0]->area == iSrcArea ||
  1017. p->nodes[0]->area == iDestArea && p->nodes[1]->area == iSrcArea )
  1018. {
  1019. // Make sure the plane normals point the same way.
  1020. plane_t *pMapPlane = &g_MainMap->mapplanes[p->onnode->planenum];
  1021. float flDot = fabs( pMapPlane->normal.Dot( pPlane->normal ) );
  1022. if( fabs( 1 - flDot ) < 0.01f )
  1023. {
  1024. Vector vPlanePt1 = pPlane->normal * pPlane->dist;
  1025. Vector vPlanePt2 = pMapPlane->normal * pMapPlane->dist;
  1026. if( vPlanePt1.DistToSqr( vPlanePt2 ) < 0.01f )
  1027. {
  1028. portals.AddToTail( p );
  1029. }
  1030. }
  1031. }
  1032. }
  1033. }
  1034. void EmitClipPortalGeometry( node_t *pHeadNode, portal_t *pPortal, int iSrcArea, dareaportal_t *dp )
  1035. {
  1036. // Build a list of all the points in portals from the same original face.
  1037. CUtlVector<portal_t*> portals;
  1038. FindPortalsLeadingToArea_R(
  1039. pHeadNode,
  1040. iSrcArea,
  1041. dp->otherarea,
  1042. &pPortal->plane,
  1043. portals );
  1044. CUtlVector<Vector> points;
  1045. for( int iPortal=0; iPortal < portals.Count(); iPortal++ )
  1046. {
  1047. portal_t *pPointPortal = portals[iPortal];
  1048. winding_t *pWinding = pPointPortal->winding;
  1049. for( int i=0; i < pWinding->numpoints; i++ )
  1050. {
  1051. points.AddToTail( pWinding->p[i] );
  1052. }
  1053. }
  1054. // Get the 2D convex hull.
  1055. //// First transform them into a plane.
  1056. QAngle vAngles;
  1057. Vector vecs[3];
  1058. VectorAngles( pPortal->plane.normal, vAngles );
  1059. AngleVectors( vAngles, &vecs[0], &vecs[1], &vecs[2] );
  1060. VMatrix mTransform;
  1061. mTransform.Identity();
  1062. mTransform.SetBasisVectors( vecs[0], vecs[1], vecs[2] );
  1063. VMatrix mInvTransform = mTransform.Transpose();
  1064. int i;
  1065. CUtlVector<Vector2D> points2D;
  1066. for( i=0; i < points.Count(); i++ )
  1067. {
  1068. Vector vTest = mTransform * points[i];
  1069. points2D.AddToTail( Vector2D( vTest.y, vTest.z ) );
  1070. }
  1071. // Build the hull.
  1072. int indices[512];
  1073. int nIndices = Convex2D( points2D.Base(), points2D.Count(), indices, 512 );
  1074. // Output the hull.
  1075. dp->m_FirstClipPortalVert = g_nClipPortalVerts;
  1076. dp->m_nClipPortalVerts = nIndices;
  1077. if ( nIndices >= 32 )
  1078. {
  1079. Warning( "Warning: area portal has %d verts. Could be a vbsp bug.\n", nIndices );
  1080. }
  1081. if( dp->m_FirstClipPortalVert + dp->m_nClipPortalVerts >= MAX_MAP_PORTALVERTS )
  1082. {
  1083. Vector *p = pPortal->winding->p;
  1084. Error( "MAX_MAP_PORTALVERTS (probably a broken areaportal near %.1f %.1f %.1f ", p->x, p->y, p->z );
  1085. }
  1086. for( i=0; i < nIndices; i++ )
  1087. {
  1088. g_ClipPortalVerts[g_nClipPortalVerts] = points[ indices[i] ];
  1089. ++g_nClipPortalVerts;
  1090. }
  1091. }
  1092. // Sets node_t::area for non-leaf nodes (this allows an optimization in the renderer).
  1093. void SetNodeAreaIndices_R( node_t *node )
  1094. {
  1095. // All leaf area indices should already be set.
  1096. if( node->planenum == PLANENUM_LEAF )
  1097. return;
  1098. // Have the children set their area indices.
  1099. SetNodeAreaIndices_R( node->children[0] );
  1100. SetNodeAreaIndices_R( node->children[1] );
  1101. // If all children (leaves or nodes) are in the same area, then set our area
  1102. // to this area as well. Otherwise, set it to -1.
  1103. if( node->children[0]->area == node->children[1]->area )
  1104. node->area = node->children[0]->area;
  1105. else
  1106. node->area = -1;
  1107. }
  1108. /*
  1109. =============
  1110. EmitAreaPortals
  1111. =============
  1112. */
  1113. void EmitAreaPortals (node_t *headnode)
  1114. {
  1115. entity_t *e;
  1116. dareaportal_t *dp;
  1117. if (c_areas > MAX_MAP_AREAS)
  1118. Error ("Map is split into %d unique areas which is too many (max = %d)\nProbably too many areaportals", c_areas, MAX_MAP_AREAS);
  1119. numareas = c_areas+1;
  1120. numareaportals = 1; // leave 0 as an error
  1121. // Reset the clip portal vert info.
  1122. g_nClipPortalVerts = 0;
  1123. for (int iSrcArea=1 ; iSrcArea<=c_areas ; iSrcArea++)
  1124. {
  1125. dareas[iSrcArea].firstareaportal = numareaportals;
  1126. for (int j=0 ; j<num_entities ; j++)
  1127. {
  1128. e = &entities[j];
  1129. if (!e->areaportalnum)
  1130. continue;
  1131. if (e->portalareas[0] == iSrcArea || e->portalareas[1] == iSrcArea)
  1132. {
  1133. int iSide = (e->portalareas[0] == iSrcArea);
  1134. // We're only interested in the portal that divides the two areas.
  1135. // One of the portals that leads into the CONTENTS_AREAPORTAL just bounds
  1136. // the same two areas but the other bounds two different ones.
  1137. portal_t *pLeadingPortal = e->m_pPortalsLeadingIntoAreas[0];
  1138. if( pLeadingPortal->nodes[0]->area == pLeadingPortal->nodes[1]->area )
  1139. pLeadingPortal = e->m_pPortalsLeadingIntoAreas[1];
  1140. if( pLeadingPortal )
  1141. {
  1142. Assert( pLeadingPortal->nodes[0]->area != pLeadingPortal->nodes[1]->area );
  1143. dp = &dareaportals[numareaportals];
  1144. numareaportals++;
  1145. dp->m_PortalKey = e->areaportalnum;
  1146. dp->otherarea = e->portalareas[iSide];
  1147. dp->planenum = pLeadingPortal->onnode->planenum;
  1148. Assert( pLeadingPortal->nodes[0]->planenum == PLANENUM_LEAF );
  1149. Assert( pLeadingPortal->nodes[1]->planenum == PLANENUM_LEAF );
  1150. if( pLeadingPortal->nodes[0]->area == dp->otherarea )
  1151. {
  1152. // Use the flipped version of the plane.
  1153. dp->planenum = (dp->planenum & ~1) | (~dp->planenum & 1);
  1154. }
  1155. EmitClipPortalGeometry( headnode, pLeadingPortal, iSrcArea, dp );
  1156. }
  1157. }
  1158. }
  1159. dareas[iSrcArea].numareaportals = numareaportals - dareas[iSrcArea].firstareaportal;
  1160. }
  1161. SetNodeAreaIndices_R( headnode );
  1162. qprintf ("%5i numareas\n", numareas);
  1163. qprintf ("%5i numareaportals\n", numareaportals);
  1164. }
  1165. /*
  1166. =============
  1167. FloodAreas
  1168. Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL
  1169. =============
  1170. */
  1171. void FloodAreas (tree_t *tree)
  1172. {
  1173. int start = Plat_FloatTime();
  1174. qprintf ("--- FloodAreas ---\n");
  1175. Msg("Processing areas...");
  1176. FindAreas_r (tree->headnode);
  1177. SetAreaPortalAreas_r (tree, tree->headnode);
  1178. qprintf ("%5i areas\n", c_areas);
  1179. Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
  1180. }
  1181. //======================================================
  1182. int c_outside;
  1183. int c_inside;
  1184. int c_solid;
  1185. void FillOutside_r (node_t *node)
  1186. {
  1187. if (node->planenum != PLANENUM_LEAF)
  1188. {
  1189. FillOutside_r (node->children[0]);
  1190. FillOutside_r (node->children[1]);
  1191. return;
  1192. }
  1193. // anything not reachable by an entity
  1194. // can be filled away
  1195. if (!node->occupied)
  1196. {
  1197. if (node->contents != CONTENTS_SOLID)
  1198. {
  1199. c_outside++;
  1200. node->contents = CONTENTS_SOLID;
  1201. }
  1202. else
  1203. c_solid++;
  1204. }
  1205. else
  1206. c_inside++;
  1207. }
  1208. /*
  1209. =============
  1210. FillOutside
  1211. Fill all nodes that can't be reached by entities
  1212. =============
  1213. */
  1214. void FillOutside (node_t *headnode)
  1215. {
  1216. c_outside = 0;
  1217. c_inside = 0;
  1218. c_solid = 0;
  1219. qprintf ("--- FillOutside ---\n");
  1220. FillOutside_r (headnode);
  1221. qprintf ("%5i solid leafs\n", c_solid);
  1222. qprintf ("%5i leafs filled\n", c_outside);
  1223. qprintf ("%5i inside leafs\n", c_inside);
  1224. }
  1225. static float ComputeDistFromPlane( winding_t *pWinding, plane_t *pPlane, float maxdist )
  1226. {
  1227. float totaldist = 0.0f;
  1228. for (int i = 0; i < pWinding->numpoints; ++i)
  1229. {
  1230. totaldist += fabs(DotProduct( pPlane->normal, pWinding->p[i] ) - pPlane->dist);
  1231. if (totaldist > maxdist)
  1232. return totaldist;
  1233. }
  1234. return totaldist;
  1235. }
  1236. //-----------------------------------------------------------------------------
  1237. // Display portal error
  1238. //-----------------------------------------------------------------------------
  1239. static void DisplayPortalError( portal_t *p, int viscontents )
  1240. {
  1241. char contents[3][1024];
  1242. PrintBrushContentsToString( p->nodes[0]->contents, contents[0], sizeof( contents[0] ) );
  1243. PrintBrushContentsToString( p->nodes[1]->contents, contents[1], sizeof( contents[1] ) );
  1244. PrintBrushContentsToString( viscontents, contents[2], sizeof( contents[2] ) );
  1245. Vector center;
  1246. WindingCenter( p->winding, center );
  1247. Warning( "\nFindPortalSide: Couldn't find a good match for which brush to assign to a portal near (%.1f %.1f %.1f)\n", center.x, center.y, center.z);
  1248. Warning( "Leaf 0 contents: %s\n", contents[0] );
  1249. Warning( "Leaf 1 contents: %s\n", contents[1] );
  1250. Warning( "viscontents (node 0 contents ^ node 1 contents): %s\n", contents[2] );
  1251. Warning( "This means that none of the brushes in leaf 0 or 1 that touches the portal has %s\n", contents[2] );
  1252. Warning( "Check for a huge brush enclosing the coordinates above that has contents %s\n", contents[2] );
  1253. Warning( "Candidate brush IDs: " );
  1254. CUtlVector<int> listed;
  1255. for (int j=0 ; j<2 ; j++)
  1256. {
  1257. node_t *n = p->nodes[j];
  1258. for (bspbrush_t *bb=n->brushlist ; bb ; bb=bb->next)
  1259. {
  1260. mapbrush_t *brush = bb->original;
  1261. if ( brush->contents & viscontents )
  1262. {
  1263. if ( listed.Find( brush->brushnum ) == -1 )
  1264. {
  1265. listed.AddToTail( brush->brushnum );
  1266. Warning( "Brush %d: ", brush->id );
  1267. }
  1268. }
  1269. }
  1270. }
  1271. Warning( "\n\n" );
  1272. }
  1273. //==============================================================
  1274. /*
  1275. ============
  1276. FindPortalSide
  1277. Finds a brush side to use for texturing the given portal
  1278. ============
  1279. */
  1280. void FindPortalSide (portal_t *p)
  1281. {
  1282. int viscontents;
  1283. bspbrush_t *bb;
  1284. mapbrush_t *brush;
  1285. node_t *n;
  1286. int i,j;
  1287. int planenum;
  1288. side_t *side, *bestside;
  1289. float bestdist;
  1290. plane_t *p1, *p2;
  1291. // decide which content change is strongest
  1292. // solid > lava > water, etc
  1293. viscontents = VisibleContents (p->nodes[0]->contents ^ p->nodes[1]->contents);
  1294. if (!viscontents)
  1295. {
  1296. return;
  1297. }
  1298. planenum = p->onnode->planenum;
  1299. bestside = NULL;
  1300. bestdist = 1000000;
  1301. for (j=0 ; j<2 ; j++)
  1302. {
  1303. n = p->nodes[j];
  1304. p1 = &g_MainMap->mapplanes[p->onnode->planenum];
  1305. for (bb=n->brushlist ; bb ; bb=bb->next)
  1306. {
  1307. brush = bb->original;
  1308. if ( !(brush->contents & viscontents) )
  1309. continue;
  1310. for (i=0 ; i<brush->numsides ; i++)
  1311. {
  1312. side = &brush->original_sides[i];
  1313. if (side->bevel)
  1314. continue;
  1315. if (side->texinfo == TEXINFO_NODE)
  1316. continue; // non-visible
  1317. if ((side->planenum&~1) == planenum)
  1318. { // exact match
  1319. bestside = &brush->original_sides[i];
  1320. bestdist = 0.0f;
  1321. goto gotit;
  1322. }
  1323. p2 = &g_MainMap->mapplanes[side->planenum&~1];
  1324. float dist = ComputeDistFromPlane( p->winding, p2, bestdist );
  1325. if (dist < bestdist)
  1326. {
  1327. bestside = side;
  1328. bestdist = dist;
  1329. }
  1330. }
  1331. }
  1332. }
  1333. gotit:
  1334. if (!bestside)
  1335. qprintf ("WARNING: side not found for portal\n");
  1336. // Compute average dist, check for problems...
  1337. if ( ((bestdist / p->winding->numpoints) > 2) && ( p->nodes[0]->brushlist || p->nodes[1]->brushlist ) )
  1338. {
  1339. static int nWarnCount = 0;
  1340. if ( nWarnCount < 8 )
  1341. {
  1342. DisplayPortalError( p, viscontents );
  1343. if ( ++nWarnCount == 8 )
  1344. {
  1345. Warning("*** Suppressing further FindPortalSide errors.... ***\n" );
  1346. }
  1347. }
  1348. }
  1349. p->sidefound = true;
  1350. p->side = bestside;
  1351. }
  1352. /*
  1353. ===============
  1354. MarkVisibleSides_r
  1355. ===============
  1356. */
  1357. void MarkVisibleSides_r (node_t *node)
  1358. {
  1359. portal_t *p;
  1360. int s;
  1361. if (node->planenum != PLANENUM_LEAF)
  1362. {
  1363. MarkVisibleSides_r (node->children[0]);
  1364. MarkVisibleSides_r (node->children[1]);
  1365. return;
  1366. }
  1367. // empty leafs are never boundary leafs
  1368. if (!node->contents)
  1369. return;
  1370. // see if there is a visible face
  1371. for (p=node->portals ; p ; p = p->next[!s])
  1372. {
  1373. s = (p->nodes[0] == node);
  1374. if (!p->onnode)
  1375. continue; // edge of world
  1376. if (!p->sidefound)
  1377. FindPortalSide (p);
  1378. if (p->side)
  1379. p->side->visible = true;
  1380. }
  1381. }
  1382. /*
  1383. =============
  1384. MarkVisibleSides
  1385. =============
  1386. */
  1387. // UNDONE: Put detail brushes in a separate list (not mapbrushes) ?
  1388. void MarkVisibleSides (tree_t *tree, int startbrush, int endbrush, int detailScreen)
  1389. {
  1390. int i, j;
  1391. mapbrush_t *mb;
  1392. int numsides;
  1393. qboolean detail;
  1394. qprintf ("--- MarkVisibleSides ---\n");
  1395. // clear all the visible flags
  1396. for (i=startbrush ; i<endbrush ; i++)
  1397. {
  1398. mb = &g_MainMap->mapbrushes[i];
  1399. if ( detailScreen != FULL_DETAIL )
  1400. {
  1401. qboolean onlyDetail = (detailScreen==ONLY_DETAIL)?true:false;
  1402. // true for detail brushes
  1403. detail = (mb->contents & CONTENTS_DETAIL) ? true : false;
  1404. if ( onlyDetail ^ detail )
  1405. {
  1406. // both of these must have the same value or we're not interested in this brush
  1407. continue;
  1408. }
  1409. }
  1410. numsides = mb->numsides;
  1411. for (j=0 ; j<numsides ; j++)
  1412. mb->original_sides[j].visible = false;
  1413. }
  1414. // set visible flags on the sides that are used by portals
  1415. MarkVisibleSides_r (tree->headnode);
  1416. }
  1417. //-----------------------------------------------------------------------------
  1418. // Used to determine which sides are visible
  1419. //-----------------------------------------------------------------------------
  1420. void MarkVisibleSides (tree_t *tree, mapbrush_t **ppBrushes, int nCount )
  1421. {
  1422. qprintf ("--- MarkVisibleSides ---\n");
  1423. // clear all the visible flags
  1424. int i, j;
  1425. for ( i=0; i < nCount; ++i )
  1426. {
  1427. mapbrush_t *mb = ppBrushes[i];
  1428. int numsides = mb->numsides;
  1429. for (j=0 ; j<numsides ; j++)
  1430. {
  1431. mb->original_sides[j].visible = false;
  1432. }
  1433. }
  1434. // set visible flags on the sides that are used by portals
  1435. MarkVisibleSides_r( tree->headnode );
  1436. }