Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

sv_world.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // world.c -- world query functions
00023 
00024 #include "server.h"
00025 
00026 /*
00027 ================
00028 SV_ClipHandleForEntity
00029 
00030 Returns a headnode that can be used for testing or clipping to a
00031 given entity.  If the entity is a bsp model, the headnode will
00032 be returned, otherwise a custom box tree will be constructed.
00033 ================
00034 */
00035 clipHandle_t SV_ClipHandleForEntity( const sharedEntity_t *ent ) {
00036     if ( ent->r.bmodel ) {
00037         // explicit hulls in the BSP model
00038         return CM_InlineModel( ent->s.modelindex );
00039     }
00040     if ( ent->r.svFlags & SVF_CAPSULE ) {
00041         // create a temp capsule from bounding box sizes
00042         return CM_TempBoxModel( ent->r.mins, ent->r.maxs, qtrue );
00043     }
00044 
00045     // create a temp tree from bounding box sizes
00046     return CM_TempBoxModel( ent->r.mins, ent->r.maxs, qfalse );
00047 }
00048 
00049 
00050 
00051 /*
00052 ===============================================================================
00053 
00054 ENTITY CHECKING
00055 
00056 To avoid linearly searching through lists of entities during environment testing,
00057 the world is carved up with an evenly spaced, axially aligned bsp tree.  Entities
00058 are kept in chains either at the final leafs, or at the first node that splits
00059 them, which prevents having to deal with multiple fragments of a single entity.
00060 
00061 ===============================================================================
00062 */
00063 
00064 typedef struct worldSector_s {
00065     int     axis;       // -1 = leaf node
00066     float   dist;
00067     struct worldSector_s    *children[2];
00068     svEntity_t  *entities;
00069 } worldSector_t;
00070 
00071 #define AREA_DEPTH  4
00072 #define AREA_NODES  64
00073 
00074 worldSector_t   sv_worldSectors[AREA_NODES];
00075 int         sv_numworldSectors;
00076 
00077 
00078 /*
00079 ===============
00080 SV_SectorList_f
00081 ===============
00082 */
00083 void SV_SectorList_f( void ) {
00084     int             i, c;
00085     worldSector_t   *sec;
00086     svEntity_t      *ent;
00087 
00088     for ( i = 0 ; i < AREA_NODES ; i++ ) {
00089         sec = &sv_worldSectors[i];
00090 
00091         c = 0;
00092         for ( ent = sec->entities ; ent ; ent = ent->nextEntityInWorldSector ) {
00093             c++;
00094         }
00095         Com_Printf( "sector %i: %i entities\n", i, c );
00096     }
00097 }
00098 
00099 /*
00100 ===============
00101 SV_CreateworldSector
00102 
00103 Builds a uniformly subdivided tree for the given world size
00104 ===============
00105 */
00106 worldSector_t *SV_CreateworldSector( int depth, vec3_t mins, vec3_t maxs ) {
00107     worldSector_t   *anode;
00108     vec3_t      size;
00109     vec3_t      mins1, maxs1, mins2, maxs2;
00110 
00111     anode = &sv_worldSectors[sv_numworldSectors];
00112     sv_numworldSectors++;
00113 
00114     if (depth == AREA_DEPTH) {
00115         anode->axis = -1;
00116         anode->children[0] = anode->children[1] = NULL;
00117         return anode;
00118     }
00119     
00120     VectorSubtract (maxs, mins, size);
00121     if (size[0] > size[1]) {
00122         anode->axis = 0;
00123     } else {
00124         anode->axis = 1;
00125     }
00126 
00127     anode->dist = 0.5 * (maxs[anode->axis] + mins[anode->axis]);
00128     VectorCopy (mins, mins1);   
00129     VectorCopy (mins, mins2);   
00130     VectorCopy (maxs, maxs1);   
00131     VectorCopy (maxs, maxs2);   
00132     
00133     maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
00134     
00135     anode->children[0] = SV_CreateworldSector (depth+1, mins2, maxs2);
00136     anode->children[1] = SV_CreateworldSector (depth+1, mins1, maxs1);
00137 
00138     return anode;
00139 }
00140 
00141 /*
00142 ===============
00143 SV_ClearWorld
00144 
00145 ===============
00146 */
00147 void SV_ClearWorld( void ) {
00148     clipHandle_t    h;
00149     vec3_t          mins, maxs;
00150 
00151     Com_Memset( sv_worldSectors, 0, sizeof(sv_worldSectors) );
00152     sv_numworldSectors = 0;
00153 
00154     // get world map bounds
00155     h = CM_InlineModel( 0 );
00156     CM_ModelBounds( h, mins, maxs );
00157     SV_CreateworldSector( 0, mins, maxs );
00158 }
00159 
00160 
00161 /*
00162 ===============
00163 SV_UnlinkEntity
00164 
00165 ===============
00166 */
00167 void SV_UnlinkEntity( sharedEntity_t *gEnt ) {
00168     svEntity_t      *ent;
00169     svEntity_t      *scan;
00170     worldSector_t   *ws;
00171 
00172     ent = SV_SvEntityForGentity( gEnt );
00173 
00174     gEnt->r.linked = qfalse;
00175 
00176     ws = ent->worldSector;
00177     if ( !ws ) {
00178         return;     // not linked in anywhere
00179     }
00180     ent->worldSector = NULL;
00181 
00182     if ( ws->entities == ent ) {
00183         ws->entities = ent->nextEntityInWorldSector;
00184         return;
00185     }
00186 
00187     for ( scan = ws->entities ; scan ; scan = scan->nextEntityInWorldSector ) {
00188         if ( scan->nextEntityInWorldSector == ent ) {
00189             scan->nextEntityInWorldSector = ent->nextEntityInWorldSector;
00190             return;
00191         }
00192     }
00193 
00194     Com_Printf( "WARNING: SV_UnlinkEntity: not found in worldSector\n" );
00195 }
00196 
00197 
00198 /*
00199 ===============
00200 SV_LinkEntity
00201 
00202 ===============
00203 */
00204 #define MAX_TOTAL_ENT_LEAFS     128
00205 void SV_LinkEntity( sharedEntity_t *gEnt ) {
00206     worldSector_t   *node;
00207     int         leafs[MAX_TOTAL_ENT_LEAFS];
00208     int         cluster;
00209     int         num_leafs;
00210     int         i, j, k;
00211     int         area;
00212     int         lastLeaf;
00213     float       *origin, *angles;
00214     svEntity_t  *ent;
00215 
00216     ent = SV_SvEntityForGentity( gEnt );
00217 
00218     if ( ent->worldSector ) {
00219         SV_UnlinkEntity( gEnt );    // unlink from old position
00220     }
00221 
00222     // encode the size into the entityState_t for client prediction
00223     if ( gEnt->r.bmodel ) {
00224         gEnt->s.solid = SOLID_BMODEL;       // a solid_box will never create this value
00225     } else if ( gEnt->r.contents & ( CONTENTS_SOLID | CONTENTS_BODY ) ) {
00226         // assume that x/y are equal and symetric
00227         i = gEnt->r.maxs[0];
00228         if (i<1)
00229             i = 1;
00230         if (i>255)
00231             i = 255;
00232 
00233         // z is not symetric
00234         j = (-gEnt->r.mins[2]);
00235         if (j<1)
00236             j = 1;
00237         if (j>255)
00238             j = 255;
00239 
00240         // and z maxs can be negative...
00241         k = (gEnt->r.maxs[2]+32);
00242         if (k<1)
00243             k = 1;
00244         if (k>255)
00245             k = 255;
00246 
00247         gEnt->s.solid = (k<<16) | (j<<8) | i;
00248     } else {
00249         gEnt->s.solid = 0;
00250     }
00251 
00252     // get the position
00253     origin = gEnt->r.currentOrigin;
00254     angles = gEnt->r.currentAngles;
00255 
00256     // set the abs box
00257     if ( gEnt->r.bmodel && (angles[0] || angles[1] || angles[2]) ) {
00258         // expand for rotation
00259         float       max;
00260         int         i;
00261 
00262         max = RadiusFromBounds( gEnt->r.mins, gEnt->r.maxs );
00263         for (i=0 ; i<3 ; i++) {
00264             gEnt->r.absmin[i] = origin[i] - max;
00265             gEnt->r.absmax[i] = origin[i] + max;
00266         }
00267     } else {
00268         // normal
00269         VectorAdd (origin, gEnt->r.mins, gEnt->r.absmin);   
00270         VectorAdd (origin, gEnt->r.maxs, gEnt->r.absmax);
00271     }
00272 
00273     // because movement is clipped an epsilon away from an actual edge,
00274     // we must fully check even when bounding boxes don't quite touch
00275     gEnt->r.absmin[0] -= 1;
00276     gEnt->r.absmin[1] -= 1;
00277     gEnt->r.absmin[2] -= 1;
00278     gEnt->r.absmax[0] += 1;
00279     gEnt->r.absmax[1] += 1;
00280     gEnt->r.absmax[2] += 1;
00281 
00282     // link to PVS leafs
00283     ent->numClusters = 0;
00284     ent->lastCluster = 0;
00285     ent->areanum = -1;
00286     ent->areanum2 = -1;
00287 
00288     //get all leafs, including solids
00289     num_leafs = CM_BoxLeafnums( gEnt->r.absmin, gEnt->r.absmax,
00290         leafs, MAX_TOTAL_ENT_LEAFS, &lastLeaf );
00291 
00292     // if none of the leafs were inside the map, the
00293     // entity is outside the world and can be considered unlinked
00294     if ( !num_leafs ) {
00295         return;
00296     }
00297 
00298     // set areas, even from clusters that don't fit in the entity array
00299     for (i=0 ; i<num_leafs ; i++) {
00300         area = CM_LeafArea (leafs[i]);
00301         if (area != -1) {
00302             // doors may legally straggle two areas,
00303             // but nothing should evern need more than that
00304             if (ent->areanum != -1 && ent->areanum != area) {
00305                 if (ent->areanum2 != -1 && ent->areanum2 != area && sv.state == SS_LOADING) {
00306                     Com_DPrintf ("Object %i touching 3 areas at %f %f %f\n",
00307                     gEnt->s.number,
00308                     gEnt->r.absmin[0], gEnt->r.absmin[1], gEnt->r.absmin[2]);
00309                 }
00310                 ent->areanum2 = area;
00311             } else {
00312                 ent->areanum = area;
00313             }
00314         }
00315     }
00316 
00317     // store as many explicit clusters as we can
00318     ent->numClusters = 0;
00319     for (i=0 ; i < num_leafs ; i++) {
00320         cluster = CM_LeafCluster( leafs[i] );
00321         if ( cluster != -1 ) {
00322             ent->clusternums[ent->numClusters++] = cluster;
00323             if ( ent->numClusters == MAX_ENT_CLUSTERS ) {
00324                 break;
00325             }
00326         }
00327     }
00328 
00329     // store off a last cluster if we need to
00330     if ( i != num_leafs ) {
00331         ent->lastCluster = CM_LeafCluster( lastLeaf );
00332     }
00333 
00334     gEnt->r.linkcount++;
00335 
00336     // find the first world sector node that the ent's box crosses
00337     node = sv_worldSectors;
00338     while (1)
00339     {
00340         if (node->axis == -1)
00341             break;
00342         if ( gEnt->r.absmin[node->axis] > node->dist)
00343             node = node->children[0];
00344         else if ( gEnt->r.absmax[node->axis] < node->dist)
00345             node = node->children[1];
00346         else
00347             break;      // crosses the node
00348     }
00349     
00350     // link it in
00351     ent->worldSector = node;
00352     ent->nextEntityInWorldSector = node->entities;
00353     node->entities = ent;
00354 
00355     gEnt->r.linked = qtrue;
00356 }
00357 
00358 /*
00359 ============================================================================
00360 
00361 AREA QUERY
00362 
00363 Fills in a list of all entities who's absmin / absmax intersects the given
00364 bounds.  This does NOT mean that they actually touch in the case of bmodels.
00365 ============================================================================
00366 */
00367 
00368 typedef struct {
00369     const float *mins;
00370     const float *maxs;
00371     int         *list;
00372     int         count, maxcount;
00373 } areaParms_t;
00374 
00375 
00376 /*
00377 ====================
00378 SV_AreaEntities_r
00379 
00380 ====================
00381 */
00382 void SV_AreaEntities_r( worldSector_t *node, areaParms_t *ap ) {
00383     svEntity_t  *check, *next;
00384     sharedEntity_t *gcheck;
00385     int         count;
00386 
00387     count = 0;
00388 
00389     for ( check = node->entities  ; check ; check = next ) {
00390         next = check->nextEntityInWorldSector;
00391 
00392         gcheck = SV_GEntityForSvEntity( check );
00393 
00394         if ( gcheck->r.absmin[0] > ap->maxs[0]
00395         || gcheck->r.absmin[1] > ap->maxs[1]
00396         || gcheck->r.absmin[2] > ap->maxs[2]
00397         || gcheck->r.absmax[0] < ap->mins[0]
00398         || gcheck->r.absmax[1] < ap->mins[1]
00399         || gcheck->r.absmax[2] < ap->mins[2]) {
00400             continue;
00401         }
00402 
00403         if ( ap->count == ap->maxcount ) {
00404             Com_Printf ("SV_AreaEntities: MAXCOUNT\n");
00405             return;
00406         }
00407 
00408         ap->list[ap->count] = check - sv.svEntities;
00409         ap->count++;
00410     }
00411     
00412     if (node->axis == -1) {
00413         return;     // terminal node
00414     }
00415 
00416     // recurse down both sides
00417     if ( ap->maxs[node->axis] > node->dist ) {
00418         SV_AreaEntities_r ( node->children[0], ap );
00419     }
00420     if ( ap->mins[node->axis] < node->dist ) {
00421         SV_AreaEntities_r ( node->children[1], ap );
00422     }
00423 }
00424 
00425 /*
00426 ================
00427 SV_AreaEntities
00428 ================
00429 */
00430 int SV_AreaEntities( const vec3_t mins, const vec3_t maxs, int *entityList, int maxcount ) {
00431     areaParms_t     ap;
00432 
00433     ap.mins = mins;
00434     ap.maxs = maxs;
00435     ap.list = entityList;
00436     ap.count = 0;
00437     ap.maxcount = maxcount;
00438 
00439     SV_AreaEntities_r( sv_worldSectors, &ap );
00440 
00441     return ap.count;
00442 }
00443 
00444 
00445 
00446 //===========================================================================
00447 
00448 
00449 typedef struct {
00450     vec3_t      boxmins, boxmaxs;// enclose the test object along entire move
00451     const float *mins;
00452     const float *maxs;  // size of the moving object
00453     const float *start;
00454     vec3_t      end;
00455     trace_t     trace;
00456     int         passEntityNum;
00457     int         contentmask;
00458     int         capsule;
00459 } moveclip_t;
00460 
00461 
00462 /*
00463 ====================
00464 SV_ClipToEntity
00465 
00466 ====================
00467 */
00468 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 ) {
00469     sharedEntity_t  *touch;
00470     clipHandle_t    clipHandle;
00471     float           *origin, *angles;
00472 
00473     touch = SV_GentityNum( entityNum );
00474 
00475     Com_Memset(trace, 0, sizeof(trace_t));
00476 
00477     // if it doesn't have any brushes of a type we
00478     // are looking for, ignore it
00479     if ( ! ( contentmask & touch->r.contents ) ) {
00480         trace->fraction = 1.0;
00481         return;
00482     }
00483 
00484     // might intersect, so do an exact clip
00485     clipHandle = SV_ClipHandleForEntity (touch);
00486 
00487     origin = touch->r.currentOrigin;
00488     angles = touch->r.currentAngles;
00489 
00490     if ( !touch->r.bmodel ) {
00491         angles = vec3_origin;   // boxes don't rotate
00492     }
00493 
00494     CM_TransformedBoxTrace ( trace, (float *)start, (float *)end,
00495         (float *)mins, (float *)maxs, clipHandle,  contentmask,
00496         origin, angles, capsule);
00497 
00498     if ( trace->fraction < 1 ) {
00499         trace->entityNum = touch->s.number;
00500     }
00501 }
00502 
00503 
00504 /*
00505 ====================
00506 SV_ClipMoveToEntities
00507 
00508 ====================
00509 */
00510 void SV_ClipMoveToEntities( moveclip_t *clip ) {
00511     int         i, num;
00512     int         touchlist[MAX_GENTITIES];
00513     sharedEntity_t *touch;
00514     int         passOwnerNum;
00515     trace_t     trace;
00516     clipHandle_t    clipHandle;
00517     float       *origin, *angles;
00518 
00519     num = SV_AreaEntities( clip->boxmins, clip->boxmaxs, touchlist, MAX_GENTITIES);
00520 
00521     if ( clip->passEntityNum != ENTITYNUM_NONE ) {
00522         passOwnerNum = ( SV_GentityNum( clip->passEntityNum ) )->r.ownerNum;
00523         if ( passOwnerNum == ENTITYNUM_NONE ) {
00524             passOwnerNum = -1;
00525         }
00526     } else {
00527         passOwnerNum = -1;
00528     }
00529 
00530     for ( i=0 ; i<num ; i++ ) {
00531         if ( clip->trace.allsolid ) {
00532             return;
00533         }
00534         touch = SV_GentityNum( touchlist[i] );
00535 
00536         // see if we should ignore this entity
00537         if ( clip->passEntityNum != ENTITYNUM_NONE ) {
00538             if ( touchlist[i] == clip->passEntityNum ) {
00539                 continue;   // don't clip against the pass entity
00540             }
00541             if ( touch->r.ownerNum == clip->passEntityNum ) {
00542                 continue;   // don't clip against own missiles
00543             }
00544             if ( touch->r.ownerNum == passOwnerNum ) {
00545                 continue;   // don't clip against other missiles from our owner
00546             }
00547         }
00548 
00549         // if it doesn't have any brushes of a type we
00550         // are looking for, ignore it
00551         if ( ! ( clip->contentmask & touch->r.contents ) ) {
00552             continue;
00553         }
00554 
00555         // might intersect, so do an exact clip
00556         clipHandle = SV_ClipHandleForEntity (touch);
00557 
00558         origin = touch->r.currentOrigin;
00559         angles = touch->r.currentAngles;
00560 
00561 
00562         if ( !touch->r.bmodel ) {
00563             angles = vec3_origin;   // boxes don't rotate
00564         }
00565 
00566         CM_TransformedBoxTrace ( &trace, (float *)clip->start, (float *)clip->end,
00567             (float *)clip->mins, (float *)clip->maxs, clipHandle,  clip->contentmask,
00568             origin, angles, clip->capsule);
00569 
00570         if ( trace.allsolid ) {
00571             clip->trace.allsolid = qtrue;
00572             trace.entityNum = touch->s.number;
00573         } else if ( trace.startsolid ) {
00574             clip->trace.startsolid = qtrue;
00575             trace.entityNum = touch->s.number;
00576         }
00577 
00578         if ( trace.fraction < clip->trace.fraction ) {
00579             qboolean    oldStart;
00580 
00581             // make sure we keep a startsolid from a previous trace
00582             oldStart = clip->trace.startsolid;
00583 
00584             trace.entityNum = touch->s.number;
00585             clip->trace = trace;
00586             clip->trace.startsolid |= oldStart;
00587         }
00588     }
00589 }
00590 
00591 
00592 /*
00593 ==================
00594 SV_Trace
00595 
00596 Moves the given mins/maxs volume through the world from start to end.
00597 passEntityNum and entities owned by passEntityNum are explicitly not checked.
00598 ==================
00599 */
00600 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 ) {
00601     moveclip_t  clip;
00602     int         i;
00603 
00604     if ( !mins ) {
00605         mins = vec3_origin;
00606     }
00607     if ( !maxs ) {
00608         maxs = vec3_origin;
00609     }
00610 
00611     Com_Memset ( &clip, 0, sizeof ( moveclip_t ) );
00612 
00613     // clip to world
00614     CM_BoxTrace( &clip.trace, start, end, mins, maxs, 0, contentmask, capsule );
00615     clip.trace.entityNum = clip.trace.fraction != 1.0 ? ENTITYNUM_WORLD : ENTITYNUM_NONE;
00616     if ( clip.trace.fraction == 0 ) {
00617         *results = clip.trace;
00618         return;     // blocked immediately by the world
00619     }
00620 
00621     clip.contentmask = contentmask;
00622     clip.start = start;
00623 //  VectorCopy( clip.trace.endpos, clip.end );
00624     VectorCopy( end, clip.end );
00625     clip.mins = mins;
00626     clip.maxs = maxs;
00627     clip.passEntityNum = passEntityNum;
00628     clip.capsule = capsule;
00629 
00630     // create the bounding box of the entire move
00631     // we can limit it to the part of the move not
00632     // already clipped off by the world, which can be
00633     // a significant savings for line of sight and shot traces
00634     for ( i=0 ; i<3 ; i++ ) {
00635         if ( end[i] > start[i] ) {
00636             clip.boxmins[i] = clip.start[i] + clip.mins[i] - 1;
00637             clip.boxmaxs[i] = clip.end[i] + clip.maxs[i] + 1;
00638         } else {
00639             clip.boxmins[i] = clip.end[i] + clip.mins[i] - 1;
00640             clip.boxmaxs[i] = clip.start[i] + clip.maxs[i] + 1;
00641         }
00642     }
00643 
00644     // clip to other solid entities
00645     SV_ClipMoveToEntities ( &clip );
00646 
00647     *results = clip.trace;
00648 }
00649 
00650 
00651 
00652 /*
00653 =============
00654 SV_PointContents
00655 =============
00656 */
00657 int SV_PointContents( const vec3_t p, int passEntityNum ) {
00658     int         touch[MAX_GENTITIES];
00659     sharedEntity_t *hit;
00660     int         i, num;
00661     int         contents, c2;
00662     clipHandle_t    clipHandle;
00663     float       *angles;
00664 
00665     // get base contents from world
00666     contents = CM_PointContents( p, 0 );
00667 
00668     // or in contents from all the other entities
00669     num = SV_AreaEntities( p, p, touch, MAX_GENTITIES );
00670 
00671     for ( i=0 ; i<num ; i++ ) {
00672         if ( touch[i] == passEntityNum ) {
00673             continue;
00674         }
00675         hit = SV_GentityNum( touch[i] );
00676         // might intersect, so do an exact clip
00677         clipHandle = SV_ClipHandleForEntity( hit );
00678         angles = hit->s.angles;
00679         if ( !hit->r.bmodel ) {
00680             angles = vec3_origin;   // boxes don't rotate
00681         }
00682 
00683         c2 = CM_TransformedPointContents (p, clipHandle, hit->s.origin, hit->s.angles);
00684 
00685         contents |= c2;
00686     }
00687 
00688     return contents;
00689 }
00690 
00691 

Generated on Thu Aug 25 12:37:58 2005 for Quake III Arena by  doxygen 1.3.9.1