Pvs.cpp (34460B)
1 /* 2 =========================================================================== 3 4 Doom 3 BFG Edition GPL Source Code 5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 6 7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). 8 9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation, either version 3 of the License, or 12 (at your option) any later version. 13 14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful, 15 but WITHOUT ANY WARRANTY; without even the implied warranty of 16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 GNU General Public License for more details. 18 19 You should have received a copy of the GNU General Public License 20 along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>. 21 22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. 23 24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. 25 26 =========================================================================== 27 */ 28 29 #include "../idlib/precompiled.h" 30 #pragma hdrstop 31 32 #include "Game_local.h" 33 34 #define MAX_BOUNDS_AREAS 16 35 36 37 typedef struct pvsPassage_s { 38 byte * canSee; // bit set for all portals that can be seen through this passage 39 } pvsPassage_t; 40 41 42 typedef struct pvsPortal_s { 43 int areaNum; // area this portal leads to 44 idWinding * w; // winding goes counter clockwise seen from the area this portal is part of 45 idBounds bounds; // winding bounds 46 idPlane plane; // winding plane, normal points towards the area this portal leads to 47 pvsPassage_t * passages; // passages to portals in the area this portal leads to 48 bool done; // true if pvs is calculated for this portal 49 byte * vis; // PVS for this portal 50 byte * mightSee; // used during construction 51 } pvsPortal_t; 52 53 54 typedef struct pvsArea_s { 55 int numPortals; // number of portals in this area 56 idBounds bounds; // bounds of the whole area 57 pvsPortal_t ** portals; // array with pointers to the portals of this area 58 } pvsArea_t; 59 60 61 typedef struct pvsStack_s { 62 struct pvsStack_s * next; // next stack entry 63 byte * mightSee; // bit set for all portals that might be visible through this passage/portal stack 64 } pvsStack_t; 65 66 67 /* 68 ================ 69 idPVS::idPVS 70 ================ 71 */ 72 idPVS::idPVS() { 73 int i; 74 75 numAreas = 0; 76 numPortals = 0; 77 78 connectedAreas = NULL; 79 areaQueue = NULL; 80 areaPVS = NULL; 81 82 for ( i = 0; i < MAX_CURRENT_PVS; i++ ) { 83 currentPVS[i].handle.i = -1; 84 currentPVS[i].handle.h = 0; 85 currentPVS[i].pvs = NULL; 86 } 87 88 pvsAreas = NULL; 89 pvsPortals = NULL; 90 } 91 92 /* 93 ================ 94 idPVS::~idPVS 95 ================ 96 */ 97 idPVS::~idPVS() { 98 Shutdown(); 99 } 100 101 /* 102 ================ 103 idPVS::GetPortalCount 104 ================ 105 */ 106 int idPVS::GetPortalCount() const { 107 int i, na, np; 108 109 na = gameRenderWorld->NumAreas(); 110 np = 0; 111 for ( i = 0; i < na; i++ ) { 112 np += gameRenderWorld->NumPortalsInArea( i ); 113 } 114 return np; 115 } 116 117 /* 118 ================ 119 idPVS::CreatePVSData 120 ================ 121 */ 122 void idPVS::CreatePVSData() { 123 int i, j, n, cp; 124 exitPortal_t portal; 125 pvsArea_t *area; 126 pvsPortal_t *p, **portalPtrs; 127 128 if ( !numPortals ) { 129 return; 130 } 131 132 pvsPortals = new (TAG_PVS) pvsPortal_t[numPortals]; 133 pvsAreas = new (TAG_PVS) pvsArea_t[numAreas]; 134 memset( pvsAreas, 0, numAreas * sizeof( *pvsAreas ) ); 135 136 cp = 0; 137 portalPtrs = new (TAG_PVS) pvsPortal_t*[numPortals]; 138 139 for ( i = 0; i < numAreas; i++ ) { 140 141 area = &pvsAreas[i]; 142 area->bounds.Clear(); 143 area->portals = portalPtrs + cp; 144 145 n = gameRenderWorld->NumPortalsInArea( i ); 146 147 for ( j = 0; j < n; j++ ) { 148 149 portal = gameRenderWorld->GetPortal( i, j ); 150 151 p = &pvsPortals[cp++]; 152 // the winding goes counter clockwise seen from this area 153 p->w = portal.w->Copy(); 154 p->areaNum = portal.areas[1]; // area[1] is always the area the portal leads to 155 156 p->vis = new (TAG_PVS) byte[portalVisBytes]; 157 memset( p->vis, 0, portalVisBytes ); 158 p->mightSee = new (TAG_PVS) byte[portalVisBytes]; 159 memset( p->mightSee, 0, portalVisBytes ); 160 p->w->GetBounds( p->bounds ); 161 p->w->GetPlane( p->plane ); 162 // plane normal points to outside the area 163 p->plane = -p->plane; 164 // no PVS calculated for this portal yet 165 p->done = false; 166 167 area->portals[area->numPortals] = p; 168 area->numPortals++; 169 170 area->bounds += p->bounds; 171 } 172 } 173 } 174 175 /* 176 ================ 177 idPVS::DestroyPVSData 178 ================ 179 */ 180 void idPVS::DestroyPVSData() { 181 int i; 182 183 if ( !pvsAreas ) { 184 return; 185 } 186 187 // delete portal pointer array 188 delete[] pvsAreas[0].portals; 189 190 // delete all areas 191 delete[] pvsAreas; 192 pvsAreas = NULL; 193 194 // delete portal data 195 for ( i = 0; i < numPortals; i++ ) { 196 delete[] pvsPortals[i].vis; 197 delete[] pvsPortals[i].mightSee; 198 delete pvsPortals[i].w; 199 } 200 201 // delete portals 202 delete[] pvsPortals; 203 pvsPortals = NULL; 204 } 205 206 /* 207 ================ 208 idPVS::FloodFrontPortalPVS_r 209 ================ 210 */ 211 void idPVS::FloodFrontPortalPVS_r( pvsPortal_t *portal, int areaNum ) const { 212 int i, n; 213 pvsArea_t *area; 214 pvsPortal_t *p; 215 216 area = &pvsAreas[ areaNum ]; 217 218 for ( i = 0; i < area->numPortals; i++ ) { 219 p = area->portals[i]; 220 n = p - pvsPortals; 221 // don't flood through if this portal is not at the front 222 if ( !( portal->mightSee[ n>>3 ] & (1 << (n&7)) ) ) { 223 continue; 224 } 225 // don't flood through if already visited this portal 226 if ( portal->vis[ n>>3 ] & (1 << (n&7)) ) { 227 continue; 228 } 229 // this portal might be visible 230 portal->vis[ n>>3 ] |= (1 << (n&7)); 231 // flood through the portal 232 FloodFrontPortalPVS_r( portal, p->areaNum ); 233 } 234 } 235 236 /* 237 ================ 238 idPVS::FrontPortalPVS 239 ================ 240 */ 241 void idPVS::FrontPortalPVS() const { 242 int i, j, k, n, p, side1, side2, areaSide; 243 pvsPortal_t *p1, *p2; 244 pvsArea_t *area; 245 246 for ( i = 0; i < numPortals; i++ ) { 247 p1 = &pvsPortals[i]; 248 249 for ( j = 0; j < numAreas; j++ ) { 250 251 area = &pvsAreas[j]; 252 253 areaSide = side1 = area->bounds.PlaneSide( p1->plane ); 254 255 // if the whole area is at the back side of the portal 256 if ( areaSide == PLANESIDE_BACK ) { 257 continue; 258 } 259 260 for ( p = 0; p < area->numPortals; p++ ) { 261 262 p2 = area->portals[p]; 263 264 // if we the whole area is not at the front we need to check 265 if ( areaSide != PLANESIDE_FRONT ) { 266 // if the second portal is completely at the back side of the first portal 267 side1 = p2->bounds.PlaneSide( p1->plane ); 268 if ( side1 == PLANESIDE_BACK ) { 269 continue; 270 } 271 } 272 273 // if the first portal is completely at the front of the second portal 274 side2 = p1->bounds.PlaneSide( p2->plane ); 275 if ( side2 == PLANESIDE_FRONT ) { 276 continue; 277 } 278 279 // if the second portal is not completely at the front of the first portal 280 if ( side1 != PLANESIDE_FRONT ) { 281 // more accurate check 282 for ( k = 0; k < p2->w->GetNumPoints(); k++ ) { 283 // if more than an epsilon at the front side 284 if ( p1->plane.Side( (*p2->w)[k].ToVec3(), ON_EPSILON ) == PLANESIDE_FRONT ) { 285 break; 286 } 287 } 288 if ( k >= p2->w->GetNumPoints() ) { 289 continue; // second portal is at the back of the first portal 290 } 291 } 292 293 // if the first portal is not completely at the back side of the second portal 294 if ( side2 != PLANESIDE_BACK ) { 295 // more accurate check 296 for ( k = 0; k < p1->w->GetNumPoints(); k++ ) { 297 // if more than an epsilon at the back side 298 if ( p2->plane.Side( (*p1->w)[k].ToVec3(), ON_EPSILON ) == PLANESIDE_BACK ) { 299 break; 300 } 301 } 302 if ( k >= p1->w->GetNumPoints() ) { 303 continue; // first portal is at the front of the second portal 304 } 305 } 306 307 // the portal might be visible at the front 308 n = p2 - pvsPortals; 309 p1->mightSee[ n >> 3 ] |= 1 << (n&7); 310 } 311 } 312 } 313 314 // flood the front portal pvs for all portals 315 for ( i = 0; i < numPortals; i++ ) { 316 p1 = &pvsPortals[i]; 317 FloodFrontPortalPVS_r( p1, p1->areaNum ); 318 } 319 } 320 321 /* 322 =============== 323 idPVS::FloodPassagePVS_r 324 =============== 325 */ 326 pvsStack_t *idPVS::FloodPassagePVS_r( pvsPortal_t *source, const pvsPortal_t *portal, pvsStack_t *prevStack ) const { 327 int i, j, n, m; 328 pvsPortal_t *p; 329 pvsArea_t *area; 330 pvsStack_t *stack; 331 pvsPassage_t *passage; 332 long *sourceVis, *passageVis, *portalVis, *mightSee, *prevMightSee, more; 333 334 area = &pvsAreas[portal->areaNum]; 335 336 stack = prevStack->next; 337 // if no next stack entry allocated 338 if ( !stack ) { 339 stack = reinterpret_cast<pvsStack_t*>(new byte[sizeof(pvsStack_t) + portalVisBytes]); 340 stack->mightSee = (reinterpret_cast<byte *>(stack)) + sizeof(pvsStack_t); 341 stack->next = NULL; 342 prevStack->next = stack; 343 } 344 345 // check all portals for flooding into other areas 346 for ( i = 0; i < area->numPortals; i++ ) { 347 348 passage = &portal->passages[i]; 349 350 // if this passage is completely empty 351 if ( !passage->canSee ) { 352 continue; 353 } 354 355 p = area->portals[i]; 356 n = p - pvsPortals; 357 358 // if this portal cannot be seen through our current portal/passage stack 359 if ( !( prevStack->mightSee[n >> 3] & (1 << (n & 7)) ) ) { 360 continue; 361 } 362 363 // mark the portal as visible 364 source->vis[n >> 3] |= (1 << (n & 7)); 365 366 // get pointers to vis data 367 prevMightSee = reinterpret_cast<long *>(prevStack->mightSee); 368 passageVis = reinterpret_cast<long *>(passage->canSee); 369 sourceVis = reinterpret_cast<long *>(source->vis); 370 mightSee = reinterpret_cast<long *>(stack->mightSee); 371 372 more = 0; 373 // use the portal PVS if it has been calculated 374 if ( p->done ) { 375 portalVis = reinterpret_cast<long *>(p->vis); 376 for ( j = 0; j < portalVisLongs; j++ ) { 377 // get new PVS which is decreased by going through this passage 378 m = *prevMightSee++ & *passageVis++ & *portalVis++; 379 // check if anything might be visible through this passage that wasn't yet visible 380 more |= (m & ~(*sourceVis++)); 381 // store new PVS 382 *mightSee++ = m; 383 } 384 } 385 else { 386 // the p->mightSee is implicitely stored in the passageVis 387 for ( j = 0; j < portalVisLongs; j++ ) { 388 // get new PVS which is decreased by going through this passage 389 m = *prevMightSee++ & *passageVis++; 390 // check if anything might be visible through this passage that wasn't yet visible 391 more |= (m & ~(*sourceVis++)); 392 // store new PVS 393 *mightSee++ = m; 394 } 395 } 396 397 // if nothing more can be seen 398 if ( !more ) { 399 continue; 400 } 401 402 // go through the portal 403 stack->next = FloodPassagePVS_r( source, p, stack ); 404 } 405 406 return stack; 407 } 408 409 /* 410 =============== 411 idPVS::PassagePVS 412 =============== 413 */ 414 void idPVS::PassagePVS() const { 415 int i; 416 pvsPortal_t *source; 417 pvsStack_t *stack, *s; 418 419 // create the passages 420 CreatePassages(); 421 422 // allocate first stack entry 423 stack = reinterpret_cast<pvsStack_t*>(new byte[sizeof(pvsStack_t) + portalVisBytes]); 424 stack->mightSee = (reinterpret_cast<byte *>(stack)) + sizeof(pvsStack_t); 425 stack->next = NULL; 426 427 // calculate portal PVS by flooding through the passages 428 for ( i = 0; i < numPortals; i++ ) { 429 source = &pvsPortals[i]; 430 memset( source->vis, 0, portalVisBytes ); 431 memcpy( stack->mightSee, source->mightSee, portalVisBytes ); 432 FloodPassagePVS_r( source, source, stack ); 433 source->done = true; 434 } 435 436 // free the allocated stack 437 for ( s = stack; s; s = stack ) { 438 stack = stack->next; 439 delete[] s; 440 } 441 442 // destroy the passages 443 DestroyPassages(); 444 } 445 446 /* 447 =============== 448 idPVS::AddPassageBoundaries 449 =============== 450 */ 451 void idPVS::AddPassageBoundaries( const idWinding &source, const idWinding &pass, bool flipClip, idPlane *bounds, int &numBounds, int maxBounds ) const { 452 int i, j, k, l; 453 idVec3 v1, v2, normal; 454 float d, dist; 455 bool flipTest, front; 456 idPlane plane; 457 458 459 // check all combinations 460 for ( i = 0; i < source.GetNumPoints(); i++ ) { 461 462 l = (i + 1) % source.GetNumPoints(); 463 v1 = source[l].ToVec3() - source[i].ToVec3(); 464 465 // find a vertex of pass that makes a plane that puts all of the 466 // vertices of pass on the front side and all of the vertices of 467 // source on the back side 468 for ( j = 0; j < pass.GetNumPoints(); j++ ) { 469 470 v2 = pass[j].ToVec3() - source[i].ToVec3(); 471 472 normal = v1.Cross( v2 ); 473 if ( normal.Normalize() < 0.01f ) { 474 continue; 475 } 476 dist = normal * pass[j].ToVec3(); 477 478 // 479 // find out which side of the generated seperating plane has the 480 // source portal 481 // 482 flipTest = false; 483 for ( k = 0; k < source.GetNumPoints(); k++ ) { 484 if ( k == i || k == l ) { 485 continue; 486 } 487 d = source[k].ToVec3() * normal - dist; 488 if ( d < -ON_EPSILON ) { 489 // source is on the negative side, so we want all 490 // pass and target on the positive side 491 flipTest = false; 492 break; 493 } 494 else if ( d > ON_EPSILON ) { 495 // source is on the positive side, so we want all 496 // pass and target on the negative side 497 flipTest = true; 498 break; 499 } 500 } 501 if ( k == source.GetNumPoints() ) { 502 continue; // planar with source portal 503 } 504 505 // flip the normal if the source portal is backwards 506 if (flipTest) { 507 normal = -normal; 508 dist = -dist; 509 } 510 511 // if all of the pass portal points are now on the positive side, 512 // this is the seperating plane 513 front = false; 514 for ( k = 0; k < pass.GetNumPoints(); k++ ) { 515 if ( k == j ) { 516 continue; 517 } 518 d = pass[k].ToVec3() * normal - dist; 519 if ( d < -ON_EPSILON ) { 520 break; 521 } 522 else if ( d > ON_EPSILON ) { 523 front = true; 524 } 525 } 526 if ( k < pass.GetNumPoints() ) { 527 continue; // points on negative side, not a seperating plane 528 } 529 if ( !front ) { 530 continue; // planar with seperating plane 531 } 532 533 // flip the normal if we want the back side 534 if ( flipClip ) { 535 plane.SetNormal( -normal ); 536 plane.SetDist( -dist ); 537 } 538 else { 539 plane.SetNormal( normal ); 540 plane.SetDist( dist ); 541 } 542 543 // check if the plane is already a passage boundary 544 for ( k = 0; k < numBounds; k++ ) { 545 if ( plane.Compare( bounds[k], 0.001f, 0.01f ) ) { 546 break; 547 } 548 } 549 if ( k < numBounds ) { 550 break; 551 } 552 553 if ( numBounds >= maxBounds ) { 554 gameLocal.Warning( "max passage boundaries." ); 555 break; 556 } 557 bounds[numBounds] = plane; 558 numBounds++; 559 break; 560 } 561 } 562 } 563 564 /* 565 ================ 566 idPVS::CreatePassages 567 ================ 568 */ 569 #define MAX_PASSAGE_BOUNDS 128 570 571 void idPVS::CreatePassages() const { 572 int i, j, l, n, numBounds, front, passageMemory, byteNum, bitNum; 573 int sides[MAX_PASSAGE_BOUNDS]; 574 idPlane passageBounds[MAX_PASSAGE_BOUNDS]; 575 pvsPortal_t *source, *target, *p; 576 pvsArea_t *area; 577 pvsPassage_t *passage; 578 idFixedWinding winding; 579 byte canSee, mightSee, bit; 580 581 passageMemory = 0; 582 for ( i = 0; i < numPortals; i++ ) { 583 source = &pvsPortals[i]; 584 area = &pvsAreas[source->areaNum]; 585 586 source->passages = new (TAG_PVS) pvsPassage_t[area->numPortals]; 587 588 for ( j = 0; j < area->numPortals; j++ ) { 589 target = area->portals[j]; 590 n = target - pvsPortals; 591 592 passage = &source->passages[j]; 593 594 // if the source portal cannot see this portal 595 if ( !( source->mightSee[ n>>3 ] & (1 << (n&7)) ) ) { 596 // not all portals in the area have to be visible because areas are not necesarily convex 597 // also no passage has to be created for the portal which is the opposite of the source 598 passage->canSee = NULL; 599 continue; 600 } 601 602 passage->canSee = new (TAG_PVS) byte[portalVisBytes]; 603 passageMemory += portalVisBytes; 604 605 // boundary plane normals point inwards 606 numBounds = 0; 607 AddPassageBoundaries( *(source->w), *(target->w), false, passageBounds, numBounds, MAX_PASSAGE_BOUNDS ); 608 AddPassageBoundaries( *(target->w), *(source->w), true, passageBounds, numBounds, MAX_PASSAGE_BOUNDS ); 609 610 // get all portals visible through this passage 611 for ( byteNum = 0; byteNum < portalVisBytes; byteNum++) { 612 613 canSee = 0; 614 mightSee = source->mightSee[byteNum] & target->mightSee[byteNum]; 615 616 // go through eight portals at a time to speed things up 617 for ( bitNum = 0; bitNum < 8; bitNum++ ) { 618 619 bit = 1 << bitNum; 620 621 if ( !( mightSee & bit ) ) { 622 continue; 623 } 624 625 p = &pvsPortals[(byteNum << 3) + bitNum]; 626 627 if ( p->areaNum == source->areaNum ) { 628 continue; 629 } 630 631 for ( front = 0, l = 0; l < numBounds; l++ ) { 632 sides[l] = p->bounds.PlaneSide( passageBounds[l] ); 633 // if completely at the back of the passage bounding plane 634 if ( sides[l] == PLANESIDE_BACK ) { 635 break; 636 } 637 // if completely at the front 638 if ( sides[l] == PLANESIDE_FRONT ) { 639 front++; 640 } 641 } 642 // if completely outside the passage 643 if ( l < numBounds ) { 644 continue; 645 } 646 647 // if not at the front of all bounding planes and thus not completely inside the passage 648 if ( front != numBounds ) { 649 650 winding = *p->w; 651 652 for ( l = 0; l < numBounds; l++ ) { 653 // only clip if the winding possibly crosses this plane 654 if ( sides[l] != PLANESIDE_CROSS ) { 655 continue; 656 } 657 // clip away the part at the back of the bounding plane 658 winding.ClipInPlace( passageBounds[l] ); 659 // if completely clipped away 660 if ( !winding.GetNumPoints() ) { 661 break; 662 } 663 } 664 // if completely outside the passage 665 if ( l < numBounds ) { 666 continue; 667 } 668 } 669 670 canSee |= bit; 671 } 672 673 // store results of all eight portals 674 passage->canSee[byteNum] = canSee; 675 } 676 677 // can always see the target portal 678 passage->canSee[n >> 3] |= (1 << (n&7)); 679 } 680 } 681 if ( passageMemory < 1024 ) { 682 gameLocal.Printf( "%5d bytes passage memory used to build PVS\n", passageMemory ); 683 } 684 else { 685 gameLocal.Printf( "%5d KB passage memory used to build PVS\n", passageMemory>>10 ); 686 } 687 } 688 689 /* 690 ================ 691 idPVS::DestroyPassages 692 ================ 693 */ 694 void idPVS::DestroyPassages() const { 695 int i, j; 696 pvsPortal_t *p; 697 pvsArea_t *area; 698 699 for ( i = 0; i < numPortals; i++ ) { 700 p = &pvsPortals[i]; 701 area = &pvsAreas[p->areaNum]; 702 for ( j = 0; j < area->numPortals; j++ ) { 703 if ( p->passages[j].canSee ) { 704 delete[] p->passages[j].canSee; 705 } 706 } 707 delete[] p->passages; 708 } 709 } 710 711 /* 712 ================ 713 idPVS::CopyPortalPVSToMightSee 714 ================ 715 */ 716 void idPVS::CopyPortalPVSToMightSee() const { 717 int i; 718 pvsPortal_t *p; 719 720 for ( i = 0; i < numPortals; i++ ) { 721 p = &pvsPortals[i]; 722 memcpy( p->mightSee, p->vis, portalVisBytes ); 723 } 724 } 725 726 /* 727 ================ 728 idPVS::AreaPVSFromPortalPVS 729 ================ 730 */ 731 int idPVS::AreaPVSFromPortalPVS() const { 732 int i, j, k, areaNum, totalVisibleAreas; 733 long *p1, *p2; 734 byte *pvs, *portalPVS; 735 pvsArea_t *area; 736 737 totalVisibleAreas = 0; 738 739 if ( !numPortals ) { 740 return totalVisibleAreas; 741 } 742 743 memset( areaPVS, 0, numAreas * areaVisBytes ); 744 745 for ( i = 0; i < numAreas; i++ ) { 746 area = &pvsAreas[i]; 747 pvs = areaPVS + i * areaVisBytes; 748 749 // the area is visible to itself 750 pvs[ i >> 3 ] |= 1 << (i & 7); 751 752 if ( !area->numPortals ) { 753 continue; 754 } 755 756 // store the PVS of all portals in this area at the first portal 757 for ( j = 1; j < area->numPortals; j++ ) { 758 p1 = reinterpret_cast<long *>(area->portals[0]->vis); 759 p2 = reinterpret_cast<long *>(area->portals[j]->vis); 760 for ( k = 0; k < portalVisLongs; k++ ) { 761 *p1++ |= *p2++; 762 } 763 } 764 765 // the portals of this area are always visible 766 for ( j = 0; j < area->numPortals; j++ ) { 767 k = area->portals[j] - pvsPortals; 768 area->portals[0]->vis[ k >> 3 ] |= 1 << (k & 7); 769 } 770 771 // set all areas to visible that can be seen from the portals of this area 772 portalPVS = area->portals[0]->vis; 773 for ( j = 0; j < numPortals; j++ ) { 774 // if this portal is visible 775 if ( portalPVS[j>>3] & (1 << (j&7)) ) { 776 areaNum = pvsPortals[j].areaNum; 777 pvs[ areaNum >> 3 ] |= 1 << (areaNum & 7); 778 } 779 } 780 781 // count the number of visible areas 782 for ( j = 0; j < numAreas; j++ ) { 783 if ( pvs[j>>3] & (1 << (j&7)) ) { 784 totalVisibleAreas++; 785 } 786 } 787 } 788 return totalVisibleAreas; 789 } 790 791 /* 792 ================ 793 idPVS::Init 794 ================ 795 */ 796 void idPVS::Init() { 797 int totalVisibleAreas; 798 799 Shutdown(); 800 801 numAreas = gameRenderWorld->NumAreas(); 802 if ( numAreas <= 0 ) { 803 return; 804 } 805 806 connectedAreas = new (TAG_PVS) bool[numAreas]; 807 areaQueue = new (TAG_PVS) int[numAreas]; 808 809 areaVisBytes = ( ((numAreas+31)&~31) >> 3); 810 areaVisLongs = areaVisBytes/sizeof(long); 811 812 areaPVS = new (TAG_PVS) byte[numAreas * areaVisBytes]; 813 memset( areaPVS, 0xFF, numAreas * areaVisBytes ); 814 815 numPortals = GetPortalCount(); 816 817 portalVisBytes = ( ((numPortals+31)&~31) >> 3); 818 portalVisLongs = portalVisBytes/sizeof(long); 819 820 for ( int i = 0; i < MAX_CURRENT_PVS; i++ ) { 821 currentPVS[i].handle.i = -1; 822 currentPVS[i].handle.h = 0; 823 currentPVS[i].pvs = new (TAG_PVS) byte[areaVisBytes]; 824 memset( currentPVS[i].pvs, 0, areaVisBytes ); 825 } 826 827 idTimer timer; 828 timer.Start(); 829 830 CreatePVSData(); 831 832 FrontPortalPVS(); 833 834 CopyPortalPVSToMightSee(); 835 836 PassagePVS(); 837 838 totalVisibleAreas = AreaPVSFromPortalPVS(); 839 840 DestroyPVSData(); 841 842 timer.Stop(); 843 844 gameLocal.Printf( "%5.0f msec to calculate PVS\n", timer.Milliseconds() ); 845 gameLocal.Printf( "%5d areas\n", numAreas ); 846 gameLocal.Printf( "%5d portals\n", numPortals ); 847 gameLocal.Printf( "%5d areas visible on average\n", totalVisibleAreas / numAreas ); 848 if ( numAreas * areaVisBytes < 1024 ) { 849 gameLocal.Printf( "%5d bytes PVS data\n", numAreas * areaVisBytes ); 850 } 851 else { 852 gameLocal.Printf( "%5d KB PVS data\n", (numAreas * areaVisBytes) >> 10 ); 853 } 854 } 855 856 /* 857 ================ 858 idPVS::Shutdown 859 ================ 860 */ 861 void idPVS::Shutdown() { 862 if ( connectedAreas ) { 863 delete connectedAreas; 864 connectedAreas = NULL; 865 } 866 if ( areaQueue ) { 867 delete areaQueue; 868 areaQueue = NULL; 869 } 870 if ( areaPVS ) { 871 delete areaPVS; 872 areaPVS = NULL; 873 } 874 for ( int i = 0; i < MAX_CURRENT_PVS; i++ ) { 875 delete currentPVS[i].pvs; 876 currentPVS[i].pvs = NULL; 877 } 878 } 879 880 /* 881 ================ 882 idPVS::GetConnectedAreas 883 884 assumes the 'areas' array is initialized to false 885 ================ 886 */ 887 void idPVS::GetConnectedAreas( int srcArea, bool *areas ) const { 888 int curArea, nextArea; 889 int queueStart, queueEnd; 890 int i, n; 891 exitPortal_t portal; 892 893 queueStart = -1; 894 queueEnd = 0; 895 areas[srcArea] = true; 896 897 for ( curArea = srcArea; queueStart < queueEnd; curArea = areaQueue[++queueStart] ) { 898 899 n = gameRenderWorld->NumPortalsInArea( curArea ); 900 901 for ( i = 0; i < n; i++ ) { 902 portal = gameRenderWorld->GetPortal( curArea, i ); 903 904 if ( portal.blockingBits & PS_BLOCK_VIEW ) { 905 continue; 906 } 907 908 // area[1] is always the area the portal leads to 909 nextArea = portal.areas[1]; 910 911 // if already visited this area 912 if ( areas[nextArea] ) { 913 continue; 914 } 915 916 // add area to queue 917 areaQueue[queueEnd++] = nextArea; 918 areas[nextArea] = true; 919 } 920 } 921 } 922 923 /* 924 ================ 925 idPVS::GetPVSArea 926 ================ 927 */ 928 int idPVS::GetPVSArea( const idVec3 &point ) const { 929 return gameRenderWorld->PointInArea( point ); 930 } 931 932 /* 933 ================ 934 idPVS::GetPVSAreas 935 ================ 936 */ 937 int idPVS::GetPVSAreas( const idBounds &bounds, int *areas, int maxAreas ) const { 938 return gameRenderWorld->BoundsInAreas( bounds, areas, maxAreas ); 939 } 940 941 /* 942 ================ 943 idPVS::SetupCurrentPVS 944 ================ 945 */ 946 pvsHandle_t idPVS::SetupCurrentPVS( const idVec3 &source, const pvsType_t type ) const { 947 int sourceArea; 948 949 sourceArea = gameRenderWorld->PointInArea( source ); 950 951 return SetupCurrentPVS( sourceArea, type ); 952 } 953 954 /* 955 ================ 956 idPVS::SetupCurrentPVS 957 ================ 958 */ 959 pvsHandle_t idPVS::SetupCurrentPVS( const idBounds &source, const pvsType_t type ) const { 960 int numSourceAreas, sourceAreas[MAX_BOUNDS_AREAS]; 961 962 numSourceAreas = gameRenderWorld->BoundsInAreas( source, sourceAreas, MAX_BOUNDS_AREAS ); 963 964 return SetupCurrentPVS( sourceAreas, numSourceAreas, type ); 965 } 966 967 /* 968 ================ 969 idPVS::SetupCurrentPVS 970 ================ 971 */ 972 pvsHandle_t idPVS::SetupCurrentPVS( const int sourceArea, const pvsType_t type ) const { 973 int i; 974 pvsHandle_t handle; 975 976 handle = AllocCurrentPVS( *reinterpret_cast<const unsigned int *>(&sourceArea) ); 977 978 if ( sourceArea < 0 || sourceArea >= numAreas ) { 979 memset( currentPVS[handle.i].pvs, 0, areaVisBytes ); 980 return handle; 981 } 982 983 if ( type != PVS_CONNECTED_AREAS ) { 984 memcpy( currentPVS[handle.i].pvs, areaPVS + sourceArea * areaVisBytes, areaVisBytes ); 985 } else { 986 memset( currentPVS[handle.i].pvs, -1, areaVisBytes ); 987 } 988 989 if ( type == PVS_ALL_PORTALS_OPEN ) { 990 return handle; 991 } 992 993 memset( connectedAreas, 0, numAreas * sizeof( *connectedAreas ) ); 994 995 GetConnectedAreas( sourceArea, connectedAreas ); 996 997 for ( i = 0; i < numAreas; i++ ) { 998 if ( !connectedAreas[i] ) { 999 currentPVS[handle.i].pvs[i>>3] &= ~(1 << (i&7)); 1000 } 1001 } 1002 1003 return handle; 1004 } 1005 1006 /* 1007 ================ 1008 idPVS::SetupCurrentPVS 1009 ================ 1010 */ 1011 pvsHandle_t idPVS::SetupCurrentPVS( const int *sourceAreas, const int numSourceAreas, const pvsType_t type ) const { 1012 int i, j; 1013 unsigned int h; 1014 long *vis, *pvs; 1015 pvsHandle_t handle; 1016 1017 h = 0; 1018 for ( i = 0; i < numSourceAreas; i++ ) { 1019 h ^= *reinterpret_cast<const unsigned int *>(&sourceAreas[i]); 1020 } 1021 handle = AllocCurrentPVS( h ); 1022 1023 if ( !numSourceAreas || sourceAreas[0] < 0 || sourceAreas[0] >= numAreas) { 1024 memset( currentPVS[handle.i].pvs, 0, areaVisBytes ); 1025 return handle; 1026 } 1027 1028 if ( type != PVS_CONNECTED_AREAS ) { 1029 // merge PVS of all areas the source is in 1030 memcpy( currentPVS[handle.i].pvs, areaPVS + sourceAreas[0] * areaVisBytes, areaVisBytes ); 1031 for ( i = 1; i < numSourceAreas; i++ ) { 1032 1033 assert( sourceAreas[i] >= 0 && sourceAreas[i] < numAreas ); 1034 1035 vis = reinterpret_cast<long*>(areaPVS + sourceAreas[i] * areaVisBytes); 1036 pvs = reinterpret_cast<long*>(currentPVS[handle.i].pvs); 1037 for ( j = 0; j < areaVisLongs; j++ ) { 1038 *pvs++ |= *vis++; 1039 } 1040 } 1041 } else { 1042 memset( currentPVS[handle.i].pvs, -1, areaVisBytes ); 1043 } 1044 1045 if ( type == PVS_ALL_PORTALS_OPEN ) { 1046 return handle; 1047 } 1048 1049 memset( connectedAreas, 0, numAreas * sizeof( *connectedAreas ) ); 1050 1051 // get all areas connected to any of the source areas 1052 for ( i = 0; i < numSourceAreas; i++ ) { 1053 if ( !connectedAreas[sourceAreas[i]] ) { 1054 GetConnectedAreas( sourceAreas[i], connectedAreas ); 1055 } 1056 } 1057 1058 // remove unconnected areas from the PVS 1059 for ( i = 0; i < numAreas; i++ ) { 1060 if ( !connectedAreas[i] ) { 1061 currentPVS[handle.i].pvs[i>>3] &= ~(1 << (i&7)); 1062 } 1063 } 1064 1065 return handle; 1066 } 1067 1068 /* 1069 ================ 1070 idPVS::MergeCurrentPVS 1071 ================ 1072 */ 1073 pvsHandle_t idPVS::MergeCurrentPVS( pvsHandle_t pvs1, pvsHandle_t pvs2 ) const { 1074 int i; 1075 long *pvs1Ptr, *pvs2Ptr, *ptr; 1076 pvsHandle_t handle = { 0 }; 1077 1078 if ( pvs1.i < 0 || pvs1.i >= MAX_CURRENT_PVS || pvs1.h != currentPVS[pvs1.i].handle.h || 1079 pvs2.i < 0 || pvs2.i >= MAX_CURRENT_PVS || pvs2.h != currentPVS[pvs2.i].handle.h ) { 1080 gameLocal.Error( "idPVS::MergeCurrentPVS: invalid handle" ); 1081 return handle; 1082 } 1083 1084 handle = AllocCurrentPVS( pvs1.h ^ pvs2.h ); 1085 1086 ptr = reinterpret_cast<long*>(currentPVS[handle.i].pvs); 1087 pvs1Ptr = reinterpret_cast<long*>(currentPVS[pvs1.i].pvs); 1088 pvs2Ptr = reinterpret_cast<long*>(currentPVS[pvs2.i].pvs); 1089 1090 for ( i = 0; i < areaVisLongs; i++ ) { 1091 *ptr++ = *pvs1Ptr++ | *pvs2Ptr++; 1092 } 1093 1094 return handle; 1095 } 1096 1097 /* 1098 ================ 1099 idPVS::AllocCurrentPVS 1100 ================ 1101 */ 1102 pvsHandle_t idPVS::AllocCurrentPVS( unsigned int h ) const { 1103 int i; 1104 pvsHandle_t handle; 1105 1106 for ( i = 0; i < MAX_CURRENT_PVS; i++ ) { 1107 if ( currentPVS[i].handle.i == -1 ) { 1108 currentPVS[i].handle.i = i; 1109 currentPVS[i].handle.h = h; 1110 return currentPVS[i].handle; 1111 } 1112 } 1113 1114 gameLocal.Error( "idPVS::AllocCurrentPVS: no free PVS left" ); 1115 1116 handle.i = -1; 1117 handle.h = 0; 1118 return handle; 1119 } 1120 1121 /* 1122 ================ 1123 idPVS::FreeCurrentPVS 1124 ================ 1125 */ 1126 void idPVS::FreeCurrentPVS( pvsHandle_t handle ) const { 1127 if ( handle.i < 0 || handle.i >= MAX_CURRENT_PVS || handle.h != currentPVS[handle.i].handle.h ) { 1128 gameLocal.Error( "idPVS::FreeCurrentPVS: invalid handle" ); 1129 return; 1130 } 1131 currentPVS[handle.i].handle.i = -1; 1132 } 1133 1134 /* 1135 ================ 1136 idPVS::InCurrentPVS 1137 ================ 1138 */ 1139 bool idPVS::InCurrentPVS( const pvsHandle_t handle, const idVec3 &target ) const { 1140 int targetArea; 1141 1142 if ( handle.i < 0 || handle.i >= MAX_CURRENT_PVS || 1143 handle.h != currentPVS[handle.i].handle.h ) { 1144 gameLocal.Warning( "idPVS::InCurrentPVS: invalid handle" ); 1145 return false; 1146 } 1147 1148 targetArea = gameRenderWorld->PointInArea( target ); 1149 1150 if ( targetArea == -1 ) { 1151 return false; 1152 } 1153 1154 return ( ( currentPVS[handle.i].pvs[targetArea>>3] & (1 << (targetArea&7)) ) != 0 ); 1155 } 1156 1157 /* 1158 ================ 1159 idPVS::InCurrentPVS 1160 ================ 1161 */ 1162 bool idPVS::InCurrentPVS( const pvsHandle_t handle, const idBounds &target ) const { 1163 int i, numTargetAreas, targetAreas[MAX_BOUNDS_AREAS]; 1164 1165 if ( handle.i < 0 || handle.i >= MAX_CURRENT_PVS || 1166 handle.h != currentPVS[handle.i].handle.h ) { 1167 gameLocal.Warning( "idPVS::InCurrentPVS: invalid handle" ); 1168 return false; 1169 } 1170 1171 numTargetAreas = gameRenderWorld->BoundsInAreas( target, targetAreas, MAX_BOUNDS_AREAS ); 1172 1173 for ( i = 0; i < numTargetAreas; i++ ) { 1174 if ( currentPVS[handle.i].pvs[targetAreas[i]>>3] & (1 << (targetAreas[i]&7)) ) { 1175 return true; 1176 } 1177 } 1178 return false; 1179 } 1180 1181 /* 1182 ================ 1183 idPVS::InCurrentPVS 1184 ================ 1185 */ 1186 bool idPVS::InCurrentPVS( const pvsHandle_t handle, const int targetArea ) const { 1187 1188 if ( handle.i < 0 || handle.i >= MAX_CURRENT_PVS || 1189 handle.h != currentPVS[handle.i].handle.h ) { 1190 gameLocal.Warning( "idPVS::InCurrentPVS: invalid handle" ); 1191 return false; 1192 } 1193 1194 if ( targetArea < 0 || targetArea >= numAreas ) { 1195 return false; 1196 } 1197 1198 return ( ( currentPVS[handle.i].pvs[targetArea>>3] & (1 << (targetArea&7)) ) != 0 ); 1199 } 1200 1201 /* 1202 ================ 1203 idPVS::InCurrentPVS 1204 ================ 1205 */ 1206 bool idPVS::InCurrentPVS( const pvsHandle_t handle, const int *targetAreas, int numTargetAreas ) const { 1207 int i; 1208 1209 if ( handle.i < 0 || handle.i >= MAX_CURRENT_PVS || 1210 handle.h != currentPVS[handle.i].handle.h ) { 1211 gameLocal.Warning( "idPVS::InCurrentPVS: invalid handle" ); 1212 return false; 1213 } 1214 1215 for ( i = 0; i < numTargetAreas; i++ ) { 1216 if ( targetAreas[i] < 0 || targetAreas[i] >= numAreas ) { 1217 continue; 1218 } 1219 if ( currentPVS[handle.i].pvs[targetAreas[i]>>3] & (1 << (targetAreas[i]&7)) ) { 1220 return true; 1221 } 1222 } 1223 return false; 1224 } 1225 1226 /* 1227 ================ 1228 idPVS::DrawPVS 1229 ================ 1230 */ 1231 void idPVS::DrawPVS( const idVec3 &source, const pvsType_t type ) const { 1232 int i, j, k, numPoints, n, sourceArea; 1233 exitPortal_t portal; 1234 idPlane plane; 1235 idVec3 offset; 1236 idVec4 *color; 1237 pvsHandle_t handle; 1238 1239 sourceArea = gameRenderWorld->PointInArea( source ); 1240 1241 if ( sourceArea == -1 ) { 1242 return; 1243 } 1244 1245 handle = SetupCurrentPVS( source, type ); 1246 1247 for ( j = 0; j < numAreas; j++ ) { 1248 1249 if ( !( currentPVS[handle.i].pvs[j>>3] & (1 << (j&7)) ) ) { 1250 continue; 1251 } 1252 1253 if ( j == sourceArea ) { 1254 color = &colorRed; 1255 } 1256 else { 1257 color = &colorCyan; 1258 } 1259 1260 n = gameRenderWorld->NumPortalsInArea( j ); 1261 1262 // draw all the portals of the area 1263 for ( i = 0; i < n; i++ ) { 1264 portal = gameRenderWorld->GetPortal( j, i ); 1265 1266 numPoints = portal.w->GetNumPoints(); 1267 1268 portal.w->GetPlane( plane ); 1269 offset = plane.Normal() * 4.0f; 1270 for ( k = 0; k < numPoints; k++ ) { 1271 gameRenderWorld->DebugLine( *color, (*portal.w)[k].ToVec3() + offset, (*portal.w)[(k+1)%numPoints].ToVec3() + offset ); 1272 } 1273 } 1274 } 1275 1276 FreeCurrentPVS( handle ); 1277 } 1278 1279 /* 1280 ================ 1281 idPVS::DrawPVS 1282 ================ 1283 */ 1284 void idPVS::DrawPVS( const idBounds &source, const pvsType_t type ) const { 1285 int i, j, k, numPoints, n, num, areas[MAX_BOUNDS_AREAS]; 1286 exitPortal_t portal; 1287 idPlane plane; 1288 idVec3 offset; 1289 idVec4 *color; 1290 pvsHandle_t handle; 1291 1292 num = gameRenderWorld->BoundsInAreas( source, areas, MAX_BOUNDS_AREAS ); 1293 1294 if ( !num ) { 1295 return; 1296 } 1297 1298 handle = SetupCurrentPVS( source, type ); 1299 1300 for ( j = 0; j < numAreas; j++ ) { 1301 1302 if ( !( currentPVS[handle.i].pvs[j>>3] & (1 << (j&7)) ) ) { 1303 continue; 1304 } 1305 1306 for ( i = 0; i < num; i++ ) { 1307 if ( j == areas[i] ) { 1308 break; 1309 } 1310 } 1311 if ( i < num ) { 1312 color = &colorRed; 1313 } 1314 else { 1315 color = &colorCyan; 1316 } 1317 1318 n = gameRenderWorld->NumPortalsInArea( j ); 1319 1320 // draw all the portals of the area 1321 for ( i = 0; i < n; i++ ) { 1322 portal = gameRenderWorld->GetPortal( j, i ); 1323 1324 numPoints = portal.w->GetNumPoints(); 1325 1326 portal.w->GetPlane( plane ); 1327 offset = plane.Normal() * 4.0f; 1328 for ( k = 0; k < numPoints; k++ ) { 1329 gameRenderWorld->DebugLine( *color, (*portal.w)[k].ToVec3() + offset, (*portal.w)[(k+1)%numPoints].ToVec3() + offset ); 1330 } 1331 } 1332 } 1333 1334 FreeCurrentPVS( handle ); 1335 } 1336 1337 /* 1338 ================ 1339 idPVS::DrawPVS 1340 ================ 1341 */ 1342 void idPVS::DrawCurrentPVS( const pvsHandle_t handle, const idVec3 &source ) const { 1343 int i, j, k, numPoints, n, sourceArea; 1344 exitPortal_t portal; 1345 idPlane plane; 1346 idVec3 offset; 1347 idVec4 *color; 1348 1349 if ( handle.i < 0 || handle.i >= MAX_CURRENT_PVS || 1350 handle.h != currentPVS[handle.i].handle.h ) { 1351 gameLocal.Error( "idPVS::DrawCurrentPVS: invalid handle" ); 1352 return; 1353 } 1354 1355 sourceArea = gameRenderWorld->PointInArea( source ); 1356 1357 if ( sourceArea == -1 ) { 1358 return; 1359 } 1360 1361 for ( j = 0; j < numAreas; j++ ) { 1362 1363 if ( !( currentPVS[handle.i].pvs[j>>3] & (1 << (j&7)) ) ) { 1364 continue; 1365 } 1366 1367 if ( j == sourceArea ) { 1368 color = &colorRed; 1369 } 1370 else { 1371 color = &colorCyan; 1372 } 1373 1374 n = gameRenderWorld->NumPortalsInArea( j ); 1375 1376 // draw all the portals of the area 1377 for ( i = 0; i < n; i++ ) { 1378 portal = gameRenderWorld->GetPortal( j, i ); 1379 1380 numPoints = portal.w->GetNumPoints(); 1381 1382 portal.w->GetPlane( plane ); 1383 offset = plane.Normal() * 4.0f; 1384 for ( k = 0; k < numPoints; k++ ) { 1385 gameRenderWorld->DebugLine( *color, (*portal.w)[k].ToVec3() + offset, (*portal.w)[(k+1)%numPoints].ToVec3() + offset ); 1386 } 1387 } 1388 } 1389 } 1390 1391 /* 1392 ================ 1393 idPVS::CheckAreasForPortalSky 1394 ================ 1395 */ 1396 bool idPVS::CheckAreasForPortalSky( const pvsHandle_t handle, const idVec3 &origin ) { 1397 int j, sourceArea; 1398 1399 if ( handle.i < 0 || handle.i >= MAX_CURRENT_PVS || handle.h != currentPVS[handle.i].handle.h ) { 1400 return false; 1401 } 1402 1403 sourceArea = gameRenderWorld->PointInArea( origin ); 1404 1405 if ( sourceArea == -1 ) { 1406 return false; 1407 } 1408 1409 for ( j = 0; j < numAreas; j++ ) { 1410 1411 if ( !( currentPVS[handle.i].pvs[j>>3] & (1 << (j&7)) ) ) { 1412 continue; 1413 } 1414 1415 if ( gameRenderWorld->CheckAreaForPortalSky( j ) ) { 1416 return true; 1417 } 1418 } 1419 1420 return false; 1421 }