p_maputl.cpp (17235B)
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 "Precompiled.h" 30 #include "globaldata.h" 31 32 33 #include <stdlib.h> 34 35 36 #include "m_bbox.h" 37 38 #include "doomdef.h" 39 #include "p_local.h" 40 41 42 // State. 43 #include "r_state.h" 44 45 // 46 // P_AproxDistance 47 // Gives an estimation of distance (not exact) 48 // 49 50 fixed_t 51 P_AproxDistance 52 ( fixed_t dx, 53 fixed_t dy ) 54 { 55 dx = abs(dx); 56 dy = abs(dy); 57 if (dx < dy) 58 return dx+dy-(dx>>1); 59 return dx+dy-(dy>>1); 60 } 61 62 63 // 64 // P_PointOnLineSide 65 // Returns 0 or 1 66 // 67 int 68 P_PointOnLineSide 69 ( fixed_t x, 70 fixed_t y, 71 line_t* line ) 72 { 73 fixed_t dx; 74 fixed_t dy; 75 fixed_t left; 76 fixed_t right; 77 78 if (!line->dx) 79 { 80 if (x <= line->v1->x) 81 return line->dy > 0; 82 83 return line->dy < 0; 84 } 85 if (!line->dy) 86 { 87 if (y <= line->v1->y) 88 return line->dx < 0; 89 90 return line->dx > 0; 91 } 92 93 dx = (x - line->v1->x); 94 dy = (y - line->v1->y); 95 96 left = FixedMul ( line->dy>>FRACBITS , dx ); 97 right = FixedMul ( dy , line->dx>>FRACBITS ); 98 99 if (right < left) 100 return 0; // front side 101 return 1; // back side 102 } 103 104 105 106 // 107 // P_BoxOnLineSide 108 // Considers the line to be infinite 109 // Returns side 0 or 1, -1 if box crosses the line. 110 // 111 int 112 P_BoxOnLineSide 113 ( fixed_t* tmbox, 114 line_t* ld ) 115 { 116 int p1 = 0; 117 int p2 = 0; 118 119 switch (ld->slopetype) 120 { 121 case ST_HORIZONTAL: 122 p1 = tmbox[BOXTOP] > ld->v1->y; 123 p2 = tmbox[BOXBOTTOM] > ld->v1->y; 124 if (ld->dx < 0) 125 { 126 p1 ^= 1; 127 p2 ^= 1; 128 } 129 break; 130 131 case ST_VERTICAL: 132 p1 = tmbox[BOXRIGHT] < ld->v1->x; 133 p2 = tmbox[BOXLEFT] < ld->v1->x; 134 if (ld->dy < 0) 135 { 136 p1 ^= 1; 137 p2 ^= 1; 138 } 139 break; 140 141 case ST_POSITIVE: 142 p1 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXTOP], ld); 143 p2 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXBOTTOM], ld); 144 break; 145 146 case ST_NEGATIVE: 147 p1 = P_PointOnLineSide (tmbox[BOXRIGHT], tmbox[BOXTOP], ld); 148 p2 = P_PointOnLineSide (tmbox[BOXLEFT], tmbox[BOXBOTTOM], ld); 149 break; 150 } 151 152 if (p1 == p2) 153 return p1; 154 return -1; 155 } 156 157 158 // 159 // P_PointOnDivlineSide 160 // Returns 0 or 1. 161 // 162 int 163 P_PointOnDivlineSide 164 ( fixed_t x, 165 fixed_t y, 166 divline_t* line ) 167 { 168 fixed_t dx; 169 fixed_t dy; 170 fixed_t left; 171 fixed_t right; 172 173 if (!line->dx) 174 { 175 if (x <= line->x) 176 return line->dy > 0; 177 178 return line->dy < 0; 179 } 180 if (!line->dy) 181 { 182 if (y <= line->y) 183 return line->dx < 0; 184 185 return line->dx > 0; 186 } 187 188 dx = (x - line->x); 189 dy = (y - line->y); 190 191 // try to quickly decide by looking at sign bits 192 if ( (line->dy ^ line->dx ^ dx ^ dy)&0x80000000 ) 193 { 194 if ( (line->dy ^ dx) & 0x80000000 ) 195 return 1; // (left is negative) 196 return 0; 197 } 198 199 left = FixedMul ( line->dy>>8, dx>>8 ); 200 right = FixedMul ( dy>>8 , line->dx>>8 ); 201 202 if (right < left) 203 return 0; // front side 204 return 1; // back side 205 } 206 207 208 209 // 210 // P_MakeDivline 211 // 212 void 213 P_MakeDivline 214 ( line_t* li, 215 divline_t* dl ) 216 { 217 dl->x = li->v1->x; 218 dl->y = li->v1->y; 219 dl->dx = li->dx; 220 dl->dy = li->dy; 221 } 222 223 224 225 // 226 // P_InterceptVector 227 // Returns the fractional intercept point 228 // along the first divline. 229 // This is only called by the addthings 230 // and addlines traversers. 231 // 232 fixed_t 233 P_InterceptVector 234 ( divline_t* v2, 235 divline_t* v1 ) 236 { 237 #if 1 238 fixed_t frac; 239 fixed_t num; 240 fixed_t den; 241 242 den = FixedMul (v1->dy>>8,v2->dx) - FixedMul(v1->dx>>8,v2->dy); 243 244 if (den == 0) 245 return 0; 246 // I_Error ("P_InterceptVector: parallel"); 247 248 num = 249 FixedMul ( (v1->x - v2->x)>>8 ,v1->dy ) 250 +FixedMul ( (v2->y - v1->y)>>8, v1->dx ); 251 252 frac = FixedDiv (num , den); 253 254 return frac; 255 #else // UNUSED, float debug. 256 float frac; 257 float num; 258 float den; 259 float v1x; 260 float v1y; 261 float v1dx; 262 float v1dy; 263 float v2x; 264 float v2y; 265 float v2dx; 266 float v2dy; 267 268 v1x = (float)v1->x/FRACUNIT; 269 v1y = (float)v1->y/FRACUNIT; 270 v1dx = (float)v1->dx/FRACUNIT; 271 v1dy = (float)v1->dy/FRACUNIT; 272 v2x = (float)v2->x/FRACUNIT; 273 v2y = (float)v2->y/FRACUNIT; 274 v2dx = (float)v2->dx/FRACUNIT; 275 v2dy = (float)v2->dy/FRACUNIT; 276 277 den = v1dy*v2dx - v1dx*v2dy; 278 279 if (den == 0) 280 return 0; // parallel 281 282 num = (v1x - v2x)*v1dy + (v2y - v1y)*v1dx; 283 frac = num / den; 284 285 return frac*FRACUNIT; 286 #endif 287 } 288 289 290 // 291 // P_LineOpening 292 // Sets ::g->opentop and ::g->openbottom to the window 293 // through a two sided line. 294 // OPTIMIZE: keep this precalculated 295 // 296 297 298 void P_LineOpening (line_t* maputil_linedef) 299 { 300 sector_t* front; 301 sector_t* back; 302 303 if (maputil_linedef->sidenum[1] == -1) 304 { 305 // single sided line 306 ::g->openrange = 0; 307 return; 308 } 309 310 front = maputil_linedef->frontsector; 311 back = maputil_linedef->backsector; 312 313 if (front->ceilingheight < back->ceilingheight) 314 ::g->opentop = front->ceilingheight; 315 else 316 ::g->opentop = back->ceilingheight; 317 318 if (front->floorheight > back->floorheight) 319 { 320 ::g->openbottom = front->floorheight; 321 ::g->lowfloor = back->floorheight; 322 } 323 else 324 { 325 ::g->openbottom = back->floorheight; 326 ::g->lowfloor = front->floorheight; 327 } 328 329 ::g->openrange = ::g->opentop - ::g->openbottom; 330 } 331 332 333 // 334 // THING POSITION SETTING 335 // 336 337 338 // 339 // P_UnsetThingPosition 340 // Unlinks a thing from block map and ::g->sectors. 341 // On each position change, BLOCKMAP and other 342 // lookups maintaining lists ot things inside 343 // these structures need to be updated. 344 // 345 void P_UnsetThingPosition (mobj_t* thing) 346 { 347 int blockx; 348 int blocky; 349 350 if ( ! (thing->flags & MF_NOSECTOR) ) 351 { 352 // inert things don't need to be in blockmap? 353 // unlink from subsector 354 if (thing->snext) 355 thing->snext->sprev = thing->sprev; 356 357 if (thing->sprev) 358 thing->sprev->snext = thing->snext; 359 else 360 thing->subsector->sector->thinglist = thing->snext; 361 } 362 363 if ( ! (thing->flags & MF_NOBLOCKMAP) ) 364 { 365 // inert things don't need to be in ::g->blockmap 366 // unlink from block map 367 if (thing->bnext) 368 thing->bnext->bprev = thing->bprev; 369 370 if (thing->bprev) 371 thing->bprev->bnext = thing->bnext; 372 else 373 { 374 blockx = (thing->x - ::g->bmaporgx)>>MAPBLOCKSHIFT; 375 blocky = (thing->y - ::g->bmaporgy)>>MAPBLOCKSHIFT; 376 377 if (blockx>=0 && blockx < ::g->bmapwidth 378 && blocky>=0 && blocky < ::g->bmapheight) 379 { 380 ::g->blocklinks[blocky*::g->bmapwidth+blockx] = thing->bnext; 381 } 382 } 383 } 384 } 385 386 387 // 388 // P_SetThingPosition 389 // Links a thing into both a block and a subsector 390 // based on it's x y. 391 // Sets thing->subsector properly 392 // 393 void 394 P_SetThingPosition (mobj_t* thing) 395 { 396 subsector_t* ss; 397 sector_t* sec; 398 int blockx; 399 int blocky; 400 mobj_t** link; 401 402 403 // link into subsector 404 ss = R_PointInSubsector (thing->x,thing->y); 405 thing->subsector = ss; 406 407 if ( ! (thing->flags & MF_NOSECTOR) ) 408 { 409 // invisible things don't go into the sector links 410 sec = ss->sector; 411 412 thing->sprev = NULL; 413 thing->snext = sec->thinglist; 414 415 if (sec->thinglist) 416 sec->thinglist->sprev = thing; 417 418 sec->thinglist = thing; 419 } 420 421 422 // link into ::g->blockmap 423 if ( ! (thing->flags & MF_NOBLOCKMAP) ) 424 { 425 // inert things don't need to be in ::g->blockmap 426 blockx = (thing->x - ::g->bmaporgx)>>MAPBLOCKSHIFT; 427 blocky = (thing->y - ::g->bmaporgy)>>MAPBLOCKSHIFT; 428 429 if (blockx>=0 430 && blockx < ::g->bmapwidth 431 && blocky>=0 432 && blocky < ::g->bmapheight) 433 { 434 link = &::g->blocklinks[blocky*::g->bmapwidth+blockx]; 435 thing->bprev = NULL; 436 thing->bnext = *link; 437 if (*link) 438 (*link)->bprev = thing; 439 440 *link = thing; 441 } 442 else 443 { 444 // thing is off the map 445 thing->bnext = thing->bprev = NULL; 446 } 447 } 448 } 449 450 451 452 // 453 // BLOCK MAP ITERATORS 454 // For each line/thing in the given mapblock, 455 // call the passed PIT_* function. 456 // If the function returns false, 457 // exit with false without checking anything else. 458 // 459 460 461 // 462 // P_BlockLinesIterator 463 // The ::g->validcount flags are used to avoid checking ::g->lines 464 // that are marked in multiple mapblocks, 465 // so increment ::g->validcount before the first call 466 // to P_BlockLinesIterator, then make one or more calls 467 // to it. 468 // 469 qboolean 470 P_BlockLinesIterator 471 ( int x, 472 int y, 473 qboolean(*func)(line_t*) ) 474 { 475 int offset; 476 short* list; 477 line_t* ld; 478 479 if (x<0 480 || y<0 481 || x>=::g->bmapwidth 482 || y>=::g->bmapheight) 483 { 484 return true; 485 } 486 487 offset = y*::g->bmapwidth+x; 488 489 offset = *(::g->blockmap+offset); 490 491 for ( list = ::g->blockmaplump+offset ; *list != -1 ; list++) 492 { 493 ld = &::g->lines[*list]; 494 495 if (ld->validcount == ::g->validcount) 496 continue; // line has already been checked 497 498 ld->validcount = ::g->validcount; 499 500 if ( !func(ld) ) 501 return false; 502 } 503 return true; // everything was checked 504 } 505 506 507 // 508 // P_BlockThingsIterator 509 // 510 qboolean 511 P_BlockThingsIterator 512 ( int x, 513 int y, 514 qboolean(*func)(mobj_t*) ) 515 { 516 mobj_t* mobj; 517 518 if ( x<0 519 || y<0 520 || x>=::g->bmapwidth 521 || y>=::g->bmapheight) 522 { 523 return true; 524 } 525 526 527 for (mobj = ::g->blocklinks[y*::g->bmapwidth+x] ; 528 mobj ; 529 mobj = mobj->bnext) 530 { 531 if (!func( mobj ) ) 532 return false; 533 } 534 return true; 535 } 536 537 538 539 // 540 // INTERCEPT ROUTINES 541 // 542 543 544 // 545 // PIT_AddLineIntercepts. 546 // Looks for ::g->lines in the given block 547 // that intercept the given ::g->trace 548 // to add to the ::g->intercepts list. 549 // 550 // A line is crossed if its endpoints 551 // are on opposite ::g->sides of the ::g->trace. 552 // Returns true if ::g->earlyout and a solid line hit. 553 // 554 qboolean 555 PIT_AddLineIntercepts (line_t* ld) 556 { 557 int s1; 558 int s2; 559 fixed_t frac; 560 divline_t dl; 561 562 // avoid precision problems with two routines 563 if ( ::g->trace.dx > FRACUNIT*16 564 || ::g->trace.dy > FRACUNIT*16 565 || ::g->trace.dx < -FRACUNIT*16 566 || ::g->trace.dy < -FRACUNIT*16) 567 { 568 s1 = P_PointOnDivlineSide (ld->v1->x, ld->v1->y, &::g->trace); 569 s2 = P_PointOnDivlineSide (ld->v2->x, ld->v2->y, &::g->trace); 570 } 571 else 572 { 573 s1 = P_PointOnLineSide (::g->trace.x, ::g->trace.y, ld); 574 s2 = P_PointOnLineSide (::g->trace.x+::g->trace.dx, ::g->trace.y+::g->trace.dy, ld); 575 } 576 577 if (s1 == s2) 578 return true; // line isn't crossed 579 580 // hit the line 581 P_MakeDivline (ld, &dl); 582 frac = P_InterceptVector (&::g->trace, &dl); 583 584 if (frac < 0) 585 return true; // behind source 586 587 // try to early out the check 588 if (::g->earlyout 589 && frac < FRACUNIT 590 && !ld->backsector) 591 { 592 return false; // stop checking 593 } 594 595 596 ::g->intercept_p->frac = frac; 597 ::g->intercept_p->isaline = true; 598 ::g->intercept_p->d.line = ld; 599 ::g->intercept_p++; 600 601 return true; // continue 602 } 603 604 605 606 // 607 // PIT_AddThingIntercepts 608 // 609 qboolean PIT_AddThingIntercepts (mobj_t* thing) 610 { 611 fixed_t x1; 612 fixed_t y1; 613 fixed_t x2; 614 fixed_t y2; 615 616 int s1; 617 int s2; 618 619 qboolean tracepositive; 620 621 divline_t dl; 622 623 fixed_t frac; 624 625 tracepositive = (::g->trace.dx ^ ::g->trace.dy)>0; 626 627 // check a corner to corner crossection for hit 628 if (tracepositive) 629 { 630 x1 = thing->x - thing->radius; 631 y1 = thing->y + thing->radius; 632 633 x2 = thing->x + thing->radius; 634 y2 = thing->y - thing->radius; 635 } 636 else 637 { 638 x1 = thing->x - thing->radius; 639 y1 = thing->y - thing->radius; 640 641 x2 = thing->x + thing->radius; 642 y2 = thing->y + thing->radius; 643 } 644 645 s1 = P_PointOnDivlineSide (x1, y1, &::g->trace); 646 s2 = P_PointOnDivlineSide (x2, y2, &::g->trace); 647 648 if (s1 == s2) 649 return true; // line isn't crossed 650 651 dl.x = x1; 652 dl.y = y1; 653 dl.dx = x2-x1; 654 dl.dy = y2-y1; 655 656 frac = P_InterceptVector (&::g->trace, &dl); 657 658 if (frac < 0) 659 return true; // behind source 660 661 ::g->intercept_p->frac = frac; 662 ::g->intercept_p->isaline = false; 663 ::g->intercept_p->d.thing = thing; 664 ::g->intercept_p++; 665 666 return true; // keep going 667 } 668 669 670 // 671 // P_TraverseIntercepts 672 // Returns true if the traverser function returns true 673 // for all ::g->lines. 674 // 675 qboolean 676 P_TraverseIntercepts 677 ( traverser_t func, 678 fixed_t maxfrac ) 679 { 680 int count; 681 fixed_t dist; 682 intercept_t* scan; 683 intercept_t* in; 684 685 count = ::g->intercept_p - ::g->intercepts; 686 687 in = 0; // shut up compiler warning 688 689 while (count--) 690 { 691 dist = MAXINT; 692 for (scan = ::g->intercepts ; scan < ::g->intercept_p ; scan++) 693 { 694 if (scan->frac < dist) 695 { 696 dist = scan->frac; 697 in = scan; 698 } 699 } 700 701 if (dist > maxfrac) 702 return true; // checked everything in range 703 704 #if 0 // UNUSED 705 { 706 // don't check these yet, there may be others inserted 707 in = scan = ::g->intercepts; 708 for ( scan = ::g->intercepts ; scan<::g->intercept_p ; scan++) 709 if (scan->frac > maxfrac) 710 *in++ = *scan; 711 ::g->intercept_p = in; 712 return false; 713 } 714 #endif 715 716 if ( !func (in) ) 717 return false; // don't bother going farther 718 719 in->frac = MAXINT; 720 } 721 722 return true; // everything was traversed 723 } 724 725 726 727 728 // 729 // P_PathTraverse 730 // Traces a line from x1,y1 to x2,y2, 731 // calling the traverser function for each. 732 // Returns true if the traverser function returns true 733 // for all ::g->lines. 734 // 735 qboolean 736 P_PathTraverse 737 ( fixed_t x1, 738 fixed_t y1, 739 fixed_t x2, 740 fixed_t y2, 741 int flags, 742 qboolean (*trav) (intercept_t *)) 743 { 744 fixed_t xt1; 745 fixed_t yt1; 746 fixed_t xt2; 747 fixed_t yt2; 748 749 fixed_t xstep; 750 fixed_t ystep; 751 752 fixed_t partial; 753 754 fixed_t xintercept; 755 fixed_t yintercept; 756 757 int mapx; 758 int mapy; 759 760 int mapxstep; 761 int mapystep; 762 763 int count; 764 765 ::g->earlyout = flags & PT_EARLYOUT; 766 767 ::g->validcount++; 768 ::g->intercept_p = ::g->intercepts; 769 770 if ( ((x1-::g->bmaporgx)&(MAPBLOCKSIZE-1)) == 0) 771 x1 += FRACUNIT; // don't side exactly on a line 772 773 if ( ((y1-::g->bmaporgy)&(MAPBLOCKSIZE-1)) == 0) 774 y1 += FRACUNIT; // don't side exactly on a line 775 776 ::g->trace.x = x1; 777 ::g->trace.y = y1; 778 ::g->trace.dx = x2 - x1; 779 ::g->trace.dy = y2 - y1; 780 781 x1 -= ::g->bmaporgx; 782 y1 -= ::g->bmaporgy; 783 xt1 = x1>>MAPBLOCKSHIFT; 784 yt1 = y1>>MAPBLOCKSHIFT; 785 786 x2 -= ::g->bmaporgx; 787 y2 -= ::g->bmaporgy; 788 xt2 = x2>>MAPBLOCKSHIFT; 789 yt2 = y2>>MAPBLOCKSHIFT; 790 791 if (xt2 > xt1) 792 { 793 mapxstep = 1; 794 partial = FRACUNIT - ((x1>>MAPBTOFRAC)&(FRACUNIT-1)); 795 ystep = FixedDiv (y2-y1,abs(x2-x1)); 796 } 797 else if (xt2 < xt1) 798 { 799 mapxstep = -1; 800 partial = (x1>>MAPBTOFRAC)&(FRACUNIT-1); 801 ystep = FixedDiv (y2-y1,abs(x2-x1)); 802 } 803 else 804 { 805 mapxstep = 0; 806 partial = FRACUNIT; 807 ystep = 256*FRACUNIT; 808 } 809 810 yintercept = (y1>>MAPBTOFRAC) + FixedMul (partial, ystep); 811 812 813 if (yt2 > yt1) 814 { 815 mapystep = 1; 816 partial = FRACUNIT - ((y1>>MAPBTOFRAC)&(FRACUNIT-1)); 817 xstep = FixedDiv (x2-x1,abs(y2-y1)); 818 } 819 else if (yt2 < yt1) 820 { 821 mapystep = -1; 822 partial = (y1>>MAPBTOFRAC)&(FRACUNIT-1); 823 xstep = FixedDiv (x2-x1,abs(y2-y1)); 824 } 825 else 826 { 827 mapystep = 0; 828 partial = FRACUNIT; 829 xstep = 256*FRACUNIT; 830 } 831 xintercept = (x1>>MAPBTOFRAC) + FixedMul (partial, xstep); 832 833 // Step through map blocks. 834 // Count is present to prevent a round off error 835 // from skipping the break. 836 mapx = xt1; 837 mapy = yt1; 838 839 for (count = 0 ; count < 64 ; count++) 840 { 841 if (flags & PT_ADDLINES) 842 { 843 if (!P_BlockLinesIterator (mapx, mapy,PIT_AddLineIntercepts)) 844 return false; // early out 845 } 846 847 if (flags & PT_ADDTHINGS) 848 { 849 if (!P_BlockThingsIterator (mapx, mapy,PIT_AddThingIntercepts)) 850 return false; // early out 851 } 852 853 if (mapx == xt2 854 && mapy == yt2) 855 { 856 break; 857 } 858 859 if ( (yintercept >> FRACBITS) == mapy) 860 { 861 yintercept += ystep; 862 mapx += mapxstep; 863 } 864 else if ( (xintercept >> FRACBITS) == mapx) 865 { 866 xintercept += xstep; 867 mapy += mapystep; 868 } 869 870 } 871 // go through the sorted list 872 return P_TraverseIntercepts ( trav, FRACUNIT ); 873 } 874 875 876 877