sv_world.c

Go to the documentation of this file.
00001 
00006 /*
00007 All original material Copyright (C) 2002-2010 UFO: Alien Invasion.
00008 
00009 Original file from Quake 2 v3.21: quake2-2.31/server/sv_world.c
00010 Copyright (C) 1997-2001 Id Software, Inc.
00011 
00012 This program is free software; you can redistribute it and/or
00013 modify it under the terms of the GNU General Public License
00014 as published by the Free Software Foundation; either version 2
00015 of the License, or (at your option) any later version.
00016 
00017 This program is distributed in the hope that it will be useful,
00018 but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
00020 
00021 See the GNU General Public License for more details.
00022 
00023 You should have received a copy of the GNU General Public License
00024 along with this program; if not, write to the Free Software
00025 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00026 
00027 */
00028 
00029 #include "server.h"
00030 
00031 #define AREA_DEPTH  4
00032 
00041 static worldSector_t *SV_CreateWorldSector (int depth, const vec3_t mins, const vec3_t maxs)
00042 {
00043     worldSector_t *anode;
00044     vec3_t size;
00045     vec3_t mins1, maxs1, mins2, maxs2;
00046 
00047     if (sv.numWorldSectors >= lengthof(sv.worldSectors))
00048         Com_Error(ERR_DROP, "SV_CreateWorldSector: overflow");
00049 
00050     anode = &sv.worldSectors[sv.numWorldSectors];
00051     sv.numWorldSectors++;
00052 
00053     anode->entities = NULL;
00054 
00055     if (depth == AREA_DEPTH) {
00056         anode->axis = LEAFNODE; /* end of tree */
00057         anode->children[0] = anode->children[1] = NULL;
00058         return anode;
00059     }
00060 
00061     VectorSubtract(maxs, mins, size);
00062     if (size[0] > size[1])
00063         anode->axis = PLANE_X;
00064     else
00065         anode->axis = PLANE_Y;
00066 
00067     anode->dist = 0.5f * (maxs[anode->axis] + mins[anode->axis]);
00068     VectorCopy(mins, mins1);
00069     VectorCopy(mins, mins2);
00070     VectorCopy(maxs, maxs1);
00071     VectorCopy(maxs, maxs2);
00072 
00073     maxs1[anode->axis] = mins2[anode->axis] = anode->dist;
00074 
00075     anode->children[0] = SV_CreateWorldSector(depth + 1, mins2, maxs2);
00076     anode->children[1] = SV_CreateWorldSector(depth + 1, mins1, maxs1);
00077 
00078     return anode;
00079 }
00080 
00087 void SV_ClearWorld (void)
00088 {
00089     SV_CreateWorldSector(0, sv.mapData.mapMin, sv.mapData.mapMax);
00090 }
00091 
00092 static inline sv_edict_t* SV_GetServerDataForEdict (const edict_t *ent)
00093 {
00094     if (!ent || ent->number < 0 || ent->number >= lengthof(sv.edicts))
00095         Com_Error(ERR_DROP, "SV_GetServerDataForEdict: bad game ent");
00096 
00097     return &sv.edicts[ent->number];
00098 }
00099 
00103 void SV_UnlinkEdict (edict_t * ent)
00104 {
00105     sv_edict_t *sv_ent = SV_GetServerDataForEdict(ent);
00106     sv_edict_t *scan;
00107     worldSector_t *ws;
00108 
00109     sv_ent->linked = qfalse;
00110 
00111     ws = sv_ent->worldSector;
00112     if (!ws)
00113         return;                 /* not linked in anywhere */
00114 
00115     sv_ent->worldSector = NULL;
00116 
00117     if (ws->entities == sv_ent) {
00118         ws->entities = sv_ent->nextEntityInWorldSector;
00119         return;
00120     }
00121 
00122     for (scan = ws->entities; scan; scan = scan->nextEntityInWorldSector) {
00123         if (scan->nextEntityInWorldSector == sv_ent) {
00124             scan->nextEntityInWorldSector = sv_ent->nextEntityInWorldSector;
00125             return;
00126         }
00127     }
00128 
00129     Com_Printf("WARNING: SV_UnlinkEntity: not found in worldSector\n");
00130 
00131     if (ent->child)
00132         SV_UnlinkEdict(ent->child);
00133 }
00134 
00140 void SV_LinkEdict (edict_t * ent)
00141 {
00142     worldSector_t *node;
00143     sv_edict_t *sv_ent = SV_GetServerDataForEdict(ent);
00144 
00145     if (sv_ent->worldSector)
00146         SV_UnlinkEdict(ent);    /* unlink from old position */
00147 
00148     if (ent == svs.ge->edicts)
00149         return;                 /* don't add the world */
00150 
00151     if (!ent->inuse)
00152         return;
00153 
00154     /* set the size */
00155     VectorSubtract(ent->maxs, ent->mins, ent->size);
00156 
00157     /* increase the linkcount - even for none solids */
00158     ent->linkcount++;
00159 
00160     /* expand for rotation */
00161     if (ent->solid == SOLID_BSP && VectorNotEmpty(ent->angles)) {
00162         vec3_t minVec, maxVec, tmpMinVec, tmpMaxVec;
00163         vec3_t centerVec, halfVec, newCenterVec, newHalfVec;
00164         vec3_t m[3];
00165 
00166         /* Find the center of the extents. */
00167         VectorCenterFromMinsMaxs(ent->mins, ent->maxs, centerVec);
00168 
00169         /* Find the half height and half width of the extents. */
00170         VectorSubtract(ent->maxs, centerVec, halfVec);
00171 
00172         /* Rotate the center about the origin. */
00173         VectorCreateRotationMatrix(ent->angles, m);
00174         VectorRotate(m, centerVec, newCenterVec);
00175         VectorRotate(m, halfVec, newHalfVec);
00176 
00177         /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */
00178         VectorSubtract(newCenterVec, newHalfVec, tmpMinVec);
00179         VectorAdd(newCenterVec, newHalfVec, tmpMaxVec);
00180 
00181         /* rotation may have changed min and max of the box, so adjust it */
00182         minVec[0] = min(tmpMinVec[0], tmpMaxVec[0]);
00183         minVec[1] = min(tmpMinVec[1], tmpMaxVec[1]);
00184         minVec[2] = min(tmpMinVec[2], tmpMaxVec[2]);
00185         maxVec[0] = max(tmpMinVec[0], tmpMaxVec[0]);
00186         maxVec[1] = max(tmpMinVec[1], tmpMaxVec[1]);
00187         maxVec[2] = max(tmpMinVec[2], tmpMaxVec[2]);
00188 
00189         /* Adjust the absolute mins/maxs */
00190         VectorAdd(ent->origin, minVec, ent->absmin);
00191         VectorAdd(ent->origin, maxVec, ent->absmax);
00192     } else {  /* normal */
00193         VectorAdd(ent->origin, ent->mins, ent->absmin);
00194         VectorAdd(ent->origin, ent->maxs, ent->absmax);
00195     }
00196 
00197     /* if not solid we have to set the abs mins/maxs above but don't really link it */
00198     if (ent->solid == SOLID_NOT)
00199         return;
00200 
00201     /* find the first node that the ent's box crosses */
00202     node = sv.worldSectors;
00203     while (1) {
00204         /* end of tree */
00205         if (node->axis == LEAFNODE)
00206             break;
00207         if (ent->absmin[node->axis] > node->dist)
00208             node = node->children[0];
00209         else if (ent->absmax[node->axis] < node->dist)
00210             node = node->children[1];
00211         else
00212             break;              /* crosses the node */
00213     }
00214 
00215     /* link it in */
00216     sv_ent->nextEntityInWorldSector = node->entities;
00217     node->entities = sv_ent;
00218 
00219     sv_ent->linked = qtrue;
00220     sv_ent->worldSector = node;
00221     sv_ent->ent = ent;
00222 
00223     /* If this ent has a child, link it back in, too */
00224     if (ent->child) {
00225         VectorCopy(ent->absmin, ent->child->mins);
00226         VectorCopy(ent->absmax, ent->child->maxs);
00227 
00228         /* expand the trigger box */
00229         ent->child->mins[0] -= (UNIT_SIZE / 2);
00230         ent->child->mins[1] -= (UNIT_SIZE / 2);
00231         ent->child->maxs[0] += (UNIT_SIZE / 2);
00232         ent->child->maxs[1] += (UNIT_SIZE / 2);
00233 
00234         /* link child back into the world */
00235         SV_LinkEdict(ent->child);
00236     }
00237 }
00238 
00246 static qboolean SV_BoundingBoxesIntersect (const vec3_t mins, const vec3_t maxs, const edict_t *ent)
00247 {
00248     const qboolean outsideMaxs = ent->absmin[0] > maxs[0] || ent->absmin[1] > maxs[1] || ent->absmin[2] > maxs[2];
00249     const qboolean outsideMins = ent->absmax[0] < mins[0] || ent->absmax[1] < mins[1] || ent->absmax[2] < mins[2];
00250     if (outsideMaxs || outsideMins)
00251         return qfalse; /* not touching */
00252     return qtrue;
00253 }
00254 
00255 typedef struct {
00256     const float *areaMins, *areaMaxs;
00257     edict_t **areaEdictList;
00258     int areaEdictListCount, areaEdictListMaxCount;
00259 } areaParms_t;
00260 
00269 static void SV_AreaEdicts_r (worldSector_t * node, areaParms_t *ap)
00270 {
00271     sv_edict_t *check, *next;
00272 
00273     for (check = node->entities; check; check = next) {
00274         next = check->nextEntityInWorldSector;
00275 
00276         /* deactivated */
00277         if (check->ent->solid == SOLID_NOT)
00278             continue;
00279 
00280         if (!check->ent->inuse)
00281             continue;
00282 
00283         if (!SV_BoundingBoxesIntersect(ap->areaMins, ap->areaMaxs, check->ent))
00284             continue;           /* not touching */
00285 
00286         if (ap->areaEdictListCount == ap->areaEdictListMaxCount) {
00287             Com_Printf("SV_AreaEdicts_r: MAXCOUNT\n");
00288             return;
00289         }
00290 
00291         ap->areaEdictList[ap->areaEdictListCount] = check->ent;
00292         ap->areaEdictListCount++;
00293     }
00294 
00295     if (node->axis == LEAFNODE)
00296         return;                 /* terminal node - end of tree */
00297 
00298     /* recurse down both sides */
00299     if (ap->areaMaxs[node->axis] > node->dist)
00300         SV_AreaEdicts_r(node->children[0], ap);
00301     if (ap->areaMins[node->axis] < node->dist)
00302         SV_AreaEdicts_r(node->children[1], ap);
00303 }
00304 
00313 int SV_AreaEdicts (const vec3_t mins, const vec3_t maxs, edict_t **list, int maxCount)
00314 {
00315     areaParms_t ap;
00316 
00317     ap.areaMins = mins;
00318     ap.areaMaxs = maxs;
00319     ap.areaEdictList = list;
00320     ap.areaEdictListCount = 0;
00321     ap.areaEdictListMaxCount = maxCount;
00322 
00323     SV_AreaEdicts_r(sv.worldSectors, &ap);
00324 
00325     return ap.areaEdictListCount;
00326 }
00327 
00328 
00338 int SV_TouchEdicts (const vec3_t mins, const vec3_t maxs, edict_t **list, int maxCount, edict_t *skip)
00339 {
00340     int num = 0;
00341     const int max = min(maxCount, svs.ge->num_edicts);
00342     int i;
00343 
00344     /* skip the world */
00345     for (i = 1; i < max; i++) {
00346         edict_t *e = EDICT_NUM(i);
00347         /* deactivated */
00348         if (e->solid == SOLID_NOT)
00349             continue;
00350         if (e == skip)
00351             continue;
00352         if (SV_BoundingBoxesIntersect(mins, maxs, e))
00353             list[num++] = e;
00354     }
00355 
00356     return num;
00357 }
00358 
00360 typedef struct {
00361     vec3_t boxmins, boxmaxs;    
00362     const float *mins, *maxs;   
00363     const float *start, *end;
00364     trace_t trace;
00365     const edict_t *passedict;
00366     int contentmask;
00367 } moveclip_t;
00368 
00369 
00381 static int SV_HullForEntity (const edict_t *ent, int *tile, vec3_t rmaShift)
00382 {
00383     assert(ent->solid != SOLID_NOT);
00384     assert(ent->solid != SOLID_TRIGGER);
00385 
00386     /* decide which clipping hull to use, based on the size */
00387     if (ent->solid == SOLID_BSP) {  /* explicit hulls in the BSP model */
00388         const cBspModel_t *model;
00389 
00390         assert(ent->modelindex < MAX_MODELS);
00391 
00392         model = sv.models[ent->modelindex];
00393         if (!model)
00394             Com_Error(ERR_FATAL, "SOLID_BSP with a non bsp model");
00395 
00396         *tile = model->tile;
00397         VectorCopy(model->shift, rmaShift);
00398         assert(model->headnode < MAX_MAP_NODES);
00399         return model->headnode;
00400     }
00401 
00402     /* create a temp hull from bounding box sizes */
00403     *tile = 0;
00404     VectorCopy(vec3_origin, rmaShift);
00405     return CM_HeadnodeForBox(&sv.mapTiles.mapTiles[*tile], ent->mins, ent->maxs);
00406 }
00407 
00408 
00414 static void SV_ClipMoveToEntities (moveclip_t *clip)
00415 {
00416     int i;
00417     edict_t *touchlist[MAX_EDICTS];
00418     trace_t trace;
00419     const float *angles;
00420     int headnode = 0;
00421     const int num = SV_AreaEdicts(clip->boxmins, clip->boxmaxs, touchlist, MAX_EDICTS);
00422 
00423     /* be careful, it is possible to have an entity in this
00424      * list removed before we get to it (killtriggered) */
00425     for (i = 0; i < num; i++) {
00426         vec3_t rmaShift;
00427         edict_t *touch = touchlist[i];
00428         int tile = 0;
00429 
00430         if (touch->solid == SOLID_NOT || touch->solid == SOLID_TRIGGER)
00431             continue;
00432         if (touch == clip->passedict)
00433             continue;
00434 
00435         if (clip->trace.allsolid)
00436             return;
00437 
00438         if (clip->passedict) {
00439             if (touch->owner == clip->passedict)
00440                 continue;       /* don't clip against own missiles */
00441             if (clip->passedict->owner == touch)
00442                 continue;       /* don't clip against owner */
00443         }
00444 
00445         /* might intersect, so do an exact clip */
00446         headnode = SV_HullForEntity(touch, &tile, rmaShift);
00447         if (headnode >= MAX_MAP_NODES)
00448             continue;
00449 
00450         if (touch->solid != SOLID_BSP)
00451             angles = vec3_origin;   /* boxes don't rotate */
00452         else
00453             angles = touch->angles;
00454 
00455         assert(headnode < MAX_MAP_NODES);
00456         trace = CM_HintedTransformedBoxTrace(&sv.mapTiles.mapTiles[tile], clip->start, clip->end, clip->mins, clip->maxs, headnode,
00457                 clip->contentmask, 0, touch->origin, angles, rmaShift, 1.0);
00458 
00459 #ifdef PARANOID
00460         Com_DPrintf(DEBUG_SERVER, "SV_ClipMoveToEntities: %i %i: (%i %i %i) (%i %i %i) (%i %i %i)\n", touch->number, touch->modelindex,
00461             (int)touch->mins[0], (int)touch->mins[1], (int)touch->mins[2],
00462             (int)touch->maxs[0], (int)touch->maxs[1], (int)touch->maxs[2],
00463             (int)touch->origin[0], (int)touch->origin[1], (int)touch->origin[2]);
00464 #endif
00465 
00466         if (trace.fraction < clip->trace.fraction) {
00467             qboolean oldStart;
00468 
00469             /* make sure we keep a startsolid from a previous trace */
00470             oldStart = clip->trace.startsolid;
00471             trace.ent = touch;
00472             clip->trace = trace;
00473             clip->trace.startsolid |= oldStart;
00474         } else if (trace.allsolid) {
00475             trace.ent = touch;
00476             clip->trace = trace;
00477         } else if (trace.startsolid) {
00478             trace.ent = touch;
00479             clip->trace.startsolid = qtrue;
00480         }
00481     }
00482 }
00483 
00491 int SV_PointContents (vec3_t p)
00492 {
00493     /* clip to all world levels */
00494     trace_t trace = CM_CompleteBoxTrace(&sv.mapTiles, p, p, vec3_origin, vec3_origin, TRACING_ALL_VISIBLE_LEVELS, MASK_ALL, 0);
00495     if (trace.fraction == 0)
00496         return trace.contentFlags;      /* blocked by the world */
00497     return 0;
00498 }
00499 
00510 static void SV_TraceBounds (const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, vec3_t boxmins, vec3_t boxmaxs)
00511 {
00512 #if 0
00513     /* debug to test against everything */
00514     boxmins[0] = boxmins[1] = boxmins[2] = -9999;
00515     boxmaxs[0] = boxmaxs[1] = boxmaxs[2] = 9999;
00516 #else
00517     int i;
00518 
00519     for (i = 0; i < 3; i++) {
00520         if (end[i] > start[i]) {
00521             boxmins[i] = start[i] + mins[i] - 1;
00522             boxmaxs[i] = end[i] + maxs[i] + 1;
00523         } else {
00524             boxmins[i] = end[i] + mins[i] - 1;
00525             boxmaxs[i] = start[i] + maxs[i] + 1;
00526         }
00527     }
00528 #endif
00529 }
00530 
00546 trace_t SV_Trace (const vec3_t start, const vec3_t mins, const vec3_t maxs, const vec3_t end, const edict_t * passedict, int contentmask)
00547 {
00548     moveclip_t clip;
00549 
00550     if (!mins)
00551         mins = vec3_origin;
00552     if (!maxs)
00553         maxs = vec3_origin;
00554 
00555     memset(&clip, 0, sizeof(clip));
00556 
00557     /* clip to world - 0x1FF = all levels */
00558     clip.trace = CM_CompleteBoxTrace(&sv.mapTiles, start, end, mins, maxs, TRACING_ALL_VISIBLE_LEVELS, contentmask, 0);
00561     clip.trace.ent = svs.ge->edicts; /* the first edict is the world */
00562     if (clip.trace.fraction == 0)
00563         return clip.trace;      /* blocked by the world */
00564 
00565     clip.contentmask = contentmask;
00566     clip.start = start;
00567     clip.end = end;
00568     clip.mins = mins;
00569     clip.maxs = maxs;
00570     clip.passedict = passedict;
00571 
00572     /* create the bounding box for the entire path traveled by the shot */
00573     SV_TraceBounds(start, clip.mins, clip.maxs, end, clip.boxmins, clip.boxmaxs);
00574 
00575 #if 0
00576     /* Output the trace bounds */
00577     Com_Printf("Trace: (%i, %i, %i) (%i, %i, %i)\n",
00578         (int) clip.boxmins[0], (int) clip.boxmins[1], (int) clip.boxmins[2],
00579         (int) clip.boxmaxs[0], (int) clip.boxmaxs[1], (int) clip.boxmaxs[2]);
00580 #endif
00581 
00582     /* clip to other solid entities */
00583     SV_ClipMoveToEntities(&clip);
00584 
00585     return clip.trace;
00586 }
00587 
00593 const char *SV_GetFootstepSound (const char *texture)
00594 {
00595     const terrainType_t *t = Com_GetTerrainType(texture);
00596     return t ? t->footStepSound : NULL;
00597 }
00598 
00604 float SV_GetBounceFraction (const char *texture)
00605 {
00606     const terrainType_t *t = Com_GetTerrainType(texture);
00607     return t ? t->bounceFraction : 1.0f;
00608 }
00609 
00616 static void SV_ModLoadAliasMD2Model (sv_model_t* mod, const byte *buffer)
00617 {
00618     const dMD2Model_t *md2 = (const dMD2Model_t *)buffer;
00619     const int num_frames = LittleLong(md2->num_frames);
00620     const int frameSize = LittleLong(md2->framesize);
00621     const dMD2Frame_t *frame = (const dMD2Frame_t *) ((const byte *) md2 + LittleLong(md2->ofs_frames) + mod->frame * frameSize);
00622     vec3_t scale, mins, maxs;
00623     int j;
00624 
00625     if (mod->frame > num_frames)
00626         return;
00627 
00628     for (j = 0; j < 3; j++) {
00629         scale[j] = LittleFloat(frame->scale[j]);
00630         mins[j] = LittleFloat(frame->translate[j]);
00631     }
00632 
00633     VectorMA(mins, 255, scale, maxs);
00634     AddPointToBounds(mins, mod->mins, mod->maxs);
00635     AddPointToBounds(maxs, mod->mins, mod->maxs);
00636 }
00637 
00644 static void SV_ModLoadAliasMD3Model (sv_model_t* mod, const byte *buffer)
00645 {
00646     const dmd3_t *md3 = (const dmd3_t *)buffer;
00647     const dmd3frame_t *frame = (const dmd3frame_t *)((const byte *)md3 + LittleLong(md3->ofs_frames));
00648     const int num_frames = LittleLong(md3->num_frames);
00649     vec3_t mins, maxs;
00650     int j;
00651 
00652     if (mod->frame > num_frames)
00653         return;
00654 
00655     frame += mod->frame;
00656     for (j = 0; j < 3; j++) {
00657         mins[j] = LittleFloat(frame->mins[j]);
00658         maxs[j] = LittleFloat(frame->maxs[j]);
00659     }
00660     AddPointToBounds(mins, mod->mins, mod->maxs);
00661     AddPointToBounds(maxs, mod->mins, mod->maxs);
00662 }
00663 
00670 static void SV_ModLoadAliasDPMModel (sv_model_t* mod, const byte *buffer)
00671 {
00672     const dpmheader_t *dpm = (const dpmheader_t *)buffer;
00673     const int num_frames = BigLong(dpm->num_frames);
00674     const int ofs_frames = BigLong(dpm->ofs_frames);
00675     const dpmframe_t *frame = (const dpmframe_t *)((const byte *)dpm + ofs_frames);
00676 
00677     if (mod->frame > num_frames)
00678         return;
00679 
00680     frame += mod->frame;
00681 
00682     mod->mins[0] = BigFloat(frame->mins[0]);
00683     mod->mins[1] = BigFloat(frame->mins[1]);
00684     mod->mins[2] = BigFloat(frame->mins[2]);
00685     mod->maxs[0] = BigFloat(frame->maxs[0]);
00686     mod->maxs[1] = BigFloat(frame->maxs[1]);
00687     mod->maxs[2] = BigFloat(frame->maxs[2]);
00688 }
00689 
00696 static void SV_ModLoadObjModel (sv_model_t* mod, const byte *buffer, int bufferLength)
00697 {
00699 }
00700 
00708 qboolean SV_LoadModelMinsMaxs (const char *model, int frame, vec3_t mins, vec3_t maxs)
00709 {
00710     sv_model_t *mod;
00711     byte *buf;
00712     unsigned int i;
00713     int modfilelen;
00714 
00715     if (model[0] == '\0')
00716         Com_Error(ERR_DROP, "SV_LoadModelMinsMaxs: NULL model");
00717 
00718     /* search the currently loaded models */
00719     for (i = 0, mod = sv.svModels; i < sv.numSVModels; i++, mod++)
00720         if (mod->frame == frame && !strcmp(mod->name, model)) {
00721             VectorCopy(mod->mins, mins);
00722             VectorCopy(mod->maxs, maxs);
00723             return qtrue;
00724         }
00725 
00726     /* find a free model slot spot */
00727     for (i = 0, mod = sv.svModels; i < sv.numSVModels; i++, mod++) {
00728         if (!mod->name)
00729             break;              /* free spot */
00730     }
00731 
00732     if (i == sv.numSVModels) {
00733         if (sv.numSVModels == MAX_MOD_KNOWN)
00734             Com_Error(ERR_DROP, "sv.numSVModels == MAX_MOD_KNOWN");
00735         sv.numSVModels++;
00736     }
00737 
00738     memset(mod, 0, sizeof(*mod));
00739     mod->name = Mem_PoolStrDup(model, com_genericPool, 0);
00740     mod->frame = frame;
00741 
00742     VectorCopy(vec3_origin, mins);
00743     VectorCopy(vec3_origin, maxs);
00744 
00745     /* load the file */
00746     modfilelen = FS_LoadFile(model, &buf);
00747     if (!buf) {
00748         memset(mod->name, 0, sizeof(mod->name));
00749         sv.numSVModels--;
00750         return qfalse;
00751     }
00752 
00753     ClearBounds(mod->mins, mod->maxs);
00754 
00755     /* call the appropriate loader */
00756     switch (LittleLong(*(unsigned *) buf)) {
00757     case IDALIASHEADER:
00758         SV_ModLoadAliasMD2Model(mod, buf);
00759         break;
00760 
00761     case DPMHEADER:
00762         SV_ModLoadAliasDPMModel(mod, buf);
00763         break;
00764 
00765     case IDMD3HEADER:
00766         SV_ModLoadAliasMD3Model(mod, buf);
00767         break;
00768 
00769     default:
00770         if (!Q_strcasecmp(mod->name + strlen(mod->name) - 4, ".obj"))
00771             SV_ModLoadObjModel(mod, buf, modfilelen);
00772         else {
00773             FS_FreeFile(buf);
00774             return qfalse;
00775         }
00776     }
00777 
00778     VectorCopy(mod->mins, mins);
00779     VectorCopy(mod->maxs, maxs);
00780 
00781     FS_FreeFile(buf);
00782     return qtrue;
00783 }

Generated by  doxygen 1.6.2