diff options
Diffstat (limited to 'source/cmodel.c')
-rw-r--r-- | source/cmodel.c | 2065 |
1 files changed, 2065 insertions, 0 deletions
diff --git a/source/cmodel.c b/source/cmodel.c new file mode 100644 index 0000000..8bc4ea0 --- /dev/null +++ b/source/cmodel.c @@ -0,0 +1,2065 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. + +This program 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. + +This program 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 this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +// cmodel.c -- model loading + +#include "com_local.h" + +#define CM_Malloc( size ) Z_TagMalloc( size, TAG_CMODEL ) +#define CM_Mallocz( size ) Z_TagMallocz( size, TAG_CMODEL ) +#define CM_Free( ptr ) Z_Free( ptr ) + +static mapsurface_t nullsurface; +static cleaf_t nullleaf; + +static int floodvalid; +static int checkcount; + +static cvar_t *map_noareas; +static cvar_t *map_load_entities; + +void CM_FloodAreaConnections( cm_t *cm ); + +/* +=============================================================================== + + MAP LOADING + +=============================================================================== +*/ + +static byte *cmod_base; +static cmcache_t *cmod; + +/* +================= +CMod_LoadSubmodels +================= +*/ +static void CMod_LoadSubmodels (lump_t *l) +{ + dmodel_t *in; + cmodel_t *out; + int i, j, count; + uint32 headnode; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadSubmodels: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Com_Error (ERR_DROP, "Map with no models"); + if (count > MAX_MAP_MODELS) + Com_Error (ERR_DROP, "Map has too many models"); + + cmod->cmodels = CM_Malloc( sizeof( *out ) * count ); + cmod->numcmodels = count; + + out = cmod->cmodels; + for ( i=0 ; i<count ; i++, in++, out++) + { + for (j=0 ; j<3 ; j++) + { // spread the mins / maxs by a pixel + out->mins[j] = LittleFloat (in->mins[j]) - 1; + out->maxs[j] = LittleFloat (in->maxs[j]) + 1; + out->origin[j] = LittleFloat (in->origin[j]); + } + headnode = LittleLong (in->headnode); + if( headnode >= cmod->numnodes ) { + // FIXME: headnode may be garbage for some models + Com_DPrintf( "CMod_LoadSubmodels: bad headnode for model %d\n", i ); + out->headnode = NULL; + } else { + out->headnode = cmod->nodes + headnode; + } + } +} + + +/* +================= +CMod_LoadSurfaces +================= +*/ +static void CMod_LoadSurfaces (lump_t *l) +{ + texinfo_t *in; + mapsurface_t *out; + int i, count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadSurfaces: funny lump size"); + count = l->filelen / sizeof(*in); + if (count < 1) + Com_Error (ERR_DROP, "Map with no surfaces"); + if (count > MAX_MAP_TEXINFO) + Com_Error (ERR_DROP, "Map has too many surfaces"); + + cmod->numtexinfo = count; + cmod->surfaces = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->surfaces; + for ( i=0 ; i<count ; i++, in++, out++) + { + Q_strncpyz (out->c.name, in->texture, sizeof(out->c.name)); + Q_strncpyz (out->rname, in->texture, sizeof(out->rname)); + out->c.flags = LittleLong (in->flags); + out->c.value = LittleLong (in->value); + } +} + + +/* +================= +CMod_LoadNodes + +================= +*/ +static void CMod_LoadNodes (lump_t *l) +{ + dnode_t *in; + uint32 child; + cnode_t *out; + int i, j, count; + uint32 planeNum; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadNodes: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Com_Error (ERR_DROP, "Map has no nodes"); + if (count > MAX_MAP_NODES) + Com_Error (ERR_DROP, "Map has too many nodes"); + + cmod->numnodes = count; + cmod->nodes = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->nodes; + for (i=0 ; i<count ; i++, out++, in++) + { + planeNum = LittleLong(in->planenum); + if( planeNum >= cmod->numplanes ) { + Com_Error (ERR_DROP, "CMod_LoadNodes: bad planenum"); + } + out->plane = cmod->planes + planeNum; + + for( j = 0; j < 2; j++ ) { + child = LittleLong( in->children[j] ); + if( child & 0x80000000 ) { + child = ~child; + if( child >= cmod->numleafs ) { + Com_Error( ERR_DROP, "CMod_LoadNodes: bad leafnum" ); + } + out->children[j] = ( cnode_t * )( cmod->leafs + child ); + } else { + if( child >= count ) { + Com_Error ( ERR_DROP, "CMod_LoadNodes: bad nodenum" ); + } + out->children[j] = cmod->nodes + child; + } + } + } + +} + +/* +================= +CMod_LoadBrushes + +================= +*/ +static void CMod_LoadBrushes (lump_t *l) +{ + dbrush_t *in; + cbrush_t *out; + int i, count; + uint32 firstSide, numSides; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadBrushes: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_MAP_BRUSHES) + Com_Error (ERR_DROP, "Map has too many brushes"); + + cmod->numbrushes = count; + cmod->brushes = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->brushes; + for (i=0 ; i<count ; i++, out++, in++) + { + firstSide = LittleLong(in->firstside); + numSides = LittleLong(in->numsides); + if( firstSide + numSides > cmod->numbrushsides ) { + Com_Error (ERR_DROP, "CMod_LoadBrushes: bad brushsides"); + } + out->firstbrushside = cmod->brushsides + firstSide; + out->numsides = numSides; + out->contents = LittleLong(in->contents); + out->checkcount = 0; + } + +} + +/* +================= +CMod_LoadLeafs +================= +*/ +static void CMod_LoadLeafs (lump_t *l) +{ + int i; + cleaf_t *out; + dleaf_t *in; + int count; + uint16 areaNum, firstBrush, numBrushes; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadLeafs: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Com_Error (ERR_DROP, "Map with no leafs"); + // need to save space for box planes + if (count > MAX_MAP_LEAFS) + Com_Error (ERR_DROP, "Map has too many leafs"); + + cmod->numleafs = count; + cmod->numclusters = 0; + cmod->leafs = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->leafs; + for ( i=0 ; i<count ; i++, in++, out++) + { + out->plane = NULL; + out->contents = LittleLong (in->contents); + out->cluster = ( signed short )LittleShort (in->cluster); + areaNum = LittleShort (in->area); + if( areaNum >= cmod->numareas ) { + Com_Error (ERR_DROP, "CMod_LoadLeafs: bad areanum"); + } + out->area = areaNum; + + firstBrush = LittleShort (in->firstleafbrush); + numBrushes = LittleShort (in->numleafbrushes); + if( firstBrush + numBrushes > cmod->numleafbrushes ) { + Com_Error (ERR_DROP, "CMod_LoadLeafs: bad brushnum"); + } + + out->firstleafbrush = cmod->leafbrushes + firstBrush; + out->numleafbrushes = numBrushes; + + if (out->cluster >= cmod->numclusters) + cmod->numclusters = out->cluster + 1; + } + + if (cmod->leafs[0].contents != CONTENTS_SOLID) + Com_Error (ERR_DROP, "Map leaf 0 is not CONTENTS_SOLID"); +} + +/* +================= +CMod_LoadPlanes +================= +*/ +static void CMod_LoadPlanes (lump_t *l) +{ + int i; + cplane_t *out; + dplane_t *in; + int count; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadPlanes: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Com_Error (ERR_DROP, "Map with no planes"); + if (count > MAX_MAP_PLANES) + Com_Error (ERR_DROP, "Map has too many planes"); + + cmod->numplanes = count; + cmod->planes = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->planes; + for ( i=0 ; i<count ; i++, in++, out++) + { + out->normal[0] = LittleFloat( in->normal[0] ); + out->normal[1] = LittleFloat( in->normal[1] ); + out->normal[2] = LittleFloat( in->normal[2] ); + out->dist = LittleFloat (in->dist); + //out->type = LittleLong (in->type); + + SetPlaneType( out ); + SetPlaneSignbits( out ); + } +} + +/* +================= +CMod_LoadLeafBrushes +================= +*/ +static void CMod_LoadLeafBrushes (lump_t *l) +{ + int i; + cbrush_t **out; + uint16 *in; + int count; + uint16 brushNum; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadLeafBrushes: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count < 1) + Com_Error (ERR_DROP, "Map with no leafbrushes"); + if (count > MAX_MAP_LEAFBRUSHES) + Com_Error (ERR_DROP, "Map has too many leafbrushes"); + + cmod->numleafbrushes = count; + cmod->leafbrushes = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->leafbrushes; + for ( i=0 ; i<count ; i++, in++, out++) { + brushNum = LittleShort (*in); + if( brushNum >= cmod->numbrushes ) { + Com_Error (ERR_DROP, "CMod_LoadLeafBrushes: bad brushnum"); + } + *out = cmod->brushes + brushNum; + } +} + +/* +================= +CMod_LoadBrushSides +================= +*/ +static void CMod_LoadBrushSides (lump_t *l) +{ + int i; + cbrushside_t *out; + dbrushside_t *in; + int count; + uint32 planeNum; + uint16 texinfoNum; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadBrushSides: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_MAP_BRUSHSIDES) + Com_Error (ERR_DROP, "Map has too many brush sides"); + + cmod->numbrushsides = count; + cmod->brushsides = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->brushsides; + for ( i=0 ; i<count ; i++, in++, out++) + { + planeNum = LittleShort (in->planenum); + if( planeNum >= cmod->numplanes ) { + Com_Error (ERR_DROP, "CMod_LoadBrushSides: bad planenum"); + } + out->plane = cmod->planes + planeNum; + texinfoNum = LittleShort (in->texinfo); + if( texinfoNum == (uint16)-1 ) { + out->surface = &nullsurface; + } else { + if (texinfoNum >= cmod->numtexinfo) + Com_Error (ERR_DROP, "CMod_LoadBrushSides: bad texinfo"); + out->surface = cmod->surfaces + texinfoNum; + } + } +} + +/* +================= +CMod_LoadAreas +================= +*/ +static void CMod_LoadAreas (lump_t *l) +{ + int i; + carea_t *out; + darea_t *in; + int count; + uint32 numPortals, firstPortal; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadAreas: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_MAP_AREAS) + Com_Error (ERR_DROP, "Map has too many areas"); + + cmod->numareas = count; + cmod->areas = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->areas; + for ( i=0 ; i<count ; i++, in++, out++) + { + numPortals = LittleLong (in->numareaportals); + firstPortal = LittleLong (in->firstareaportal); + if( firstPortal + numPortals > cmod->numareaportals ) { + Com_Error (ERR_DROP, "CMod_LoadAreas: bad areaportals"); + } + out->numareaportals = numPortals; + out->firstareaportal = firstPortal; + out->floodvalid = 0; + } +} + +/* +================= +CMod_LoadAreaPortals +================= +*/ +static void CMod_LoadAreaPortals (lump_t *l) +{ + int i; + careaportal_t *out; + dareaportal_t *in; + int count; + uint32 portalNum, otherArea; + + in = (void *)(cmod_base + l->fileofs); + if (l->filelen % sizeof(*in)) + Com_Error (ERR_DROP, "CMod_LoadAreaPortals: funny lump size"); + count = l->filelen / sizeof(*in); + + if (count > MAX_MAP_AREAS) + Com_Error (ERR_DROP, "Map has too many areas"); + + cmod->numareaportals = count; + cmod->areaportals = CM_Malloc( sizeof( *out ) * count ); + + out = cmod->areaportals; + for ( i=0 ; i<count ; i++, in++, out++) + { + portalNum = LittleLong (in->portalnum); + if( portalNum >= MAX_MAP_AREAPORTALS ) { + Com_Error (ERR_DROP, "CMod_LoadAreaPortals: bad portalnum"); + } + out->portalnum = portalNum; + otherArea = LittleLong (in->otherarea); + if( otherArea >= MAX_MAP_AREAS ) { + Com_Error (ERR_DROP, "CMod_LoadAreaPortals: bad otherarea"); + } + out->otherarea = otherArea; + } +} + +/* +================= +CMod_LoadVisibility +================= +*/ +static void CMod_LoadVisibility( lump_t *l ) { + int i; + uint32 numClusters; + + if( !l->filelen ) { + return; + } + + if( l->filelen > MAX_MAP_VISIBILITY ) + Com_Error (ERR_DROP, "Map has too large visibility lump"); + + cmod->numvisibility = l->filelen; + cmod->vis = CM_Malloc( l->filelen ); + memcpy( cmod->vis, cmod_base + l->fileofs, l->filelen ); + + numClusters = LittleLong( cmod->vis->numclusters ); + cmod->vis->numclusters = numClusters; + for( i = 0; i < numClusters; i++ ) { + cmod->vis->bitofs[i][0] = LittleLong( cmod->vis->bitofs[i][0] ); + cmod->vis->bitofs[i][1] = LittleLong( cmod->vis->bitofs[i][1] ); + } + + cmod->visrowsize = ( numClusters + 7 ) >> 3; +} + + +/* +================= +CMod_LoadEntityString +================= +*/ +static void CMod_LoadEntityString( lump_t *l ) { + if( l->filelen > MAX_MAP_ENTSTRING ) { + Com_Error( ERR_DROP, "Map has too large entity lump" ); + } + + cmod->numEntityChars = l->filelen; + cmod->entitystring = CM_Malloc( l->filelen + 1 ); + memcpy( cmod->entitystring, cmod_base + l->fileofs, l->filelen ); + cmod->entitystring[l->filelen] = 0; +} + +#if 0 +/* +================== +CM_ConcatToEntityString +================== +*/ +static void CM_ConcatToEntityString( const char *text ) { + int len; + + len = strlen( text ); + if( numEntityChars + len > sizeof( cmod->entitystring ) - 1 ) { + Com_Error( ERR_DROP, "CM_ConcatToEntityString: oversize entity lump" ); + } + + strcpy( cmod->entitystring + numEntityChars, text ); + numEntityChars += len; +} + +/* +================== +CM_ParseMapFile + +Parses complete *.map file +================== +*/ +static qboolean CM_ParseMapFile( const char *data ) { + char *token; + int numInlineModels; + qboolean inlineModel; + char buffer[MAX_STRING_CHARS]; + char key[MAX_TOKEN_CHARS]; + + numInlineModels = 0; + + while( 1 ) { + token = COM_Parse( ( const char ** )&data ); + if( !data ) { + break; + } + + if( *token != '{' ) { + Com_WPrintf( "CM_ParseMapFile: expected '{', found '%s'\n", token ); + return qfalse; + } + + CM_ConcatToEntityString( "{ " ); + + inlineModel = qfalse; + + // Parse entity + while( 1 ) { + token = COM_Parse( ( const char ** )&data ); + if( !data ) { + Com_WPrintf( "CM_ParseMapFile: expected key, found EOF\n" ); + return qfalse; + } + + if( *token == '}' ) { + // FIXME HACK: restore inline model number + // This may not work properly if the entity order is different!!! + if( inlineModel && numInlineModels ) { + Com_sprintf( buffer, sizeof( buffer ), "\"model\" \"*%i\" } ", numInlineModels ); + CM_ConcatToEntityString( buffer ); + } else { + CM_ConcatToEntityString( "} " ); + } + + if( inlineModel ) { + numInlineModels++; + } + + break; + } + + if( *token == '{' ) { + inlineModel = qtrue; + + // Parse brush + while( 1 ) { + token = COM_Parse( ( const char ** )&data ); + if( !data ) { + Com_WPrintf( "CM_ParseMapFile: expected brush data, found EOF\n" ); + return qfalse; + } + + if( *token == '}' ) { + break; + } + + if( *token == '{' ) { + Com_WPrintf( "CM_ParseMapFile: expected brush data, found '{'\n" ); + return qfalse; + } + + } + + continue; + } + + Q_strncpyz( key, token, sizeof( key ) ); + + token = COM_Parse( ( const char ** )&data ); + if( !data ) { + Com_WPrintf( "CM_ParseMapFile: expected value, found EOF\n" ); + return qfalse; + } + + if( *token == '}' || *token == '{' ) { + Com_WPrintf( "CM_ParseMapFile: expected value, found '%s'\n", token ); + return qfalse; + } + + Com_sprintf( buffer, sizeof( buffer ), "\"%s\" \"%s\" ", key, token ); + + CM_ConcatToEntityString( buffer ); + + } + } + + cmod->entitystring[numEntityChars] = 0; + + if( numInlineModels != numcmodels + 1 ) { + Com_WPrintf( "CM_ParseMapFile: mismatched number of inline models\n" ); + return qfalse; + } + + return qtrue; +} +#endif + +#define CM_CACHESIZE 16 + +static cmcache_t cm_cache[CM_CACHESIZE]; + +void CM_FreeMap( cm_t *cm ) { + cmcache_t *cache; + + if( cm->floodnums ) { + Z_Free( cm->floodnums ); + } + + cache = cm->cache; + if( cache ) { + if( cache->refcount <= 0 ) { + Com_Error( ERR_FATAL, "CM_FreeMap: negative refcount" ); + } + if( --cache->refcount == 0 ) { + +#define CM_FREE( f ) \ + do { \ + if( cache->f ) { \ + CM_Free( cache->f ); \ + } \ + } while( 0 ) + + CM_FREE( vis ); + CM_FREE( surfaces ); + CM_FREE( planes ); + CM_FREE( brushsides ); + CM_FREE( brushes ); + CM_FREE( leafbrushes ); + CM_FREE( areaportals ); + CM_FREE( areas ); + CM_FREE( leafs ); + CM_FREE( nodes ); + CM_FREE( cmodels ); + CM_FREE( entitystring ); + + memset( cache, 0, sizeof( *cache ) ); + } + } + + memset( cm, 0, sizeof( *cm ) ); +} + + +/* +================== +CM_LoadMap + +Loads in the map and all submodels +================== +*/ +qboolean CM_LoadMap( cm_t *cm, const char *name, uint32 flags, uint32 *checksum ) { + cmcache_t *cache; + byte *buf; + int i; + dheader_t header; + int length; +// char *entstring; +// char buffer[MAX_QPATH]; + + if( !name ) { + Com_Error( ERR_FATAL, "CM_LoadMap: NULL name" ); + } + if( !name[0] ) { + Com_Error( ERR_DROP, "CM_LoadMap: empty name" ); + } + + for( i = 0, cache = cm_cache; i < CM_CACHESIZE; i++, cache++ ) { + if( !cache->name[0] ) { + continue; + } + if( strcmp( cache->name, name ) ) { + continue; + } + //if( !( cache->flags & CM_LOAD_VISONLY ) || ( flags & CM_LOAD_VISONLY ) ) + { + cm->cache = cache; + if( checksum ) { + *checksum = cache->checksum; + } + cm->cache = cache; + if( flags & CM_LOAD_CLIENT ) { + cm->floodnums = NULL; + cm->portalopen = NULL; + } else { + cm->floodnums = CM_Mallocz( sizeof( int ) * cache->numareas + + sizeof( qboolean ) * cache->numareaportals ); + cm->portalopen = ( qboolean * )( cm->floodnums + cache->numareas ); + CM_FloodAreaConnections( cm ); + } + cache->refcount++; + return qtrue; // still have the right version + } + break; + } + + // find a free slot + for( i = 0, cache = cm_cache; i < CM_CACHESIZE; i++, cache++ ) { + if( !cache->name[0] ) { + break; + } + } + if( i == CM_CACHESIZE ) { + Com_Error( ERR_DROP, "Out of cache slots to load %s", name ); + } + + // + // load the file + // + length = FS_LoadFileEx( name, (void **)&buf, FS_FLAG_CACHE ); + if( !buf ) + Com_Error( ERR_DROP, "Couldn't load %s", name ); + + cache->checksum = LittleLong( Com_BlockChecksum( buf, length ) ); + if( checksum ) { + *checksum = cache->checksum; + } + + header = *( dheader_t * )buf; + for( i = 0; i < sizeof( dheader_t )/4; i++ ) + (( uint32 * )&header)[i] = LittleLong( (( uint32 * )&header)[i] ); + + if( header.version != BSPVERSION ) { + Com_Error( ERR_DROP, "CM_LoadMap: %s has wrong version number (%i should be %i)", + name, header.version, BSPVERSION ); + } + + cmod_base = buf; + cmod = cache; + +#define CM_LOAD( Func, Lump ) \ + do { \ + CMod_Load##Func( &header.lumps[LUMP_##Lump] ); \ + } while( 0 ) + + // load into heap + CM_LOAD( Visibility, VISIBILITY ); + CM_LOAD( Surfaces, TEXINFO ); + CM_LOAD( Planes, PLANES ); + CM_LOAD( BrushSides, BRUSHSIDES ); + CM_LOAD( Brushes, BRUSHES ); + CM_LOAD( LeafBrushes, LEAFBRUSHES ); + CM_LOAD( AreaPortals, AREAPORTALS ); + CM_LOAD( Areas, AREAS ); + CM_LOAD( Leafs, LEAFS ); + CM_LOAD( Nodes, NODES ); + CM_LOAD( Submodels, MODELS ); + +#if 0 + // Load the entity string from file, if specified + entstring = NULL; + if( cmod->load_entities->integer && !clientload ) { + COM_StripExtension( name, buffer, sizeof( buffer ) ); + Q_strcat( buffer, sizeof( buffer ), ".map" ); + FS_LoadFile( buffer, ( void ** )&entstring ); + } + + if( entstring ) { + Com_Printf( "Loading %s...\n", buffer ); + if( !CM_ParseMapFile( entstring ) ) { + CMod_LoadEntityString( &header.lumps[LUMP_ENTITIES] ); + } + FS_FreeFile( entstring ); + } else { + CMod_LoadEntityString( &header.lumps[LUMP_ENTITIES] ); + } +#else + CMod_LoadEntityString( &header.lumps[LUMP_ENTITIES] ); +#endif + + FS_FreeFile( buf ); + + cache->refcount = 1; + cm->cache = cache; + + if( flags & CM_LOAD_CLIENT ) { + cm->floodnums = NULL; + cm->portalopen = NULL; + } else { + cm->floodnums = CM_Mallocz( sizeof( int ) * cache->numareas + + sizeof( qboolean ) * cache->numareaportals ); + cm->portalopen = ( qboolean * )( cm->floodnums + cache->numareas ); + CM_FloodAreaConnections( cm ); + } + + Q_strncpyz( cache->name, name, sizeof( cache->name ) ); + + //map_load_entities->modified = qfalse; + + return qtrue; +} + +/* +================== +CM_InlineModel +================== +*/ +cmodel_t *CM_InlineModel( cm_t *cm, const char *name ) { + int num; + cmodel_t *cmodel; + + if( !name || name[0] != '*' ) { + Com_Error( ERR_DROP, "CM_InlineModel: bad name: %s", name ); + } + if( !cm->cache ) { + Com_Error( ERR_DROP, "CM_InlineModel: NULL cache" ); + } + num = atoi( name + 1 ); + if( num < 1 || num >= cm->cache->numcmodels ) { + Com_Error ( ERR_DROP, "CM_InlineModel: bad number: %d", num ); + } + + cmodel = &cm->cache->cmodels[num]; + + return cmodel; +} + +int CM_NumClusters( cm_t *cm ) { + if( !cm->cache ) { + return 0; + } + return cm->cache->numclusters; +} + +int CM_NumInlineModels( cm_t *cm ) { + if( !cm->cache ) { + return 0; + } + return cm->cache->numcmodels; +} + +char *CM_EntityString( cm_t *cm ) { + if( !cm->cache ) { + return ""; + } + if( !cm->cache->entitystring ) { + return ""; + } + return cm->cache->entitystring; +} + +cnode_t *CM_NodeNum( cm_t *cm, int number ) { + if( !cm->cache ) { + Com_Error( ERR_DROP, "CM_NodeNum: NULL cache" ); + } + if( number < 0 || number >= cm->cache->numnodes ) { + Com_Error( ERR_DROP, "CM_NodeNum: bad number: %d", number ); + } + return cm->cache->nodes + number; +} + +cleaf_t *CM_LeafNum( cm_t *cm, int number ) { + if( !cm->cache ) { + Com_Error( ERR_DROP, "CM_LeafNum: NULL cache" ); + } + if( number < 0 || number >= cm->cache->numleafs ) { + Com_Error( ERR_DROP, "CM_LeafNum: bad number: %d", number ); + } + return cm->cache->leafs + number; +} + +//======================================================================= + +static cplane_t box_planes[12]; +static cnode_t box_nodes[6]; +static cnode_t *box_headnode; +static cbrush_t box_brush; +static cbrush_t *box_leafbrush; +static cbrushside_t box_brushsides[6]; +static cleaf_t box_leaf; +static cleaf_t box_emptyleaf; + +/* +=================== +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. +=================== +*/ +static void CM_InitBoxHull( void ) { + int i; + int side; + cnode_t *c; + cplane_t *p; + cbrushside_t *s; + + box_headnode = &box_nodes[0]; + + box_brush.numsides = 6; + box_brush.firstbrushside = &box_brushsides[0]; + box_brush.contents = CONTENTS_MONSTER; + + box_leaf.contents = CONTENTS_MONSTER; + box_leaf.firstleafbrush = &box_leafbrush; + box_leaf.numleafbrushes = 1; + + box_leafbrush = &box_brush; + + for( i = 0; i < 6; i++ ) { + side = i & 1; + + // brush sides + s = &box_brushsides[i]; + s->plane = &box_planes[i*2+side]; + s->surface = &nullsurface; + + // nodes + c = &box_nodes[i]; + c->plane = &box_planes[i*2]; + c->children[side] = ( cnode_t * )&box_emptyleaf; + if( i != 5 ) + c->children[side^1] = &box_nodes[i + 1]; + else + c->children[side^1] = ( cnode_t * )&box_leaf; + + // 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; + } +} + + +/* +=================== +CM_HeadnodeForBox + +To keep everything totally uniform, bounding boxes are turned into small +BSP trees instead of being compared directly. +=================== +*/ +cnode_t *CM_HeadnodeForBox( vec3_t mins, vec3_t maxs ) { + 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]; + + return box_headnode; +} + + +/* +================== +CM_PointLeaf_r + +================== +*/ +static cleaf_t *CM_PointLeaf_r( vec3_t p, cnode_t *node ) { + float d; + cplane_t *plane; + + while( node->plane ) { + plane = node->plane; + if( plane->type < 3 ) + d = p[plane->type] - plane->dist; + else + d = DotProduct( plane->normal, p ) - plane->dist; + if( d < 0 ) + node = node->children[1]; + else + node = node->children[0]; + } + + return ( cleaf_t * )node; +} + +cleaf_t *CM_PointLeaf( cm_t *cm, vec3_t p ) { + if( !cm->cache ) { + return &nullleaf; // server may call this without map loaded + } + return CM_PointLeaf_r( p, cm->cache->nodes ); +} + + +/* +============= +CM_BoxLeafnums + +Fills in a list of all the leafs touched +============= +*/ +static int leaf_count, leaf_maxcount; +static cleaf_t **leaf_list; +static float *leaf_mins, *leaf_maxs; +static cnode_t *leaf_topnode; + +static void CM_BoxLeafs_r( cnode_t *node ) { + cplane_t *plane; + int s; + + while( 1 ) { + plane = node->plane; + if( !plane ) { + if( leaf_count >= leaf_maxcount ) { + return; + } + leaf_list[leaf_count++] = ( cleaf_t * )node; + return; + } + + s = BoxOnPlaneSide( leaf_mins, leaf_maxs, plane ); + if( s == 1 ) { + node = node->children[0]; + } else if( s == 2 ) { + node = node->children[1]; + } else { + // go down both + if( !leaf_topnode ) { + leaf_topnode = node; + } + CM_BoxLeafs_r( node->children[0] ); + node = node->children[1]; + } + + } +} + +static int CM_BoxLeafs_headnode( vec3_t mins, vec3_t maxs, cleaf_t **list, int listsize, + cnode_t *headnode, cnode_t **topnode ) +{ + leaf_list = list; + leaf_count = 0; + leaf_maxcount = listsize; + leaf_mins = mins; + leaf_maxs = maxs; + + leaf_topnode = NULL; + + CM_BoxLeafs_r( headnode ); + + if( topnode ) + *topnode = leaf_topnode; + + return leaf_count; +} + +int CM_BoxLeafs( cm_t *cm, vec3_t mins, vec3_t maxs, cleaf_t **list, int listsize, cnode_t **topnode ) { + if( !cm->cache ) // map not loaded + return 0; + return CM_BoxLeafs_headnode( mins, maxs, list, + listsize, cm->cache->nodes, topnode ); +} + + + +/* +================== +CM_PointContents + +================== +*/ +int CM_PointContents( vec3_t p, cnode_t *headnode ) { + cleaf_t *leaf; + + if( !headnode ) { + return 0; + } + + leaf = CM_PointLeaf_r( p, headnode ); + + return leaf->contents; +} + +/* +================== +CM_TransformedPointContents + +Handles offseting and rotation of the end points for moving and +rotating entities +================== +*/ +int CM_TransformedPointContents( vec3_t p, cnode_t *headnode, vec3_t origin, vec3_t angles ) { + vec3_t p_l; + vec3_t temp; + vec3_t forward, right, up; + cleaf_t *leaf; + + if( !headnode ) { + return 0; + } + + // subtract origin offset + VectorSubtract (p, origin, p_l); + + // rotate start and end into the models frame of reference + if (headnode != box_headnode && + (angles[0] || angles[1] || angles[2]) ) + { + AngleVectors (angles, forward, right, up); + + VectorCopy (p_l, temp); + p_l[0] = DotProduct (temp, forward); + p_l[1] = -DotProduct (temp, right); + p_l[2] = DotProduct (temp, up); + } + + leaf = CM_PointLeaf_r( p_l, headnode ); + + return leaf->contents; +} + + +/* +=============================================================================== + +BOX TRACING + +=============================================================================== +*/ + +// 1/32 epsilon to keep floating point happy +#define DIST_EPSILON (0.03125) + +static vec3_t trace_start, trace_end; +static vec3_t trace_mins, trace_maxs; +static vec3_t trace_extents; + +static trace_t *trace_trace; +static int trace_contents; +static qboolean trace_ispoint; // optimized case + +/* +================ +CM_ClipBoxToBrush +================ +*/ +static void CM_ClipBoxToBrush (vec3_t mins, vec3_t maxs, vec3_t p1, vec3_t p2, + trace_t *trace, cbrush_t *brush) +{ + int i, j; + cplane_t *plane, *clipplane; + float dist; + float enterfrac, leavefrac; + vec3_t ofs; + float d1, d2; + qboolean getout, startout; + float f; + cbrushside_t *side, *leadside; + + enterfrac = -1; + leavefrac = 1; + clipplane = NULL; + + if (!brush->numsides) + return; + + getout = qfalse; + startout = qfalse; + leadside = NULL; + + side = brush->firstbrushside; + for (i=0 ; i<brush->numsides ; i++, side++) + { + plane = side->plane; + + // FIXME: special case for axial + + if (!trace_ispoint) + { // general box case + + // push the plane out apropriately for mins/maxs + + // FIXME: use signbits into 8 way lookup for each mins/maxs + for (j=0 ; j<3 ; j++) + { + if (plane->normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct (ofs, plane->normal); + dist = plane->dist - dist; + } + else + { // special point case + dist = plane->dist; + } + + d1 = DotProduct (p1, plane->normal) - dist; + d2 = DotProduct (p2, plane->normal) - dist; + + if (d2 > 0) + getout = qtrue; // endpoint is not in solid + if (d1 > 0) + startout = qtrue; + + // if completely in front of face, no intersection + if (d1 > 0 && d2 >= d1) + return; + + if (d1 <= 0 && d2 <= 0) + continue; + + // crosses face + if (d1 > d2) + { // enter + f = (d1-DIST_EPSILON) / (d1-d2); + if (f > enterfrac) + { + enterfrac = f; + clipplane = plane; + leadside = side; + } + } + else + { // leave + f = (d1+DIST_EPSILON) / (d1-d2); + if (f < leavefrac) + leavefrac = f; + } + } + + if (!startout) + { // original point was inside brush + trace->fraction = 0; + trace->startsolid = qtrue; + if (!getout) + trace->allsolid = qtrue; + return; + } + if (enterfrac < leavefrac) + { + if (enterfrac > -1 && enterfrac < trace->fraction) + { + if (enterfrac < 0) + enterfrac = 0; + trace->fraction = enterfrac; + trace->plane = *clipplane; + trace->surface = &(leadside->surface->c); + trace->contents = brush->contents; + } + } +} + +/* +================ +CM_TestBoxInBrush +================ +*/ +static void CM_TestBoxInBrush (vec3_t mins, vec3_t maxs, vec3_t p1, + trace_t *trace, cbrush_t *brush) +{ + int i, j; + cplane_t *plane; + float dist; + vec3_t ofs; + float d1; + cbrushside_t *side; + + if (!brush->numsides) + return; + + side = brush->firstbrushside; + for (i=0 ; i<brush->numsides ; i++, side++) + { + plane = side->plane; + + // FIXME: special case for axial + + // general box case + + // push the plane out apropriately for mins/maxs + + // FIXME: use signbits into 8 way lookup for each mins/maxs + for (j=0 ; j<3 ; j++) + { + if (plane->normal[j] < 0) + ofs[j] = maxs[j]; + else + ofs[j] = mins[j]; + } + dist = DotProduct (ofs, plane->normal); + dist = plane->dist - dist; + + d1 = DotProduct (p1, plane->normal) - dist; + + // if completely in front of face, no intersection + if (d1 > 0) + return; + + } + + // inside this brush + trace->startsolid = trace->allsolid = qtrue; + trace->fraction = 0; + trace->contents = brush->contents; +} + + +/* +================ +CM_TraceToLeaf +================ +*/ +static void CM_TraceToLeaf ( cleaf_t *leaf ) +{ + int k; + cbrush_t *b, **leafbrush; + + if( !( leaf->contents & trace_contents ) ) + return; + // trace line against all brushes in the leaf + leafbrush = leaf->firstleafbrush; + for (k=0 ; k<leaf->numleafbrushes ; k++, leafbrush++) + { + b = *leafbrush; + if (b->checkcount == checkcount) + continue; // already checked this brush in another leaf + b->checkcount = checkcount; + + if ( !(b->contents & trace_contents)) + continue; + CM_ClipBoxToBrush (trace_mins, trace_maxs, trace_start, trace_end, trace_trace, b); + if (!trace_trace->fraction) + return; + } + +} + + +/* +================ +CM_TestInLeaf +================ +*/ +static void CM_TestInLeaf ( cleaf_t *leaf ) +{ + int k; + cbrush_t *b, **leafbrush; + + if( !( leaf->contents & trace_contents ) ) + return; + // trace line against all brushes in the leaf + leafbrush = leaf->firstleafbrush; + for (k=0 ; k<leaf->numleafbrushes ; k++, leafbrush++) + { + b = *leafbrush; + if (b->checkcount == checkcount) + continue; // already checked this brush in another leaf + b->checkcount = checkcount; + + if ( !(b->contents & trace_contents)) + continue; + CM_TestBoxInBrush (trace_mins, trace_maxs, trace_start, trace_trace, b); + if (!trace_trace->fraction) + return; + } + +} + + +/* +================== +CM_RecursiveHullCheck + +================== +*/ +static void CM_RecursiveHullCheck ( cnode_t *node, float p1f, float p2f, vec3_t p1, vec3_t p2) +{ + cplane_t *plane; + float t1, t2, offset; + float frac, frac2; + float idist; + int i; + vec3_t mid; + int side; + float midf; + + if (trace_trace->fraction <= p1f) + return; // already hit something nearer + + // if plane is NULL, we are in a leaf node + plane = node->plane; + if (!plane) + { + CM_TraceToLeaf ( ( cleaf_t * )node ); + return; + } + + // + // find the point distances to the seperating plane + // and the offset for the size of the box + // + if (plane->type < 3) + { + t1 = p1[plane->type] - plane->dist; + t2 = p2[plane->type] - plane->dist; + offset = trace_extents[plane->type]; + } + else + { + t1 = DotProduct (plane->normal, p1) - plane->dist; + t2 = DotProduct (plane->normal, p2) - plane->dist; + if (trace_ispoint) + offset = 0; + else + offset = fabs(trace_extents[0]*plane->normal[0]) + + fabs(trace_extents[1]*plane->normal[1]) + + fabs(trace_extents[2]*plane->normal[2]); + } + + // see which sides we need to consider + if (t1 >= offset && t2 >= offset) + { + CM_RecursiveHullCheck (node->children[0], p1f, p2f, p1, p2); + return; + } + if (t1 < -offset && t2 < -offset) + { + CM_RecursiveHullCheck (node->children[1], p1f, p2f, p1, p2); + return; + } + + // put the crosspoint DIST_EPSILON pixels on the near side + if (t1 < t2) + { + idist = 1.0/(t1-t2); + side = 1; + frac2 = (t1 + offset + DIST_EPSILON)*idist; + frac = (t1 - offset + DIST_EPSILON)*idist; + } + else if (t1 > t2) + { + idist = 1.0/(t1-t2); + side = 0; + frac2 = (t1 - offset - DIST_EPSILON)*idist; + frac = (t1 + offset + DIST_EPSILON)*idist; + } + else + { + side = 0; + frac = 1; + frac2 = 0; + } + + // move up to the node + if (frac < 0) + frac = 0; + if (frac > 1) + frac = 1; + + midf = p1f + (p2f - p1f)*frac; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac*(p2[i] - p1[i]); + + CM_RecursiveHullCheck (node->children[side], p1f, midf, p1, mid); + + + // go past the node + if (frac2 < 0) + frac2 = 0; + if (frac2 > 1) + frac2 = 1; + + midf = p1f + (p2f - p1f)*frac2; + for (i=0 ; i<3 ; i++) + mid[i] = p1[i] + frac2*(p2[i] - p1[i]); + + CM_RecursiveHullCheck (node->children[side^1], midf, p2f, mid, p2); +} + + + +//====================================================================== + +/* +================== +CM_BoxTrace +================== +*/ +void CM_BoxTrace( trace_t *trace, vec3_t start, vec3_t end, + vec3_t mins, vec3_t maxs, + cnode_t *headnode, int brushmask ) +{ + int i; + + checkcount++; // for multi-check avoidance + + // fill in a default trace + trace_trace = trace; + memset (trace_trace, 0, sizeof( *trace_trace )); + trace_trace->fraction = 1; + trace_trace->surface = &(nullsurface.c); + + if( !headnode ) { + return; + } + + trace_contents = brushmask; + VectorCopy (start, trace_start); + VectorCopy (end, trace_end); + VectorCopy (mins, trace_mins); + VectorCopy (maxs, trace_maxs); + + // + // check for position test special case + // + if (start[0] == end[0] && start[1] == end[1] && start[2] == end[2]) + { + cleaf_t *leafs[1024]; + int i, numleafs; + vec3_t c1, c2; + cnode_t *topnode; + + VectorAdd (start, mins, c1); + VectorAdd (start, maxs, c2); + for (i=0 ; i<3 ; i++) + { + c1[i] -= 1; + c2[i] += 1; + } + + numleafs = CM_BoxLeafs_headnode (c1, c2, leafs, 1024, headnode, &topnode); + for (i=0 ; i<numleafs ; i++) + { + CM_TestInLeaf (leafs[i]); + if (trace_trace->allsolid) + break; + } + VectorCopy (start, trace_trace->endpos); + return; + } + + // + // check for point special case + // + if (mins[0] == 0 && mins[1] == 0 && mins[2] == 0 + && maxs[0] == 0 && maxs[1] == 0 && maxs[2] == 0) + { + trace_ispoint = qtrue; + VectorClear (trace_extents); + } + else + { + trace_ispoint = qfalse; + trace_extents[0] = -mins[0] > maxs[0] ? -mins[0] : maxs[0]; + trace_extents[1] = -mins[1] > maxs[1] ? -mins[1] : maxs[1]; + trace_extents[2] = -mins[2] > maxs[2] ? -mins[2] : maxs[2]; + } + + // + // general sweeping through world + // + CM_RecursiveHullCheck (headnode, 0, 1, start, end); + + if (trace_trace->fraction == 1) + { + VectorCopy (end, trace_trace->endpos); + } + else + { + for (i=0 ; i<3 ; i++) + trace_trace->endpos[i] = start[i] + trace_trace->fraction * (end[i] - start[i]); + } + +} + + +/* +================== +CM_TransformedBoxTrace + +Handles offseting and rotation of the end points for moving and +rotating entities +================== +*/ +void CM_TransformedBoxTrace ( trace_t *trace, vec3_t start, vec3_t end, + vec3_t mins, vec3_t maxs, + cnode_t *headnode, int brushmask, + vec3_t origin, vec3_t angles) +{ + vec3_t start_l, end_l; + vec3_t a; + vec3_t forward, right, up; + vec3_t temp; + qboolean rotated; + + // subtract origin offset + VectorSubtract (start, origin, start_l); + VectorSubtract (end, origin, end_l); + + // rotate start and end into the models frame of reference + if (headnode != box_headnode && + (angles[0] || angles[1] || angles[2]) ) + rotated = qtrue; + else + rotated = qfalse; + + if (rotated) + { + AngleVectors (angles, forward, right, up); + + VectorCopy (start_l, temp); + start_l[0] = DotProduct (temp, forward); + start_l[1] = -DotProduct (temp, right); + start_l[2] = DotProduct (temp, up); + + VectorCopy (end_l, temp); + end_l[0] = DotProduct (temp, forward); + end_l[1] = -DotProduct (temp, right); + end_l[2] = DotProduct (temp, up); + } + + // sweep the box through the model + CM_BoxTrace ( trace, start_l, end_l, mins, maxs, headnode, brushmask); + + if (rotated && trace->fraction != 1.0) + { + // FIXME: figure out how to do this with existing angles + VectorNegate (angles, a); + AngleVectors (a, forward, right, up); + + VectorCopy (trace->plane.normal, temp); + trace->plane.normal[0] = DotProduct (temp, forward); + trace->plane.normal[1] = -DotProduct (temp, right); + trace->plane.normal[2] = DotProduct (temp, up); + } + + trace->endpos[0] = start[0] + trace->fraction * (end[0] - start[0]); + trace->endpos[1] = start[1] + trace->fraction * (end[1] - start[1]); + trace->endpos[2] = start[2] + trace->fraction * (end[2] - start[2]); +} + +/* +=============================================================================== + +PVS / PHS + +=============================================================================== +*/ + +static byte pvsrow[MAX_MAP_LEAFS/8]; +static byte phsrow[MAX_MAP_LEAFS/8]; + +/* +=================== +CM_DecompressVis +=================== +*/ +static void CM_DecompressVis( byte *in, byte *out, int rowsize ) { + int c; + byte *out_p; + + out_p = out; + do { + if( *in ) { + *out_p++ = *in++; + continue; + } + + c = in[1]; + in += 2; + if( ( out_p - out ) + c > rowsize ) { + c = rowsize - ( out_p - out ); + Com_WPrintf( "CM_DecompressVis: overrun\n" ); + } + while( c ) { + *out_p++ = 0; + c--; + } + } while( out_p - out < rowsize ); +} + +byte *CM_ClusterPVS( cm_t *cm, int cluster ) { + cmcache_t *cache = cm->cache; + + if( !cache || !cache->vis ) { + memset( pvsrow, 0xff, sizeof( pvsrow ) ); + } else if( cluster == -1 ) { + memset( pvsrow, 0, cache->visrowsize ); + } else { + if( cluster < 0 || cluster >= cache->vis->numclusters ) { + Com_Error( ERR_DROP, "CM_ClusterPVS: bad cluster" ); + } + CM_DecompressVis( ( byte * )cache->vis + cache->vis->bitofs[cluster][DVIS_PVS], + pvsrow, cache->visrowsize ); + } + return pvsrow; +} + +byte *CM_ClusterPHS( cm_t *cm, int cluster ) { + cmcache_t *cache = cm->cache; + + if( !cache || !cache->vis ) { + memset( phsrow, 0xff, sizeof( phsrow ) ); + } else if( cluster == -1 ) { + memset( phsrow, 0, cache->visrowsize ); + } else { + if( cluster < 0 || cluster >= cache->vis->numclusters ) { + Com_Error( ERR_DROP, "CM_ClusterPVS: bad cluster" ); + } + CM_DecompressVis( ( byte * )cache->vis + cache->vis->bitofs[cluster][DVIS_PHS], + phsrow, cache->visrowsize ); + } + return phsrow; +} + +/* +=============================================================================== + +AREAPORTALS + +=============================================================================== +*/ + +static void FloodArea_r( cm_t *cm, int number, int floodnum ) { + int i; + careaportal_t *p; + carea_t *area; + + area = &cm->cache->areas[number]; + if( area->floodvalid == floodvalid ) { + if( cm->floodnums[number] == floodnum ) + return; + Com_Error( ERR_DROP, "FloodArea_r: reflooded" ); + } + + cm->floodnums[number] = floodnum; + area->floodvalid = floodvalid; + p = &cm->cache->areaportals[area->firstareaportal]; + for( i = 0; i < area->numareaportals; i++, p++ ) { + if( cm->portalopen[p->portalnum] ) + FloodArea_r( cm, p->otherarea, floodnum ); + } +} + +/* +==================== +CM_FloodAreaConnections +==================== +*/ +void CM_FloodAreaConnections( cm_t *cm ) { + int i; + carea_t *area; + int floodnum; + + // all current floods are now invalid + floodvalid++; + floodnum = 0; + + // area 0 is not used + for( i = 1; i < cm->cache->numareas; i++ ) { + area = &cm->cache->areas[i]; + if( area->floodvalid == floodvalid ) + continue; // already flooded into + floodnum++; + FloodArea_r( cm, i, floodnum ); + } + +} + +void CM_SetAreaPortalState( cm_t *cm, int portalnum, qboolean open ) { + if( portalnum > cm->cache->numareaportals ) + Com_Error( ERR_DROP, "CM_SetAreaPortalState: areaportal > numareaportals" ); + + cm->portalopen[portalnum] = open; + CM_FloodAreaConnections( cm ); +} + +qboolean CM_AreasConnected( cm_t *cm, int area1, int area2 ) { + cmcache_t *cache = cm->cache; + + if( !cache ) { + return qfalse; + } + if( map_noareas->integer ) + return qtrue; + + if( area1 > cache->numareas || area2 > cache->numareas ) + Com_Error( ERR_DROP, "CM_AreasConnected: area > numareas" ); + + if( cm->floodnums[area1] == cm->floodnums[area2] ) + return qtrue; + + return qfalse; +} + + +/* +================= +CM_WriteAreaBits + +Writes a length byte followed by a bit vector of all the areas +that area in the same flood as the area parameter + +This is used by the client refreshes to cull visibility +================= +*/ +int CM_WriteAreaBits( cm_t *cm, byte *buffer, int area ) { + cmcache_t *cache = cm->cache; + int i; + int floodnum; + int bytes; + + if( !cache ) { + return 0; + } + + bytes = ( cache->numareas + 7 ) >> 3; + + if( map_noareas->integer || !area ) { + // for debugging, send everything + memset( buffer, 255, bytes ); + } else { + memset( buffer, 0, bytes ); + + floodnum = cm->floodnums[area]; + for ( i = 0; i < cache->numareas; i++) { + if( cm->floodnums[i] == floodnum ) { + Q_SetBit( buffer, i ); + } + } + } + + return bytes; +} + +int CM_WritePortalBits( cm_t *cm, byte *buffer ) { + int i, bytes, numportals; + + if( !cm->cache ) { + return 0; + } + + numportals = cm->cache->numareaportals; + if( numportals > MAX_MAP_AREAS ) { + /* HACK: use the same array size as areabytes! + * It is nonsense for a map to have > 256 areaportals anyway. */ + Com_WPrintf( "CM_WritePortalBits: too many areaportals\n" ); + numportals = MAX_MAP_AREAS; + } + + bytes = ( numportals + 7 ) >> 3; + memset( buffer, 0, bytes ); + for( i = 0; i < numportals; i++ ) { + if( cm->portalopen[i] ) { + Q_SetBit( buffer, i ); + } + } + + return bytes; +} + +void CM_SetPortalStates( cm_t *cm, byte *buffer, int bytes ) { + int i, numportals; + + if( !bytes ) { + for( i = 0; i < cm->cache->numareaportals; i++ ) { + cm->portalopen[i] = qtrue; + } + } else { + numportals = bytes << 3; + if( numportals > cm->cache->numareaportals ) { + numportals = cm->cache->numareaportals; + } + for( i = 0; i < numportals; i++ ) { + cm->portalopen[i] = Q_IsBitSet( buffer, i ) ? qtrue : qfalse; + } + } + + CM_FloodAreaConnections( cm ); +} + + +/* +=================== +CM_WritePortalState + +Writes the portal state to a savegame file +=================== +*/ +void CM_WritePortalState( cm_t *cm, fileHandle_t f ) { + // FIXME: incompatible savegames + FS_Write( cm->portalopen, sizeof( qboolean ) * cm->cache->numareaportals, f ); +} + +/* +=================== +CM_ReadPortalState + +Reads the portal state from a savegame file +and recalculates the area connections +=================== +*/ +void CM_ReadPortalState( cm_t *cm, fileHandle_t f ) { + FS_Read( cm->portalopen, sizeof( qboolean ) * cm->cache->numareaportals, f ); + CM_FloodAreaConnections( cm ); +} + +/* +============= +CM_HeadnodeVisible + +Returns qtrue if any leaf under headnode has a cluster that +is potentially visible +============= +*/ +qboolean CM_HeadnodeVisible( cnode_t *node, byte *visbits ) { + cleaf_t *leaf; + int cluster; + + if( !node->plane ) { + leaf = ( cleaf_t * )node; + cluster = leaf->cluster; + if( cluster == -1 ) + return qfalse; + if( Q_IsBitSet( visbits, cluster ) ) + return qtrue; + return qfalse; + } + + if( CM_HeadnodeVisible( node->children[0], visbits ) ) + return qtrue; + return CM_HeadnodeVisible( node->children[1], visbits ); +} + + +/* +============ +CM_FatPVS + +The client will interpolate the view position, +so we can't use a single PVS point +=========== +*/ +byte *CM_FatPVS( cm_t *cm, const vec3_t org ) { + static byte fatpvs[MAX_MAP_LEAFS/8]; + cleaf_t *leafs[64]; + int clusters[64]; + int i, j, count; + int longs; + uint32 *src, *dst; + vec3_t mins, maxs; + + if( !cm->cache ) { // map not loaded + memset( fatpvs, 0, sizeof( fatpvs ) ); + return fatpvs; + } + + for( i = 0; i < 3; i++ ) { + mins[i] = org[i] - 8; + maxs[i] = org[i] + 8; + } + + count = CM_BoxLeafs( cm, mins, maxs, leafs, 64, NULL ); + if( count < 1 ) + Com_Error( ERR_DROP, "CM_FatPVS: leaf count < 1" ); + longs = ( cm->cache->numclusters + 31 ) >> 5; + + // convert leafs to clusters + for( i = 0 ; i < count; i++ ) { + clusters[i] = leafs[i]->cluster; + } + + src = ( uint32 * )CM_ClusterPVS( cm, clusters[0] ); + dst = ( uint32 * )fatpvs; + for( j = 0; j < longs; j++ ) { + *dst++ = *src++; + } + + // or in all the other leaf bits + for( i = 1; i < count; i++ ) { + for( j = 0; j < i; j++ ) { + if( clusters[i] == clusters[j] ) { + goto nextleaf; // already have the cluster we want + } + } + src = ( uint32 * )CM_ClusterPVS( cm, clusters[i] ); + dst = ( uint32 * )fatpvs; + for( j = 0; j < longs; j++ ) { + *dst++ |= *src++; + } + +nextleaf:; + } + + return fatpvs; +} + +/* +============= +CM_Init +============= +*/ +void CM_Init( void ) { + CM_InitBoxHull(); + + map_noareas = Cvar_Get( "map_noareas", "0", 0 ); + map_load_entities = Cvar_Get( "map_load_entities", "0", 0 ); +} + |