portals.c (16497B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 23 #include "qbsp.h" 24 25 26 int c_active_portals; 27 int c_peak_portals; 28 int c_boundary; 29 int c_boundary_sides; 30 31 /* 32 =========== 33 AllocPortal 34 =========== 35 */ 36 portal_t *AllocPortal (void) 37 { 38 portal_t *p; 39 40 if (numthreads == 1) 41 c_active_portals++; 42 if (c_active_portals > c_peak_portals) 43 c_peak_portals = c_active_portals; 44 45 p = malloc (sizeof(portal_t)); 46 memset (p, 0, sizeof(portal_t)); 47 48 return p; 49 } 50 51 void FreePortal (portal_t *p) 52 { 53 if (p->winding) 54 FreeWinding (p->winding); 55 if (numthreads == 1) 56 c_active_portals--; 57 free (p); 58 } 59 60 //============================================================== 61 62 /* 63 ============= 64 Portal_Passable 65 66 Returns true if the portal has non-opaque leafs on both sides 67 ============= 68 */ 69 qboolean Portal_Passable(portal_t *p) { 70 if (!p->onnode) { 71 return qfalse; // to global outsideleaf 72 } 73 74 if (p->nodes[0]->planenum != PLANENUM_LEAF 75 || p->nodes[1]->planenum != PLANENUM_LEAF) { 76 Error ("Portal_EntityFlood: not a leaf"); 77 } 78 79 if ( !p->nodes[0]->opaque && !p->nodes[1]->opaque ) { 80 return qtrue; 81 } 82 83 return qfalse; 84 } 85 86 87 //============================================================================= 88 89 int c_tinyportals; 90 91 /* 92 ============= 93 AddPortalToNodes 94 ============= 95 */ 96 void AddPortalToNodes (portal_t *p, node_t *front, node_t *back) 97 { 98 if (p->nodes[0] || p->nodes[1]) 99 Error ("AddPortalToNode: allready included"); 100 101 p->nodes[0] = front; 102 p->next[0] = front->portals; 103 front->portals = p; 104 105 p->nodes[1] = back; 106 p->next[1] = back->portals; 107 back->portals = p; 108 } 109 110 111 /* 112 ============= 113 RemovePortalFromNode 114 ============= 115 */ 116 void RemovePortalFromNode (portal_t *portal, node_t *l) 117 { 118 portal_t **pp, *t; 119 120 // remove reference to the current portal 121 pp = &l->portals; 122 while (1) 123 { 124 t = *pp; 125 if (!t) 126 Error ("RemovePortalFromNode: portal not in leaf"); 127 128 if ( t == portal ) 129 break; 130 131 if (t->nodes[0] == l) 132 pp = &t->next[0]; 133 else if (t->nodes[1] == l) 134 pp = &t->next[1]; 135 else 136 Error ("RemovePortalFromNode: portal not bounding leaf"); 137 } 138 139 if (portal->nodes[0] == l) 140 { 141 *pp = portal->next[0]; 142 portal->nodes[0] = NULL; 143 } 144 else if (portal->nodes[1] == l) 145 { 146 *pp = portal->next[1]; 147 portal->nodes[1] = NULL; 148 } 149 } 150 151 //============================================================================ 152 153 void PrintPortal (portal_t *p) 154 { 155 int i; 156 winding_t *w; 157 158 w = p->winding; 159 for (i=0 ; i<w->numpoints ; i++) 160 _printf ("(%5.0f,%5.0f,%5.0f)\n",w->p[i][0] 161 , w->p[i][1], w->p[i][2]); 162 } 163 164 /* 165 ================ 166 MakeHeadnodePortals 167 168 The created portals will face the global outside_node 169 ================ 170 */ 171 #define SIDESPACE 8 172 void MakeHeadnodePortals (tree_t *tree) 173 { 174 vec3_t bounds[2]; 175 int i, j, n; 176 portal_t *p, *portals[6]; 177 plane_t bplanes[6], *pl; 178 node_t *node; 179 180 node = tree->headnode; 181 182 // pad with some space so there will never be null volume leafs 183 for (i=0 ; i<3 ; i++) 184 { 185 bounds[0][i] = tree->mins[i] - SIDESPACE; 186 bounds[1][i] = tree->maxs[i] + SIDESPACE; 187 if ( bounds[0][i] >= bounds[1][i] ) { 188 Error( "Backwards tree volume" ); 189 } 190 } 191 192 tree->outside_node.planenum = PLANENUM_LEAF; 193 tree->outside_node.brushlist = NULL; 194 tree->outside_node.portals = NULL; 195 tree->outside_node.opaque = qfalse; 196 197 for (i=0 ; i<3 ; i++) 198 for (j=0 ; j<2 ; j++) 199 { 200 n = j*3 + i; 201 202 p = AllocPortal (); 203 portals[n] = p; 204 205 pl = &bplanes[n]; 206 memset (pl, 0, sizeof(*pl)); 207 if (j) 208 { 209 pl->normal[i] = -1; 210 pl->dist = -bounds[j][i]; 211 } 212 else 213 { 214 pl->normal[i] = 1; 215 pl->dist = bounds[j][i]; 216 } 217 p->plane = *pl; 218 p->winding = BaseWindingForPlane (pl->normal, pl->dist); 219 AddPortalToNodes (p, node, &tree->outside_node); 220 } 221 222 // clip the basewindings by all the other planes 223 for (i=0 ; i<6 ; i++) 224 { 225 for (j=0 ; j<6 ; j++) 226 { 227 if (j == i) 228 continue; 229 ChopWindingInPlace (&portals[i]->winding, bplanes[j].normal, bplanes[j].dist, ON_EPSILON); 230 } 231 } 232 } 233 234 //=================================================== 235 236 237 /* 238 ================ 239 BaseWindingForNode 240 ================ 241 */ 242 #define BASE_WINDING_EPSILON 0.001 243 #define SPLIT_WINDING_EPSILON 0.001 244 245 winding_t *BaseWindingForNode (node_t *node) 246 { 247 winding_t *w; 248 node_t *n; 249 plane_t *plane; 250 vec3_t normal; 251 vec_t dist; 252 253 w = BaseWindingForPlane (mapplanes[node->planenum].normal 254 , mapplanes[node->planenum].dist); 255 256 // clip by all the parents 257 for (n=node->parent ; n && w ; ) 258 { 259 plane = &mapplanes[n->planenum]; 260 261 if (n->children[0] == node) 262 { // take front 263 ChopWindingInPlace (&w, plane->normal, plane->dist, BASE_WINDING_EPSILON); 264 } 265 else 266 { // take back 267 VectorSubtract (vec3_origin, plane->normal, normal); 268 dist = -plane->dist; 269 ChopWindingInPlace (&w, normal, dist, BASE_WINDING_EPSILON); 270 } 271 node = n; 272 n = n->parent; 273 } 274 275 return w; 276 } 277 278 //============================================================ 279 280 /* 281 ================== 282 MakeNodePortal 283 284 create the new portal by taking the full plane winding for the cutting plane 285 and clipping it by all of parents of this node 286 ================== 287 */ 288 void MakeNodePortal (node_t *node) 289 { 290 portal_t *new_portal, *p; 291 winding_t *w; 292 vec3_t normal; 293 float dist; 294 int side; 295 296 w = BaseWindingForNode (node); 297 298 // clip the portal by all the other portals in the node 299 for (p = node->portals ; p && w; p = p->next[side]) 300 { 301 if (p->nodes[0] == node) 302 { 303 side = 0; 304 VectorCopy (p->plane.normal, normal); 305 dist = p->plane.dist; 306 } 307 else if (p->nodes[1] == node) 308 { 309 side = 1; 310 VectorSubtract (vec3_origin, p->plane.normal, normal); 311 dist = -p->plane.dist; 312 } 313 else 314 Error ("CutNodePortals_r: mislinked portal"); 315 316 ChopWindingInPlace (&w, normal, dist, CLIP_EPSILON); 317 } 318 319 if (!w) 320 { 321 return; 322 } 323 324 if (WindingIsTiny (w)) 325 { 326 c_tinyportals++; 327 FreeWinding (w); 328 return; 329 } 330 331 new_portal = AllocPortal (); 332 new_portal->plane = mapplanes[node->planenum]; 333 new_portal->onnode = node; 334 new_portal->winding = w; 335 new_portal->hint = node->hint; 336 AddPortalToNodes (new_portal, node->children[0], node->children[1]); 337 } 338 339 340 /* 341 ============== 342 SplitNodePortals 343 344 Move or split the portals that bound node so that the node's 345 children have portals instead of node. 346 ============== 347 */ 348 void SplitNodePortals (node_t *node) 349 { 350 portal_t *p, *next_portal, *new_portal; 351 node_t *f, *b, *other_node; 352 int side; 353 plane_t *plane; 354 winding_t *frontwinding, *backwinding; 355 356 plane = &mapplanes[node->planenum]; 357 f = node->children[0]; 358 b = node->children[1]; 359 360 for (p = node->portals ; p ; p = next_portal) 361 { 362 if (p->nodes[0] == node) 363 side = 0; 364 else if (p->nodes[1] == node) 365 side = 1; 366 else 367 Error ("SplitNodePortals: mislinked portal"); 368 next_portal = p->next[side]; 369 370 other_node = p->nodes[!side]; 371 RemovePortalFromNode (p, p->nodes[0]); 372 RemovePortalFromNode (p, p->nodes[1]); 373 374 // 375 // cut the portal into two portals, one on each side of the cut plane 376 // 377 ClipWindingEpsilon (p->winding, plane->normal, plane->dist, 378 SPLIT_WINDING_EPSILON, &frontwinding, &backwinding); 379 380 if (frontwinding && WindingIsTiny(frontwinding)) 381 { 382 if (!f->tinyportals) 383 VectorCopy(frontwinding->p[0], f->referencepoint); 384 f->tinyportals++; 385 if (!other_node->tinyportals) 386 VectorCopy(frontwinding->p[0], other_node->referencepoint); 387 other_node->tinyportals++; 388 389 FreeWinding (frontwinding); 390 frontwinding = NULL; 391 c_tinyportals++; 392 } 393 394 if (backwinding && WindingIsTiny(backwinding)) 395 { 396 if (!b->tinyportals) 397 VectorCopy(backwinding->p[0], b->referencepoint); 398 b->tinyportals++; 399 if (!other_node->tinyportals) 400 VectorCopy(backwinding->p[0], other_node->referencepoint); 401 other_node->tinyportals++; 402 403 FreeWinding (backwinding); 404 backwinding = NULL; 405 c_tinyportals++; 406 } 407 408 if (!frontwinding && !backwinding) 409 { // tiny windings on both sides 410 continue; 411 } 412 413 if (!frontwinding) 414 { 415 FreeWinding (backwinding); 416 if (side == 0) 417 AddPortalToNodes (p, b, other_node); 418 else 419 AddPortalToNodes (p, other_node, b); 420 continue; 421 } 422 if (!backwinding) 423 { 424 FreeWinding (frontwinding); 425 if (side == 0) 426 AddPortalToNodes (p, f, other_node); 427 else 428 AddPortalToNodes (p, other_node, f); 429 continue; 430 } 431 432 // the winding is split 433 new_portal = AllocPortal (); 434 *new_portal = *p; 435 new_portal->winding = backwinding; 436 FreeWinding (p->winding); 437 p->winding = frontwinding; 438 439 if (side == 0) 440 { 441 AddPortalToNodes (p, f, other_node); 442 AddPortalToNodes (new_portal, b, other_node); 443 } 444 else 445 { 446 AddPortalToNodes (p, other_node, f); 447 AddPortalToNodes (new_portal, other_node, b); 448 } 449 } 450 451 node->portals = NULL; 452 } 453 454 455 /* 456 ================ 457 CalcNodeBounds 458 ================ 459 */ 460 void CalcNodeBounds (node_t *node) 461 { 462 portal_t *p; 463 int s; 464 int i; 465 466 // calc mins/maxs for both leafs and nodes 467 ClearBounds (node->mins, node->maxs); 468 for (p = node->portals ; p ; p = p->next[s]) 469 { 470 s = (p->nodes[1] == node); 471 for (i=0 ; i<p->winding->numpoints ; i++) 472 AddPointToBounds (p->winding->p[i], node->mins, node->maxs); 473 } 474 } 475 476 /* 477 ================== 478 MakeTreePortals_r 479 ================== 480 */ 481 void MakeTreePortals_r (node_t *node) 482 { 483 int i; 484 485 CalcNodeBounds (node); 486 if (node->mins[0] >= node->maxs[0]) 487 { 488 _printf ("WARNING: node without a volume\n"); 489 _printf("node has %d tiny portals\n", node->tinyportals); 490 _printf("node reference point %1.2f %1.2f %1.2f\n", node->referencepoint[0], 491 node->referencepoint[1], 492 node->referencepoint[2]); 493 } 494 495 for (i=0 ; i<3 ; i++) 496 { 497 if (node->mins[i] < MIN_WORLD_COORD || node->maxs[i] > MAX_WORLD_COORD) 498 { 499 _printf ("WARNING: node with unbounded volume\n"); 500 break; 501 } 502 } 503 if (node->planenum == PLANENUM_LEAF) 504 return; 505 506 MakeNodePortal (node); 507 SplitNodePortals (node); 508 509 MakeTreePortals_r (node->children[0]); 510 MakeTreePortals_r (node->children[1]); 511 } 512 513 /* 514 ================== 515 MakeTreePortals 516 ================== 517 */ 518 void MakeTreePortals (tree_t *tree) 519 { 520 qprintf( "----- MakeTreePortals -----\n"); 521 MakeHeadnodePortals (tree); 522 MakeTreePortals_r (tree->headnode); 523 qprintf("%6d tiny portals\n", c_tinyportals); 524 } 525 526 /* 527 ========================================================= 528 529 FLOOD ENTITIES 530 531 ========================================================= 532 */ 533 534 int c_floodedleafs; 535 536 /* 537 ============= 538 FloodPortals_r 539 ============= 540 */ 541 void FloodPortals_r (node_t *node, int dist) { 542 portal_t *p; 543 int s; 544 545 if ( node->occupied ) { 546 return; 547 } 548 549 if ( node->opaque ) { 550 return; 551 } 552 553 c_floodedleafs++; 554 node->occupied = dist; 555 556 for (p=node->portals ; p ; p = p->next[s]) { 557 s = (p->nodes[1] == node); 558 FloodPortals_r (p->nodes[!s], dist+1); 559 } 560 } 561 562 /* 563 ============= 564 PlaceOccupant 565 ============= 566 */ 567 qboolean PlaceOccupant (node_t *headnode, vec3_t origin, entity_t *occupant) 568 { 569 node_t *node; 570 vec_t d; 571 plane_t *plane; 572 573 // find the leaf to start in 574 node = headnode; 575 while (node->planenum != PLANENUM_LEAF) 576 { 577 plane = &mapplanes[node->planenum]; 578 d = DotProduct (origin, plane->normal) - plane->dist; 579 if (d >= 0) 580 node = node->children[0]; 581 else 582 node = node->children[1]; 583 } 584 585 if ( node->opaque ) 586 return qfalse; 587 node->occupant = occupant; 588 589 FloodPortals_r (node, 1); 590 591 return qtrue; 592 } 593 594 /* 595 ============= 596 FloodEntities 597 598 Marks all nodes that can be reached by entites 599 ============= 600 */ 601 qboolean FloodEntities( tree_t *tree ) { 602 int i; 603 vec3_t origin; 604 const char *cl; 605 qboolean inside; 606 node_t *headnode; 607 608 headnode = tree->headnode; 609 qprintf ("--- FloodEntities ---\n"); 610 inside = qfalse; 611 tree->outside_node.occupied = 0; 612 613 c_floodedleafs = 0; 614 for (i=1 ; i<num_entities ; i++) 615 { 616 GetVectorForKey (&entities[i], "origin", origin); 617 if (VectorCompare(origin, vec3_origin)) 618 continue; 619 620 cl = ValueForKey (&entities[i], "classname"); 621 622 origin[2] += 1; // so objects on floor are ok 623 624 if (PlaceOccupant (headnode, origin, &entities[i])) 625 inside = qtrue; 626 } 627 628 qprintf("%5i flooded leafs\n", c_floodedleafs ); 629 630 if (!inside) 631 { 632 qprintf ("no entities in open -- no filling\n"); 633 } 634 else if (tree->outside_node.occupied) 635 { 636 qprintf ("entity reached from outside -- no filling\n"); 637 } 638 639 return (qboolean)(inside && !tree->outside_node.occupied); 640 } 641 642 /* 643 ========================================================= 644 645 FLOOD AREAS 646 647 ========================================================= 648 */ 649 650 int c_areas; 651 652 /* 653 ============= 654 FloodAreas_r 655 ============= 656 */ 657 void FloodAreas_r (node_t *node) 658 { 659 portal_t *p; 660 int s; 661 bspbrush_t *b; 662 663 if ( node->areaportal ) { 664 // 665 if ( node->area == -1 ) { 666 node->area = c_areas; 667 } 668 // this node is part of an area portal brush 669 b = node->brushlist->original; 670 671 // if the current area has allready touched this 672 // portal, we are done 673 if (b->portalareas[0] == c_areas || b->portalareas[1] == c_areas) 674 return; 675 676 // note the current area as bounding the portal 677 if (b->portalareas[1] != -1) 678 { 679 _printf ("WARNING: areaportal brush %i touches > 2 areas\n", b->brushnum ); 680 return; 681 } 682 if (b->portalareas[0] != -1) { 683 b->portalareas[1] = c_areas; 684 } else { 685 b->portalareas[0] = c_areas; 686 } 687 688 return; 689 } 690 691 if (node->area != -1) { 692 return; // allready got it 693 } 694 if ( node->cluster == -1 ) { 695 return; 696 } 697 698 node->area = c_areas; 699 700 for (p=node->portals ; p ; p = p->next[s]) 701 { 702 s = (p->nodes[1] == node); 703 704 if ( !Portal_Passable(p) ) 705 continue; 706 707 FloodAreas_r (p->nodes[!s]); 708 } 709 } 710 711 712 /* 713 ============= 714 FindAreas_r 715 716 Just decend the tree, and for each node that hasn't had an 717 area set, flood fill out from there 718 ============= 719 */ 720 void FindAreas_r (node_t *node) 721 { 722 if (node->planenum != PLANENUM_LEAF) 723 { 724 FindAreas_r (node->children[0]); 725 FindAreas_r (node->children[1]); 726 return; 727 } 728 729 if (node->opaque) 730 return; 731 732 if (node->areaportal) 733 return; 734 735 if (node->area != -1) 736 return; // allready got it 737 738 FloodAreas_r (node); 739 c_areas++; 740 } 741 742 /* 743 ============= 744 CheckAreas_r 745 ============= 746 */ 747 void CheckAreas_r (node_t *node) 748 { 749 bspbrush_t *b; 750 751 if (node->planenum != PLANENUM_LEAF) 752 { 753 CheckAreas_r (node->children[0]); 754 CheckAreas_r (node->children[1]); 755 return; 756 } 757 758 if (node->opaque) 759 return; 760 761 if (node->cluster != -1) 762 if (node->area == -1) 763 _printf("WARNING: cluster %d has area set to -1\n", node->cluster); 764 if (node->areaportal) 765 { 766 b = node->brushlist->original; 767 768 // check if the areaportal touches two areas 769 if (b->portalareas[0] == -1 || b->portalareas[1] == -1) 770 _printf ("WARNING: areaportal brush %i doesn't touch two areas\n", b->brushnum); 771 } 772 } 773 774 /* 775 ============= 776 FloodAreas 777 778 Mark each leaf with an area, bounded by CONTENTS_AREAPORTAL 779 ============= 780 */ 781 void FloodAreas (tree_t *tree) 782 { 783 qprintf ("--- FloodAreas ---\n"); 784 FindAreas_r( tree->headnode ); 785 786 // check for areaportal brushes that don't touch two areas 787 CheckAreas_r( tree->headnode ); 788 789 qprintf ("%5i areas\n", c_areas); 790 } 791 792 //====================================================== 793 794 int c_outside; 795 int c_inside; 796 int c_solid; 797 798 void FillOutside_r (node_t *node) 799 { 800 if (node->planenum != PLANENUM_LEAF) 801 { 802 FillOutside_r (node->children[0]); 803 FillOutside_r (node->children[1]); 804 return; 805 } 806 807 // anything not reachable by an entity 808 // can be filled away 809 if (!node->occupied) { 810 if ( !node->opaque ) { 811 c_outside++; 812 node->opaque = qtrue; 813 } else { 814 c_solid++; 815 } 816 } else { 817 c_inside++; 818 } 819 820 } 821 822 /* 823 ============= 824 FillOutside 825 826 Fill all nodes that can't be reached by entities 827 ============= 828 */ 829 void FillOutside (node_t *headnode) 830 { 831 c_outside = 0; 832 c_inside = 0; 833 c_solid = 0; 834 qprintf ("--- FillOutside ---\n"); 835 FillOutside_r (headnode); 836 qprintf ("%5i solid leafs\n", c_solid); 837 qprintf ("%5i leafs filled\n", c_outside); 838 qprintf ("%5i inside leafs\n", c_inside); 839 } 840 841 842 //============================================================== 843