/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. This file is part of Quake III Arena source code. Quake III Arena source code is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Quake III Arena source code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Quake III Arena source code; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ // cmodel.c -- model loading #include "cm_local.h" #include "../client/client.h" #ifdef BSPC #include "../bspc/l_qfiles.h" void SetPlaneSignbits (cplane_t *out) { int bits, j; // for fast box on planeside test bits = 0; for (j=0 ; j<3 ; j++) { if (out->normal[j] < 0) { bits |= 1<signbits = bits; } #endif //BSPC // to allow boxes to be treated as brush models, we allocate // some extra indexes along with those needed by the map #define BOX_BRUSHES 1 #define BOX_SIDES 6 #define BOX_LEAFS 2 #define BOX_PLANES 12 #define LL(x) x=LittleLong(x) clipMap_t cm; int c_pointcontents; int c_traces, c_brush_traces, c_patch_traces, c_terrain_patch_traces; byte *cmod_base; #ifndef BSPC cvar_t *cm_noAreas; cvar_t *cm_noCurves; cvar_t *cm_playerCurveClip; cvar_t *cm_FCMcacheall; cvar_t *cm_FCMdebug; cvar_t *cm_ter_usesphere; #endif cmodel_t box_model; cplane_t *box_planes; cbrush_t *box_brush; static unsigned int numleafbrushes; static unsigned int g_iNumSideEquations; void CM_InitBoxHull (void); void CM_FloodAreaConnections (void); /* =============================================================================== MAP LOADING =============================================================================== */ /* ================= CMod_LoadShaders ================= */ void CMod_LoadShaders( gamelump_t *l, int **shaderSubdivisions ) { dshader_t *in; cshader_t *out; obfuscation_t *obfuscationList; int i, count; *shaderSubdivisions = NULL; in = ( dshader_t * )l->buffer; if ( l->length % sizeof( dshader_t ) ) { Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); } count = l->length / sizeof( dshader_t ); if (count < 1) { Com_Error (ERR_DROP, "Map with no shaders"); } cm.shaders = Hunk_Alloc( count * sizeof( *cm.shaders ), h_dontcare); cm.numShaders = count; cm.fencemasks = NULL; *shaderSubdivisions = Hunk_AllocateTempMemory( count * sizeof( *shaderSubdivisions ) ); obfuscationList = CM_SetupObfuscationMapping(); out = cm.shaders; for ( i=0 ; ishader, in->shader, sizeof( out->shader ) ); out->contentFlags = LittleLong( in->contentFlags ); out->surfaceFlags = LittleLong( in->surfaceFlags ); out->mask = CM_GetFenceMask( in->fenceMaskImage ); out->obfuscationWidthDensity = 0; if (out->contentFlags & CONTENTS_DONOTENTER) { CM_ObfuscationForShader(obfuscationList, out->shader, &out->obfuscationWidthDensity, &out->obfuscationHeightDensity); } ( *shaderSubdivisions )[ i ] = LittleLong( in->subdivisions ); } CM_ReleaseObfuscationMapping(obfuscationList); } /* ================= CMod_LoadSubmodels ================= */ void CMod_LoadSubmodels( gamelump_t *l ) { dmodel_t *in; cmodel_t *out; int i, j, count; int *indexes; in = ( dmodel_t * )l->buffer; if (l->length % sizeof(*in)) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); count = l->length / sizeof(*in); if (count < 1) Com_Error (ERR_DROP, "Map with no models"); cm.cmodels = Hunk_Alloc( count * sizeof( *cm.cmodels ), h_dontcare); cm.numSubModels = count; if ( count > MAX_SUBMODELS ) { Com_Error( ERR_DROP, "MAX_SUBMODELS exceeded" ); } for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; out->maxs[j] = LittleFloat (in->maxs[j]) + 1; } if ( i == 0 ) { continue; // world model doesn't need other info } // make a "leaf" just to hold the model's brushes and surfaces out->leaf.numLeafBrushes = LittleLong( in->numBrushes ); if( out->leaf.numLeafBrushes ) { indexes = Hunk_Alloc( out->leaf.numLeafBrushes * 4, h_dontcare); out->leaf.firstLeafBrush = indexes - cm.leafbrushes; for( j = 0; j < out->leaf.numLeafBrushes; j++ ) { indexes[ j ] = LittleLong( in->firstBrush ) + j; } } else { out->leaf.firstLeafBrush = -1; } out->leaf.numLeafSurfaces = LittleLong( in->numSurfaces ); if( out->leaf.numLeafSurfaces ) { indexes = Hunk_Alloc( out->leaf.numLeafSurfaces * 4, h_dontcare); out->leaf.firstLeafSurface = indexes - cm.leafsurfaces; for( j = 0; j < out->leaf.numLeafSurfaces; j++ ) { indexes[ j ] = LittleLong( in->firstSurface ) + j; } } else { out->leaf.numLeafSurfaces = -1; } } } /* ================= CMod_LoadNodes ================= */ void CMod_LoadNodes( gamelump_t *l ) { dnode_t *in; int child; cNode_t *out; int i, j, count; in = ( dnode_t * )l->buffer; if (l->length % sizeof(*in)) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); count = l->length / sizeof(*in); if (count < 1) Com_Error (ERR_DROP, "Map has no nodes"); cm.nodes = Hunk_Alloc( count * sizeof( *cm.nodes ), h_dontcare); cm.numNodes = count; out = cm.nodes; for (i=0 ; iplane = cm.planes + LittleLong( in->planeNum ); for (j=0 ; j<2 ; j++) { child = LittleLong (in->children[j]); out->children[j] = child; } } } /* ================= CM_BoundBrush ================= */ void CM_BoundBrush( cbrush_t *b ) { b->bounds[0][0] = -b->sides[0].plane->dist; b->bounds[1][0] = b->sides[1].plane->dist; b->bounds[0][1] = -b->sides[2].plane->dist; b->bounds[1][1] = b->sides[3].plane->dist; b->bounds[0][2] = -b->sides[4].plane->dist; b->bounds[1][2] = b->sides[5].plane->dist; } /* ================= CMod_LoadBrushes ================= */ void CMod_LoadBrushes( gamelump_t *l ) { dbrush_t *in; cbrush_t *out; int i, count; in = ( dbrush_t * )l->buffer; if (l->length % sizeof(*in)) { Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); } count = l->length / sizeof(*in); cm.brushes = Hunk_Alloc( ( BOX_BRUSHES + count ) * sizeof( *cm.brushes ), h_dontcare); cm.numBrushes = count; out = cm.brushes; for ( i=0 ; isides = cm.brushsides + LittleLong(in->firstSide); out->numsides = LittleLong(in->numSides); out->shaderNum = LittleLong( in->shaderNum ); if ( out->shaderNum < 0 || out->shaderNum >= cm.numShaders ) { Com_Error( ERR_DROP, "CMod_LoadBrushes: bad shaderNum: %i", out->shaderNum ); } out->contents = cm.shaders[out->shaderNum].contentFlags; CM_BoundBrush( out ); } } /* ================= CMod_LoadLeafs ================= */ void CMod_LoadLeafs( gamelump_t *l ) { int i; cLeaf_t *out; dleaf_t *in; int count; in = ( dleaf_t * )l->buffer; if( l->length % sizeof( *in ) ) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); count = l->length / sizeof(*in); if (count < 1) Com_Error (ERR_DROP, "Map with no leafs"); cm.leafs = Hunk_Alloc( ( BOX_LEAFS + count ) * sizeof( *cm.leafs ), h_dontcare); cm.numLeafs = count; out = cm.leafs; for( i = 0; icluster = LittleLong( in->cluster ); out->area = LittleLong( in->area ); out->firstLeafBrush = LittleLong( in->firstLeafBrush ); out->numLeafBrushes = LittleLong( in->numLeafBrushes ); out->firstLeafSurface = LittleLong( in->firstLeafSurface ); out->numLeafSurfaces = LittleLong( in->numLeafSurfaces ); out->firstLeafTerrain = LittleLong( in->firstTerraPatch ); out->numLeafTerrains = LittleLong( in->numTerraPatches ); if( out->cluster >= cm.numClusters ) cm.numClusters = out->cluster + 1; if( out->area >= cm.numAreas ) cm.numAreas = out->area + 1; } cm.areas = Hunk_Alloc( cm.numAreas * sizeof( *cm.areas ), h_dontcare); cm.areaPortals = Hunk_Alloc( cm.numAreas * cm.numAreas * sizeof( *cm.areaPortals ), h_dontcare); } /* ================= CMod_LoadLeafsOld ================= */ void CMod_LoadLeafsOld( gamelump_t *l ) { int i; cLeaf_t *out; dleaf_t_ver17 *in; int count; in = ( dleaf_t_ver17 * )l->buffer; if( l->length % sizeof( *in ) ) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); count = l->length / sizeof( *in ); if( count < 1 ) Com_Error( ERR_DROP, "Map with no leafs" ); cm.leafs = Hunk_Alloc( ( BOX_LEAFS + count ) * sizeof( *cm.leafs ), h_dontcare); cm.numLeafs = count; out = cm.leafs; for( i = 0; icluster = LittleLong( in->cluster ); out->area = LittleLong( in->area ); out->firstLeafBrush = LittleLong( in->firstLeafBrush ); out->numLeafBrushes = LittleLong( in->numLeafBrushes ); out->firstLeafSurface = LittleLong( in->firstLeafSurface ); out->numLeafSurfaces = LittleLong( in->numLeafSurfaces ); if( out->cluster >= cm.numClusters ) cm.numClusters = out->cluster + 1; if( out->area >= cm.numAreas ) cm.numAreas = out->area + 1; } cm.areas = Hunk_Alloc( cm.numAreas * sizeof( *cm.areas ), h_dontcare); cm.areaPortals = Hunk_Alloc( cm.numAreas * cm.numAreas * sizeof( *cm.areaPortals ), h_dontcare); } /* ================= CMod_LoadPlanes ================= */ void CMod_LoadPlanes( gamelump_t *l ) { int i, j; cplane_t *out; dplane_t *in; int count; int bits; in = ( dplane_t * )l->buffer; if( l->length % sizeof( *in ) ) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); count = l->length / sizeof( *in ); if (count < 1) Com_Error (ERR_DROP, "Map with no planes"); cm.planes = Hunk_Alloc( ( BOX_PLANES + count ) * sizeof( *cm.planes ), h_dontcare); cm.numPlanes = count; out = cm.planes; for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<dist = LittleFloat (in->dist); out->type = PlaneTypeForNormal( out->normal ); out->signbits = bits; } } /* ================= CMod_LoadLeafBrushes ================= */ void CMod_LoadLeafBrushes( gamelump_t *l ) { int i; int *out; int *in; int count; in = ( int * )l->buffer; if( l->length % sizeof( *in ) ) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); count = l->length / sizeof(*in); cm.leafbrushes = Hunk_Alloc( (count + BOX_BRUSHES) * sizeof( *cm.leafbrushes ), h_dontcare); cm.numLeafBrushes = count; out = cm.leafbrushes; for ( i=0 ; ibuffer; if( l->length % sizeof( *in ) ) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); count = l->length / sizeof(*in); cm.leafsurfaces = Hunk_Alloc( count * sizeof( *cm.leafsurfaces ), h_dontcare); cm.numLeafSurfaces = count; out = cm.leafsurfaces; for ( i=0 ; ibuffer; if( l->length % sizeof( *in ) ) { Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); } count = l->length / sizeof( *in ); if( !count ) { cm.numSideEquations = 0; cm.sideequations = NULL; return; } cm.sideequations = Hunk_Alloc( count * sizeof( *cm.sideequations ), h_dontcare); cm.numSideEquations = count; out = cm.sideequations; for( i = 0; ifSeq[ j ] = LittleFloat( in->fSeq[ j ] ); out->fTeq[ j ] = LittleFloat( in->fTeq[ j ] ); } } } /* ================= CMod_LoadBrushSides ================= */ void CMod_LoadBrushSides( gamelump_t *l ) { int i; cbrushside_t *out; dbrushside_t *in; int count; int num; in = ( dbrushside_t * )l->buffer; if( l->length % sizeof( *in ) ) { Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); } count = l->length / sizeof(*in); cm.brushsides = Hunk_Alloc( ( BOX_SIDES + count ) * sizeof( *cm.brushsides ), h_dontcare); cm.numBrushSides = count; out = cm.brushsides; for ( i=0 ; iplaneNum ); out->plane = &cm.planes[num]; out->shaderNum = LittleLong( in->shaderNum ); if ( out->shaderNum < 0 || out->shaderNum >= cm.numShaders ) { Com_Error( ERR_DROP, "CMod_LoadBrushSides: bad shaderNum: %i", out->shaderNum ); } out->surfaceFlags = cm.shaders[out->shaderNum].surfaceFlags; num = LittleLong( in->equationNum ); if( num ) { out->pEq = &cm.sideequations[ num ]; } else { out->pEq = NULL; } } } /* ================= CMod_LoadEntityString ================= */ void CMod_LoadEntityString( gamelump_t *l ) { cm.entityString = Hunk_Alloc( l->length, h_dontcare); cm.numEntityChars = l->length; Com_Memcpy( cm.entityString, l->buffer, l->length ); } /* ================= CMod_LoadVisibility ================= */ #define VIS_HEADER 8 void CMod_LoadVisibility( gamelump_t *l ) { int len; byte *buf; len = l->length; if ( !len ) { cm.clusterBytes = ( cm.numClusters + 31 ) & ~31; cm.visibility = Hunk_Alloc( cm.clusterBytes, h_dontcare); Com_Memset( cm.visibility, 255, cm.clusterBytes ); return; } buf = ( byte * )l->buffer; cm.vised = qtrue; cm.visibility = Hunk_Alloc( len, h_dontcare); cm.numClusters = LittleLong( ( ( int * )buf )[ 0 ] ); cm.clusterBytes = LittleLong( ( ( int * )buf )[ 1 ] ); Com_Memcpy( cm.visibility, buf + VIS_HEADER, len - VIS_HEADER ); } //================================================================== /* ================= CMod_LoadPatches ================= */ #define MAX_PATCH_VERTS 1024 void CMod_LoadPatches( gamelump_t *surfs, gamelump_t *verts, int *shaderSubdivisions ) { drawVert_t *dv, *dv_p; dsurface_t *in; int count; int i, j; int c; cPatch_t *patch; vec3_t points[MAX_PATCH_VERTS]; int width, height; int shaderNum; char tempName[MAX_QPATH + 1]; in = ( dsurface_t * )surfs->buffer; if( surfs->length % sizeof( *in ) ) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); cm.numSurfaces = count = surfs->length / sizeof(*in); cm.surfaces = Hunk_Alloc( cm.numSurfaces * sizeof( cm.surfaces[0] ), h_dontcare); memset( cm.surfaces, 0, cm.numSurfaces * sizeof( cm.surfaces[ 0 ] ) ); dv = ( drawVert_t * )verts->buffer; if( verts->length % sizeof( *dv ) ) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); // scan through all the surfaces, but only load patches, // not planar faces for ( i = 0 ; i < count ; i++, in++ ) { if ( LittleLong( in->surfaceType ) != MST_PATCH ) { assert(cm.surfaces[ i ] == 0); continue; // ignore other surfaces } shaderNum = LittleLong( in->shaderNum ); if( !( cm.shaders[ shaderNum ].contentFlags & CONTENTS_SOLID ) && !( cm.shaders[ shaderNum ].contentFlags & MASK_CLICK ) ) { cm.surfaces[ i ] = NULL; continue; } cm.surfaces[ i ] = patch = Hunk_Alloc( sizeof( *patch ), h_dontcare); assert( cm.surfaces[ i ] != ( cPatch_t * )0 ); assert( cm.surfaces[ i ] != ( cPatch_t * )0x4 ); // load the full drawverts onto the stack width = LittleLong( in->patchWidth ); height = LittleLong( in->patchHeight ); c = width * height; if ( c > MAX_PATCH_VERTS ) { Com_Error( ERR_DROP, "ParseMesh: MAX_PATCH_VERTS" ); } dv_p = dv + LittleLong( in->firstVert ); for ( j = 0 ; j < c ; j++, dv_p++ ) { points[j][0] = LittleFloat( dv_p->xyz[0] ); points[j][1] = LittleFloat( dv_p->xyz[1] ); points[j][2] = LittleFloat( dv_p->xyz[2] ); } shaderNum = LittleLong( in->shaderNum ); patch->shaderNum = shaderNum; patch->contents = cm.shaders[ shaderNum ].contentFlags; patch->surfaceFlags = cm.shaders[ shaderNum ].surfaceFlags; patch->subdivisions = shaderSubdivisions[ shaderNum ]; if( patch->subdivisions < MIN_MAP_SUBDIVISIONS ) { patch->subdivisions = MIN_MAP_SUBDIVISIONS; } // create the internal facet structure patch->pc = CM_GeneratePatchCollide( width, height, points, patch->subdivisions ); Com_sprintf(tempName, sizeof(tempName), "s%d", i); UI_LoadResource(tempName); } Hunk_FreeTempMemory( shaderSubdivisions ); } /* ================= CMod_LoadTerrain ================= */ void CMod_LoadTerrain( gamelump_t *lump ) { int numTerraPatches; int i; cTerraPatch_t *terraPatches; if( !lump->length ) return; terraPatches = ( cTerraPatch_t * )lump->buffer; if( lump->length % sizeof( *terraPatches ) ) Com_Error( ERR_DROP, "CM_LoadMap: funny lump size in %s", cm.name ); numTerraPatches = lump->length / sizeof( *terraPatches ); cm.numTerrain = numTerraPatches; cm.terrain = ( cTerrain_t * )Hunk_Alloc( cm.numTerrain * sizeof( cm.terrain[ 0 ] ), h_dontcare); // Prepare collision CM_PrepareGenerateTerrainCollide(); for( i = 0; i < numTerraPatches; i++ ) { CM_SwapTerraPatch(&terraPatches[i]); // Generate collision CM_GenerateTerrainCollide( &terraPatches[ i ], &cm.terrain[ i ].tc ); cm.terrain[ i ].contents = cm.shaders[ terraPatches[ i ].iShader ].contentFlags; cm.terrain[ i ].surfaceFlags = cm.shaders[ terraPatches[ i ].iShader ].surfaceFlags; cm.terrain[ i ].shaderNum = terraPatches[ i ].iShader; } } /* ================= CMod_LoadTerrainIndexes ================= */ void CMod_LoadTerrainIndexes( gamelump_t *lump ) { int i; short *in; if( !lump->length ) { cm.numLeafTerrains = 0; cm.leafterrains = NULL; return; } if( lump->length % sizeof( short ) ) { Com_Error( ERR_DROP, "CMod_LoadTerrainIndexes: funny lump size" ); } cm.numLeafTerrains = lump->length / sizeof( short ); cm.leafterrains = ( cTerrain_t ** )Hunk_Alloc( cm.numLeafTerrains * sizeof( cTerrain_t * ), h_dontcare); in = ( short * )lump->buffer; for( i = 0; i < cm.numLeafTerrains; i++, in++ ) { cm.leafterrains[ i ] = &cm.terrain[ LittleShort( *in ) ]; } } //================================================================== unsigned CM_Checksum( dheader_t *header ) { /* unsigned checksums[16]; checksums[0] = CM_LumpChecksum(&header->lumps[LUMP_SHADERS]); checksums[1] = CM_LumpChecksum(&header->lumps[LUMP_LEAFS]); checksums[2] = CM_LumpChecksum(&header->lumps[LUMP_LEAFBRUSHES]); checksums[3] = CM_LumpChecksum(&header->lumps[LUMP_LEAFSURFACES]); checksums[4] = CM_LumpChecksum(&header->lumps[LUMP_PLANES]); checksums[5] = CM_LumpChecksum(&header->lumps[LUMP_BRUSHSIDES]); checksums[6] = CM_LumpChecksum(&header->lumps[LUMP_BRUSHES]); checksums[7] = CM_LumpChecksum(&header->lumps[LUMP_MODELS]); checksums[8] = CM_LumpChecksum(&header->lumps[LUMP_NODES]); checksums[9] = CM_LumpChecksum(&header->lumps[LUMP_SURFACES]); checksums[10] = CM_LumpChecksum(&header->lumps[LUMP_DRAWVERTS]); return LittleLong(Com_BlockChecksum(checksums, 11 * 4)); */ return LittleLong(header->checksum); } /* ================== CM_LoadLump Loads a lump from the BSP file ================== */ int CM_LoadLump( fileHandle_t handle, lump_t *lump, gamelump_t *glump, int size ) { glump->buffer = NULL; glump->length = lump->filelen; if( lump->filelen ) { glump->buffer = Hunk_AllocateTempMemory( lump->filelen ); if( FS_Seek( handle, lump->fileofs, FS_SEEK_SET ) < 0 ) { Com_Error( ERR_DROP, "CM_LoadLump: Error seeking to lump." ); } FS_Read( glump->buffer, lump->filelen, handle ); if( size ) { return lump->filelen / size; } } return 0; } /* ================== CM_FreeLump Free a previously allocated lump ================== */ void CM_FreeLump( gamelump_t *lump ) { if( lump->buffer ) { Hunk_FreeTempMemory( lump->buffer ); lump->buffer = NULL; lump->length = 0; } } #define _R( id ) UI_LoadResource( "*" #id ) /* ================== CM_LoadMap Loads in the map and all submodels ================== */ void CM_LoadMap( const char *name, qboolean clientload, int *checksum ) { gamelump_t lump, lump2; int *shaderSubdivisions; int i; dheader_t header; int length; fileHandle_t h; static unsigned last_checksum; if ( !name || !name[0] ) { Com_Error( ERR_DROP, "CM_LoadMap: NULL name" ); } #ifndef BSPC cm_noAreas = Cvar_Get( "cm_noAreas", "0", CVAR_CHEAT ); cm_noCurves = Cvar_Get( "cm_noCurves", "0", CVAR_CHEAT ); cm_playerCurveClip = Cvar_Get( "cm_playerCurveClip", "1", CVAR_ARCHIVE | CVAR_CHEAT ); cm_FCMcacheall = Cvar_Get( "cm_FCMcacheall", "0", CVAR_CHEAT ); cm_FCMdebug = Cvar_Get( "cm_FCMdebug", "0", CVAR_CHEAT ); cm_ter_usesphere = Cvar_Get( "cm_ter_usesphere", "1", CVAR_CHEAT ); #endif Com_DPrintf( "CM_LoadMap( %s, %i )\n", name, clientload ); if ( !strcmp( cm.name, name ) && clientload ) { *checksum = last_checksum; return; } // free old stuff Com_Memset( &cm, 0, sizeof( cm ) ); CM_ClearLevelPatches(); if ( !name[0] ) { cm.numLeafs = 1; cm.numClusters = 1; cm.numAreas = 1; cm.cmodels = Hunk_Alloc( sizeof( *cm.cmodels ), h_dontcare); *checksum = 0; return; } // clear aliases Alias_Clear(); // // load the file // #ifndef BSPC length = FS_FOpenFileRead( name, &h, qtrue, qtrue ); #else length = LoadQuakeFile((quakefile_t *) name, (void **)&buf); #endif if( length <= 0 ) { Com_Error( ERR_DROP, "Couldn't load %s", name ); } FS_Read( &header, sizeof( dheader_t ), h ); last_checksum = LittleLong(header.checksum); *checksum = last_checksum; for (i=0 ; i BSP_MAX_VERSION ) { Com_Error (ERR_DROP, "CM_LoadMap: %s has wrong version number (%i should be between %i and %i)" , name, header.version, BSP_MIN_VERSION, BSP_MAX_VERSION ); } cmod_base = ( byte * )&header; // load into heap _R( 0 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_SHADERS ), &lump, 0); _R( 1 ); CMod_LoadShaders( &lump, &shaderSubdivisions ); _R( 2 ); CM_FreeLump( &lump ); _R( 3 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_PLANES ), &lump, 0 ); _R( 4 ); CMod_LoadPlanes( &lump ); _R( 5 ); CM_FreeLump( &lump ); _R( 6 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_SURFACES ), &lump, 0 ); _R( 7 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_DRAWVERTS ), &lump2, 0 ); _R( 8 ); CMod_LoadPatches( &lump, &lump2, shaderSubdivisions ); _R( 9 ); CM_FreeLump( &lump2 ); _R( 10 ); CM_FreeLump( &lump ); _R( 11 ); cm.numLeafBrushes = CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_LEAFBRUSHES ), &lump, sizeof( int ) ); _R( 12 ); CMod_LoadLeafBrushes( &lump ); _R( 13 ); CM_FreeLump( &lump ); _R( 14 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_LEAFSURFACES ), &lump, 0 ); _R( 15 ); CMod_LoadLeafSurfaces( &lump ); _R( 16 ); CM_FreeLump( &lump ); _R( 17 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_LEAFS ), &lump, 0 ); _R( 18 ); if( header.version >= 18 ) { CMod_LoadLeafs( &lump ); } else { CMod_LoadLeafsOld( &lump ); } _R( 19 ); CM_FreeLump( &lump ); _R( 20 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_NODES ), &lump, 0 ); _R( 21 ); CMod_LoadNodes( &lump ); _R( 22 ); CM_FreeLump( &lump ); _R( 23 ); g_iNumSideEquations = CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_SIDEEQUATIONS ), &lump, 32 ); _R( 24 ); CMod_LoadSideEquations( &lump ); _R( 25 ); CM_FreeLump( &lump ); _R( 26 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_BRUSHSIDES ), &lump, 0 ); _R( 27 ); CMod_LoadBrushSides( &lump ); _R( 28 ); CM_FreeLump( &lump ); _R( 29 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_BRUSHES ), &lump, 0 ); _R( 30 ); CMod_LoadBrushes( &lump ); _R( 31 ); CM_FreeLump( &lump ); _R( 32 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_MODELS ), &lump, 0 ); _R( 33 ); CMod_LoadSubmodels( &lump ); _R( 34 ); CM_FreeLump( &lump ); _R( 35 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_ENTITIES ), &lump, 0 ); _R( 36 ); CMod_LoadEntityString( &lump ); _R( 37 ); CM_FreeLump( &lump ); _R( 38 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_VISIBILITY ), &lump, 0 ); _R( 39 ); CMod_LoadVisibility( &lump ); _R( 40 ); CM_FreeLump( &lump ); _R( 41 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_TERRAIN ), &lump, 0 ); _R( 42 ); CMod_LoadTerrain( &lump ); _R( 43 ); CM_FreeLump( &lump ); _R( 44 ); CM_LoadLump( h, Q_GetLumpByVersion( &header, LUMP_TERRAININDEXES ), &lump, 0 ); _R( 45 ); CMod_LoadTerrainIndexes( &lump ); _R( 46 ); CM_FreeLump( &lump ); _R( 47 ); FS_FCloseFile( h ); _R( 48 ); CM_InitBoxHull(); _R( 49 ); CM_FloodAreaConnections(); _R( 50 ); // allow this to be cached if it is loaded by the server if ( !clientload ) { Q_strncpyz( cm.name, name, sizeof( cm.name ) ); } _R( 51 ); } /* ================== CM_ClearMap ================== */ void CM_ClearMap( void ) { Com_Memset( &cm, 0, sizeof( cm ) ); CM_ClearLevelPatches(); } /* ================== CM_ClipHandleToModel ================== */ cmodel_t *CM_ClipHandleToModel( clipHandle_t handle ) { if ( handle < 0 ) { Com_Error( ERR_DROP, "CM_ClipHandleToModel: bad handle %i", handle ); } if ( handle < cm.numSubModels ) { return &cm.cmodels[handle]; } if ( handle == BOX_MODEL_HANDLE ) { return &box_model; } if ( handle < MAX_SUBMODELS ) { Com_Error( ERR_DROP, "CM_ClipHandleToModel: bad handle %i < %i < %i", cm.numSubModels, handle, MAX_SUBMODELS ); } Com_Error( ERR_DROP, "CM_ClipHandleToModel: bad handle %i", handle + MAX_SUBMODELS ); return NULL; } /* ================== CM_InlineModel ================== */ clipHandle_t CM_InlineModel( int index ) { if ( index < 0 || index >= cm.numSubModels ) { Com_Error (ERR_DROP, "CM_InlineModel: bad number"); } return index; } int CM_NumClusters( void ) { return cm.numClusters; } int CM_NumInlineModels( void ) { return cm.numSubModels; } char *CM_EntityString( void ) { return cm.entityString; } const char *CM_MapTime( void ) { const char *com_token; char *data; data = CM_EntityString(); if( !data || *data != '{' ) { return NULL; } while( 1 ) { com_token = COM_Parse( &data ); if( !data || *com_token == '}' ) { break; } if( !strcmp( com_token, "map_time" ) ) { com_token = COM_Parse( &data ); if( com_token && *com_token != '}' ) { return com_token; } else { return ""; } } } return ""; } int CM_LeafCluster( int leafnum ) { if (leafnum < 0 || leafnum >= cm.numLeafs) { Com_Error (ERR_DROP, "CM_LeafCluster: bad number"); } return cm.leafs[leafnum].cluster; } int CM_LeafArea( int leafnum ) { if ( leafnum < 0 || leafnum >= cm.numLeafs ) { Com_Error (ERR_DROP, "CM_LeafArea: bad number"); } return cm.leafs[leafnum].area; } //======================================================================= /* =================== CM_InitBoxHull Set up the planes and nodes so that the six floats of a bounding box can just be stored out and get a proper clipping hull structure. =================== */ void CM_InitBoxHull (void) { int i; int side; cplane_t *p; cbrushside_t *s; box_planes = &cm.planes[cm.numPlanes]; box_brush = &cm.brushes[cm.numBrushes]; box_brush->numsides = 6; box_brush->sides = cm.brushsides + cm.numBrushSides; box_brush->contents = CONTENTS_BBOX; box_model.leaf.numLeafBrushes = 1; // box_model.leaf.firstLeafBrush = cm.numBrushes; box_model.leaf.firstLeafBrush = cm.numLeafBrushes; cm.leafbrushes[cm.numLeafBrushes] = cm.numBrushes; for (i=0 ; i<6 ; i++) { side = i&1; // brush sides s = &cm.brushsides[cm.numBrushSides+i]; s->plane = cm.planes + (cm.numPlanes+i*2+side); s->surfaceFlags = 0; // planes p = &box_planes[i*2]; p->type = i>>1; p->signbits = 0; VectorClear (p->normal); p->normal[i>>1] = 1; p = &box_planes[i*2+1]; p->type = 3 + (i>>1); p->signbits = 0; VectorClear (p->normal); p->normal[i>>1] = -1; SetPlaneSignbits( p ); } } /* =================== CM_TempBoxModel To keep everything totally uniform, bounding boxes are turned into small BSP trees instead of being compared directly. Capsules are handled differently though. =================== */ clipHandle_t CM_TempBoxModel( const vec3_t mins, const vec3_t maxs, int contents ) { box_planes[0].dist = maxs[0]; box_planes[1].dist = -maxs[0]; box_planes[2].dist = mins[0]; box_planes[3].dist = -mins[0]; box_planes[4].dist = maxs[1]; box_planes[5].dist = -maxs[1]; box_planes[6].dist = mins[1]; box_planes[7].dist = -mins[1]; box_planes[8].dist = maxs[2]; box_planes[9].dist = -maxs[2]; box_planes[10].dist = mins[2]; box_planes[11].dist = -mins[2]; VectorCopy( mins, box_brush->bounds[0] ); VectorCopy( maxs, box_brush->bounds[1] ); box_brush->contents = contents; return BOX_MODEL_HANDLE; } /* =================== CM_ModelBounds =================== */ void CM_ModelBounds( clipHandle_t model, vec3_t mins, vec3_t maxs ) { cmodel_t *cmod; cmod = CM_ClipHandleToModel( model ); VectorCopy( cmod->mins, mins ); VectorCopy( cmod->maxs, maxs ); } /* =================== CM_ModelBoundsFromName =================== */ void CM_ModelBoundsFromName( const char *name, vec3_t mins, vec3_t maxs ) { clipHandle_t h; if( !name ) { Com_Error( ERR_DROP, "SV_SetBrushModel: NULL" ); } if( *name != '*' ) { Com_Error( ERR_DROP, "SV_SetBrushModel: %s isn't a brush model", name ); } h = CM_InlineModel( atoi( name + 1 ) ); CM_ModelBounds( h, mins, maxs ); } /* =================== CM_VisibilityPointer =================== */ byte *CM_VisibilityPointer( void ) { if( cm.vised ) { return cm.visibility; } else { return NULL; } } /* =================== CM_PrintBSPFileSizes =================== */ void CM_PrintBSPFileSizes( void ) { Com_Printf("%6u sideequations %7zu\n", g_iNumSideEquations, g_iNumSideEquations * sizeof(dsideequation_t)); Com_Printf("%6u leafbrushes %7zu\n", numleafbrushes, numleafbrushes * sizeof(int)); }