sv_world.c (16559B)
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 // world.c -- world query functions 23 24 #include "server.h" 25 26 /* 27 ================ 28 SV_ClipHandleForEntity 29 30 Returns a headnode that can be used for testing or clipping to a 31 given entity. If the entity is a bsp model, the headnode will 32 be returned, otherwise a custom box tree will be constructed. 33 ================ 34 */ 35 clipHandle_t SV_ClipHandleForEntity( const sharedEntity_t *ent ) { 36 if ( ent->r.bmodel ) { 37 // explicit hulls in the BSP model 38 return CM_InlineModel( ent->s.modelindex ); 39 } 40 if ( ent->r.svFlags & SVF_CAPSULE ) { 41 // create a temp capsule from bounding box sizes 42 return CM_TempBoxModel( ent->r.mins, ent->r.maxs, qtrue ); 43 } 44 45 // create a temp tree from bounding box sizes 46 return CM_TempBoxModel( ent->r.mins, ent->r.maxs, qfalse ); 47 } 48 49 50 51 /* 52 =============================================================================== 53 54 ENTITY CHECKING 55 56 To avoid linearly searching through lists of entities during environment testing, 57 the world is carved up with an evenly spaced, axially aligned bsp tree. Entities 58 are kept in chains either at the final leafs, or at the first node that splits 59 them, which prevents having to deal with multiple fragments of a single entity. 60 61 =============================================================================== 62 */ 63 64 typedef struct worldSector_s { 65 int axis; // -1 = leaf node 66 float dist; 67 struct worldSector_s *children[2]; 68 svEntity_t *entities; 69 } worldSector_t; 70 71 #define AREA_DEPTH 4 72 #define AREA_NODES 64 73 74 worldSector_t sv_worldSectors[AREA_NODES]; 75 int sv_numworldSectors; 76 77 78 /* 79 =============== 80 SV_SectorList_f 81 =============== 82 */ 83 void SV_SectorList_f( void ) { 84 int i, c; 85 worldSector_t *sec; 86 svEntity_t *ent; 87 88 for ( i = 0 ; i < AREA_NODES ; i++ ) { 89 sec = &sv_worldSectors[i]; 90 91 c = 0; 92 for ( ent = sec->entities ; ent ; ent = ent->nextEntityInWorldSector ) { 93 c++; 94 } 95 Com_Printf( "sector %i: %i entities\n", i, c ); 96 } 97 } 98 99 /* 100 =============== 101 SV_CreateworldSector 102 103 Builds a uniformly subdivided tree for the given world size 104 =============== 105 */ 106 worldSector_t *SV_CreateworldSector( int depth, vec3_t mins, vec3_t maxs ) { 107 worldSector_t *anode; 108 vec3_t size; 109 vec3_t mins1, maxs1, mins2, maxs2; 110 111 anode = &sv_worldSectors[sv_numworldSectors]; 112 sv_numworldSectors++; 113 114 if (depth == AREA_DEPTH) { 115 anode->axis = -1; 116 anode->children[0] = anode->children[1] = NULL; 117 return anode; 118 } 119 120 VectorSubtract (maxs, mins, size); 121 if (size[0] > size[1]) { 122 anode->axis = 0; 123 } else { 124 anode->axis = 1; 125 } 126 127 anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]); 128 VectorCopy (mins, mins1); 129 VectorCopy (mins, mins2); 130 VectorCopy (maxs, maxs1); 131 VectorCopy (maxs, maxs2); 132 133 maxs1[anode->axis] = mins2[anode->axis] = anode->dist; 134 135 anode->children[0] = SV_CreateworldSector (depth+1, mins2, maxs2); 136 anode->children[1] = SV_CreateworldSector (depth+1, mins1, maxs1); 137 138 return anode; 139 } 140 141 /* 142 =============== 143 SV_ClearWorld 144 145 =============== 146 */ 147 void SV_ClearWorld( void ) { 148 clipHandle_t h; 149 vec3_t mins, maxs; 150 151 Com_Memset( sv_worldSectors, 0, sizeof(sv_worldSectors) ); 152 sv_numworldSectors = 0; 153 154 // get world map bounds 155 h = CM_InlineModel( 0 ); 156 CM_ModelBounds( h, mins, maxs ); 157 SV_CreateworldSector( 0, mins, maxs ); 158 } 159 160 161 /* 162 =============== 163 SV_UnlinkEntity 164 165 =============== 166 */ 167 void SV_UnlinkEntity( sharedEntity_t *gEnt ) { 168 svEntity_t *ent; 169 svEntity_t *scan; 170 worldSector_t *ws; 171 172 ent = SV_SvEntityForGentity( gEnt ); 173 174 gEnt->r.linked = qfalse; 175 176 ws = ent->worldSector; 177 if ( !ws ) { 178 return; // not linked in anywhere 179 } 180 ent->worldSector = NULL; 181 182 if ( ws->entities == ent ) { 183 ws->entities = ent->nextEntityInWorldSector; 184 return; 185 } 186 187 for ( scan = ws->entities ; scan ; scan = scan->nextEntityInWorldSector ) { 188 if ( scan->nextEntityInWorldSector == ent ) { 189 scan->nextEntityInWorldSector = ent->nextEntityInWorldSector; 190 return; 191 } 192 } 193 194 Com_Printf( "WARNING: SV_UnlinkEntity: not found in worldSector\n" ); 195 } 196 197 198 /* 199 =============== 200 SV_LinkEntity 201 202 =============== 203 */ 204 #define MAX_TOTAL_ENT_LEAFS 128 205 void SV_LinkEntity( sharedEntity_t *gEnt ) { 206 worldSector_t *node; 207 int leafs[MAX_TOTAL_ENT_LEAFS]; 208 int cluster; 209 int num_leafs; 210 int i, j, k; 211 int area; 212 int lastLeaf; 213 float *origin, *angles; 214 svEntity_t *ent; 215 216 ent = SV_SvEntityForGentity( gEnt ); 217 218 if ( ent->worldSector ) { 219 SV_UnlinkEntity( gEnt ); // unlink from old position 220 } 221 222 // encode the size into the entityState_t for client prediction 223 if ( gEnt->r.bmodel ) { 224 gEnt->s.solid = SOLID_BMODEL; // a solid_box will never create this value 225 } else if ( gEnt->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY ) ) { 226 // assume that x/y are equal and symetric 227 i = gEnt->r.maxs[0]; 228 if (i<1) 229 i = 1; 230 if (i>255) 231 i = 255; 232 233 // z is not symetric 234 j = (-gEnt->r.mins[2]); 235 if (j<1) 236 j = 1; 237 if (j>255) 238 j = 255; 239 240 // and z maxs can be negative... 241 k = (gEnt->r.maxs[2]+32); 242 if (k<1) 243 k = 1; 244 if (k>255) 245 k = 255; 246 247 gEnt->s.solid = (k<<16) | (j<<8) | i; 248 } else { 249 gEnt->s.solid = 0; 250 } 251 252 // get the position 253 origin = gEnt->r.currentOrigin; 254 angles = gEnt->r.currentAngles; 255 256 // set the abs box 257 if ( gEnt->r.bmodel && (angles[0] || angles[1] || angles[2]) ) { 258 // expand for rotation 259 float max; 260 int i; 261 262 max = RadiusFromBounds( gEnt->r.mins, gEnt->r.maxs ); 263 for (i=0 ; i<3 ; i++) { 264 gEnt->r.absmin[i] = origin[i] - max; 265 gEnt->r.absmax[i] = origin[i] + max; 266 } 267 } else { 268 // normal 269 VectorAdd (origin, gEnt->r.mins, gEnt->r.absmin); 270 VectorAdd (origin, gEnt->r.maxs, gEnt->r.absmax); 271 } 272 273 // because movement is clipped an epsilon away from an actual edge, 274 // we must fully check even when bounding boxes don't quite touch 275 gEnt->r.absmin[0] -= 1; 276 gEnt->r.absmin[1] -= 1; 277 gEnt->r.absmin[2] -= 1; 278 gEnt->r.absmax[0] += 1; 279 gEnt->r.absmax[1] += 1; 280 gEnt->r.absmax[2] += 1; 281 282 // link to PVS leafs 283 ent->numClusters = 0; 284 ent->lastCluster = 0; 285 ent->areanum = -1; 286 ent->areanum2 = -1; 287 288 //get all leafs, including solids 289 num_leafs = CM_BoxLeafnums( gEnt->r.absmin, gEnt->r.absmax, 290 leafs, MAX_TOTAL_ENT_LEAFS, &lastLeaf ); 291 292 // if none of the leafs were inside the map, the 293 // entity is outside the world and can be considered unlinked 294 if ( !num_leafs ) { 295 return; 296 } 297 298 // set areas, even from clusters that don't fit in the entity array 299 for (i=0 ; i<num_leafs ; i++) { 300 area = CM_LeafArea (leafs[i]); 301 if (area != -1) { 302 // doors may legally straggle two areas, 303 // but nothing should evern need more than that 304 if (ent->areanum != -1 && ent->areanum != area) { 305 if (ent->areanum2 != -1 && ent->areanum2 != area && sv.state == SS_LOADING) { 306 Com_DPrintf ("Object %i touching 3 areas at %f %f %f\n", 307 gEnt->s.number, 308 gEnt->r.absmin[0], gEnt->r.absmin[1], gEnt->r.absmin[2]); 309 } 310 ent->areanum2 = area; 311 } else { 312 ent->areanum = area; 313 } 314 } 315 } 316 317 // store as many explicit clusters as we can 318 ent->numClusters = 0; 319 for (i=0 ; i < num_leafs ; i++) { 320 cluster = CM_LeafCluster( leafs[i] ); 321 if ( cluster != -1 ) { 322 ent->clusternums[ent->numClusters++] = cluster; 323 if ( ent->numClusters == MAX_ENT_CLUSTERS ) { 324 break; 325 } 326 } 327 } 328 329 // store off a last cluster if we need to 330 if ( i != num_leafs ) { 331 ent->lastCluster = CM_LeafCluster( lastLeaf ); 332 } 333 334 gEnt->r.linkcount++; 335 336 // find the first world sector node that the ent's box crosses 337 node = sv_worldSectors; 338 while (1) 339 { 340 if (node->axis == -1) 341 break; 342 if ( gEnt->r.absmin[node->axis] > node->dist) 343 node = node->children[0]; 344 else if ( gEnt->r.absmax[node->axis] < node->dist) 345 node = node->children[1]; 346 else 347 break; // crosses the node 348 } 349 350 // link it in 351 ent->worldSector = node; 352 ent->nextEntityInWorldSector = node->entities; 353 node->entities = ent; 354 355 gEnt->r.linked = qtrue; 356 } 357 358 /* 359 ============================================================================ 360 361 AREA QUERY 362 363 Fills in a list of all entities who's absmin / absmax intersects the given 364 bounds. This does NOT mean that they actually touch in the case of bmodels. 365 ============================================================================ 366 */ 367 368 typedef struct { 369 const float *mins; 370 const float *maxs; 371 int *list; 372 int count, maxcount; 373 } areaParms_t; 374 375 376 /* 377 ==================== 378 SV_AreaEntities_r 379 380 ==================== 381 */ 382 void SV_AreaEntities_r( worldSector_t *node, areaParms_t *ap ) { 383 svEntity_t *check, *next; 384 sharedEntity_t *gcheck; 385 int count; 386 387 count = 0; 388 389 for ( check = node->entities ; check ; check = next ) { 390 next = check->nextEntityInWorldSector; 391 392 gcheck = SV_GEntityForSvEntity( check ); 393 394 if ( gcheck->r.absmin[0] > ap->maxs[0] 395 || gcheck->r.absmin[1] > ap->maxs[1] 396 || gcheck->r.absmin[2] > ap->maxs[2] 397 || gcheck->r.absmax[0] < ap->mins[0] 398 || gcheck->r.absmax[1] < ap->mins[1] 399 || gcheck->r.absmax[2] < ap->mins[2]) { 400 continue; 401 } 402 403 if ( ap->count == ap->maxcount ) { 404 Com_Printf ("SV_AreaEntities: MAXCOUNT\n"); 405 return; 406 } 407 408 ap->list[ap->count] = check - sv.svEntities; 409 ap->count++; 410 } 411 412 if (node->axis == -1) { 413 return; // terminal node 414 } 415 416 // recurse down both sides 417 if ( ap->maxs[node->axis] > node->dist ) { 418 SV_AreaEntities_r ( node->children[0], ap ); 419 } 420 if ( ap->mins[node->axis] < node->dist ) { 421 SV_AreaEntities_r ( node->children[1], ap ); 422 } 423 } 424 425 /* 426 ================ 427 SV_AreaEntities 428 ================ 429 */ 430 int SV_AreaEntities( const vec3_t mins, const vec3_t maxs, int *entityList, int maxcount ) { 431 areaParms_t ap; 432 433 ap.mins = mins; 434 ap.maxs = maxs; 435 ap.list = entityList; 436 ap.count = 0; 437 ap.maxcount = maxcount; 438 439 SV_AreaEntities_r( sv_worldSectors, &ap ); 440 441 return ap.count; 442 } 443 444 445 446 //=========================================================================== 447 448 449 typedef struct { 450 vec3_t boxmins, boxmaxs;// enclose the test object along entire move 451 const float *mins; 452 const float *maxs; // size of the moving object 453 const float *start; 454 vec3_t end; 455 trace_t trace; 456 int passEntityNum; 457 int contentmask; 458 int capsule; 459 } moveclip_t; 460 461 462 /* 463 ==================== 464 SV_ClipToEntity 465 466 ==================== 467 */ 468 void SV_ClipToEntity( trace_t *trace, const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, int entityNum, int contentmask, int capsule ) { 469 sharedEntity_t *touch; 470 clipHandle_t clipHandle; 471 float *origin, *angles; 472 473 touch = SV_GentityNum( entityNum ); 474 475 Com_Memset(trace, 0, sizeof(trace_t)); 476 477 // if it doesn't have any brushes of a type we 478 // are looking for, ignore it 479 if ( ! ( contentmask & touch->r.contents ) ) { 480 trace->fraction = 1.0; 481 return; 482 } 483 484 // might intersect, so do an exact clip 485 clipHandle = SV_ClipHandleForEntity (touch); 486 487 origin = touch->r.currentOrigin; 488 angles = touch->r.currentAngles; 489 490 if ( !touch->r.bmodel ) { 491 angles = vec3_origin; // boxes don't rotate 492 } 493 494 CM_TransformedBoxTrace ( trace, (float *)start, (float *)end, 495 (float *)mins, (float *)maxs, clipHandle, contentmask, 496 origin, angles, capsule); 497 498 if ( trace->fraction < 1 ) { 499 trace->entityNum = touch->s.number; 500 } 501 } 502 503 504 /* 505 ==================== 506 SV_ClipMoveToEntities 507 508 ==================== 509 */ 510 void SV_ClipMoveToEntities( moveclip_t *clip ) { 511 int i, num; 512 int touchlist[MAX_GENTITIES]; 513 sharedEntity_t *touch; 514 int passOwnerNum; 515 trace_t trace; 516 clipHandle_t clipHandle; 517 float *origin, *angles; 518 519 num = SV_AreaEntities( clip->boxmins, clip->boxmaxs, touchlist, MAX_GENTITIES); 520 521 if ( clip->passEntityNum != ENTITYNUM_NONE ) { 522 passOwnerNum = ( SV_GentityNum( clip->passEntityNum ) )->r.ownerNum; 523 if ( passOwnerNum == ENTITYNUM_NONE ) { 524 passOwnerNum = -1; 525 } 526 } else { 527 passOwnerNum = -1; 528 } 529 530 for ( i=0 ; i<num ; i++ ) { 531 if ( clip->trace.allsolid ) { 532 return; 533 } 534 touch = SV_GentityNum( touchlist[i] ); 535 536 // see if we should ignore this entity 537 if ( clip->passEntityNum != ENTITYNUM_NONE ) { 538 if ( touchlist[i] == clip->passEntityNum ) { 539 continue; // don't clip against the pass entity 540 } 541 if ( touch->r.ownerNum == clip->passEntityNum ) { 542 continue; // don't clip against own missiles 543 } 544 if ( touch->r.ownerNum == passOwnerNum ) { 545 continue; // don't clip against other missiles from our owner 546 } 547 } 548 549 // if it doesn't have any brushes of a type we 550 // are looking for, ignore it 551 if ( ! ( clip->contentmask & touch->r.contents ) ) { 552 continue; 553 } 554 555 // might intersect, so do an exact clip 556 clipHandle = SV_ClipHandleForEntity (touch); 557 558 origin = touch->r.currentOrigin; 559 angles = touch->r.currentAngles; 560 561 562 if ( !touch->r.bmodel ) { 563 angles = vec3_origin; // boxes don't rotate 564 } 565 566 CM_TransformedBoxTrace ( &trace, (float *)clip->start, (float *)clip->end, 567 (float *)clip->mins, (float *)clip->maxs, clipHandle, clip->contentmask, 568 origin, angles, clip->capsule); 569 570 if ( trace.allsolid ) { 571 clip->trace.allsolid = qtrue; 572 trace.entityNum = touch->s.number; 573 } else if ( trace.startsolid ) { 574 clip->trace.startsolid = qtrue; 575 trace.entityNum = touch->s.number; 576 } 577 578 if ( trace.fraction < clip->trace.fraction ) { 579 qboolean oldStart; 580 581 // make sure we keep a startsolid from a previous trace 582 oldStart = clip->trace.startsolid; 583 584 trace.entityNum = touch->s.number; 585 clip->trace = trace; 586 clip->trace.startsolid |= oldStart; 587 } 588 } 589 } 590 591 592 /* 593 ================== 594 SV_Trace 595 596 Moves the given mins/maxs volume through the world from start to end. 597 passEntityNum and entities owned by passEntityNum are explicitly not checked. 598 ================== 599 */ 600 void SV_Trace( trace_t *results, const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, int passEntityNum, int contentmask, int capsule ) { 601 moveclip_t clip; 602 int i; 603 604 if ( !mins ) { 605 mins = vec3_origin; 606 } 607 if ( !maxs ) { 608 maxs = vec3_origin; 609 } 610 611 Com_Memset ( &clip, 0, sizeof ( moveclip_t ) ); 612 613 // clip to world 614 CM_BoxTrace( &clip.trace, start, end, mins, maxs, 0, contentmask, capsule ); 615 clip.trace.entityNum = clip.trace.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE; 616 if ( clip.trace.fraction == 0 ) { 617 *results = clip.trace; 618 return; // blocked immediately by the world 619 } 620 621 clip.contentmask = contentmask; 622 clip.start = start; 623 // VectorCopy( clip.trace.endpos, clip.end ); 624 VectorCopy( end, clip.end ); 625 clip.mins = mins; 626 clip.maxs = maxs; 627 clip.passEntityNum = passEntityNum; 628 clip.capsule = capsule; 629 630 // create the bounding box of the entire move 631 // we can limit it to the part of the move not 632 // already clipped off by the world, which can be 633 // a significant savings for line of sight and shot traces 634 for ( i=0 ; i<3 ; i++ ) { 635 if ( end[i] > start[i] ) { 636 clip.boxmins[i] = clip.start[i] + clip.mins[i] - 1; 637 clip.boxmaxs[i] = clip.end[i] + clip.maxs[i] + 1; 638 } else { 639 clip.boxmins[i] = clip.end[i] + clip.mins[i] - 1; 640 clip.boxmaxs[i] = clip.start[i] + clip.maxs[i] + 1; 641 } 642 } 643 644 // clip to other solid entities 645 SV_ClipMoveToEntities ( &clip ); 646 647 *results = clip.trace; 648 } 649 650 651 652 /* 653 ============= 654 SV_PointContents 655 ============= 656 */ 657 int SV_PointContents( const vec3_t p, int passEntityNum ) { 658 int touch[MAX_GENTITIES]; 659 sharedEntity_t *hit; 660 int i, num; 661 int contents, c2; 662 clipHandle_t clipHandle; 663 float *angles; 664 665 // get base contents from world 666 contents = CM_PointContents( p, 0 ); 667 668 // or in contents from all the other entities 669 num = SV_AreaEntities( p, p, touch, MAX_GENTITIES ); 670 671 for ( i=0 ; i<num ; i++ ) { 672 if ( touch[i] == passEntityNum ) { 673 continue; 674 } 675 hit = SV_GentityNum( touch[i] ); 676 // might intersect, so do an exact clip 677 clipHandle = SV_ClipHandleForEntity( hit ); 678 angles = hit->s.angles; 679 if ( !hit->r.bmodel ) { 680 angles = vec3_origin; // boxes don't rotate 681 } 682 683 c2 = CM_TransformedPointContents (p, clipHandle, hit->s.origin, hit->s.angles); 684 685 contents |= c2; 686 } 687 688 return contents; 689 } 690 691