diff options
Diffstat (limited to 'source/bsp.c')
-rw-r--r-- | source/bsp.c | 1179 |
1 files changed, 1179 insertions, 0 deletions
diff --git a/source/bsp.c b/source/bsp.c new file mode 100644 index 0000000..2a30dc2 --- /dev/null +++ b/source/bsp.c @@ -0,0 +1,1179 @@ +/* +Copyright (C) 1997-2001 Id Software, Inc. +Copyright (C) 2008 Andrey Nazarov + +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. + +*/ + +// bsp.c -- model loading + +#include "com_local.h" +#include "q_list.h" +#include "files.h" +#include "sys_public.h" +#include "bsp.h" + +void BSP_SetError( const char *fmt, ... ) q_printf( 1, 2 ); + +extern mtexinfo_t nulltexinfo; + +static cvar_t *map_override; + +/* +=============================================================================== + + LUMP LOADING + +=============================================================================== +*/ + +#define BSP_Malloc( size ) Hunk_Alloc( &bsp->pool, size ) + +#define LOAD( Func ) \ + static qboolean BSP_Load##Func( bsp_t *bsp, void *base, size_t count ) + +/* +================= +Visibility +================= +*/ +LOAD( Visibility ) { + unsigned numclusters, bitofs; + int i, j; + + if( !count ) { + return qtrue; + } + + bsp->numvisibility = count; + bsp->vis = BSP_Malloc( count ); + memcpy( bsp->vis, base, count ); + + numclusters = LittleLong( bsp->vis->numclusters ); + if( numclusters > ( count - 4 ) / 8 ) { + BSP_SetError( "%s: bad numclusters", __func__ ); + } + bsp->vis->numclusters = numclusters; + bsp->visrowsize = ( numclusters + 7 ) >> 3; + for( i = 0; i < numclusters; i++ ) { + for( j = 0; j < 2; j++ ) { + bitofs = LittleLong( bsp->vis->bitofs[i][j] ); + if( bitofs >= count ) { + BSP_SetError( "%s: bad bitofs", __func__ ); + return qfalse; + } + bsp->vis->bitofs[i][j] = bitofs; + } + } + + return qtrue; +} + +/* +================= +Texinfo +================= +*/ +LOAD( Texinfo ) { + dtexinfo_t *in; + mtexinfo_t *out; + int i; +#if USE_REF + int j, k; + int next; + mtexinfo_t *step; +#endif + + bsp->numtexinfo = count; + bsp->texinfo = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->texinfo; + for( i = 0; i < count; i++, in++, out++ ) { + memcpy( out->c.name, in->texture, sizeof( out->c.name ) ); + out->c.name[ sizeof( out->c.name ) - 1 ] = 0; + memcpy( out->name, in->texture, sizeof( out->name ) ); + out->name[ sizeof( out->name ) - 1 ] = 0; + out->c.flags = LittleLong (in->flags); + out->c.value = LittleLong (in->value); + +#if USE_REF + for( j = 0; j < 2; j++ ) { + for( k = 0; k < 3; k++ ) { + out->axis[j][k] = LittleFloat( in->vecs[j][k] ); + } + out->offset[j] = LittleFloat( in->vecs[j][k] ); + } + + next = LittleLong( in->nexttexinfo ); + if( next > 0 ) { + if( next >= count ) { + BSP_SetError( "%s: bad anim chain", __func__ ); + return qfalse; + } + out->next = bsp->texinfo + next; + } else { + out->next = NULL; + } +#endif + } + +#if USE_REF + // count animation frames + out = bsp->texinfo; + for( i = 0; i < count; i++, out++ ) { + out->numframes = 1; + for( step = out->next; step && step != out; step = step->next ) { + out->numframes++; + } + } +#endif + return qtrue; +} + +/* +================= +Planes +================= +*/ +LOAD( Planes ) { + dplane_t *in; + cplane_t *out; + int i, j; + + bsp->numplanes = count; + bsp->planes = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->planes; + for( i = 0; i < count; i++, in++, out++ ) { + for( j = 0; j < 3; j++ ) { + out->normal[j] = LittleFloat( in->normal[j] ); + } + out->dist = LittleFloat( in->dist ); + SetPlaneType( out ); + SetPlaneSignbits( out ); + } + return qtrue; +} + +/* +================= +BrushSides +================= +*/ +LOAD( BrushSides ) { + dbrushside_t *in; + mbrushside_t *out; + int i; + unsigned planenum, texinfo; + + bsp->numbrushsides = count; + bsp->brushsides = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->brushsides; + for( i = 0; i < count; i++, in++, out++ ) { + planenum = LittleShort (in->planenum); + if( planenum >= bsp->numplanes ) { + BSP_SetError( "%s: bad planenum", __func__ ); + return qfalse; + } + out->plane = bsp->planes + planenum; + texinfo = LittleShort (in->texinfo); + if( texinfo == ( uint16_t )-1 ) { + out->texinfo = &nulltexinfo; + } else { + if (texinfo >= bsp->numtexinfo) { + BSP_SetError( "%s: bad texinfo", __func__ ); + return qfalse; + } + out->texinfo = bsp->texinfo + texinfo; + } + } + return qtrue; +} + +/* +================= +Brushes +================= +*/ +LOAD( Brushes ) { + dbrush_t *in; + mbrush_t *out; + int i; + unsigned firstside, numsides, lastside; + + bsp->numbrushes = count; + bsp->brushes = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->brushes; + for( i = 0; i < count; i++, out++, in++ ) { + firstside = LittleLong(in->firstside); + numsides = LittleLong(in->numsides); + lastside = firstside + numsides; + if( lastside < firstside || lastside > bsp->numbrushsides ) { + BSP_SetError( "%s: bad brushsides", __func__ ); + return qfalse; + } + out->firstbrushside = bsp->brushsides + firstside; + out->numsides = numsides; + out->contents = LittleLong(in->contents); + out->checkcount = 0; + } + return qtrue; +} + +/* +================= +LeafBrushes +================= +*/ +LOAD( LeafBrushes ) { + uint16_t *in; + mbrush_t **out; + int i; + unsigned brushnum; + + bsp->numleafbrushes = count; + bsp->leafbrushes = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->leafbrushes; + for( i = 0; i < count; i++, in++, out++ ) { + brushnum = LittleShort( *in ); + if( brushnum >= bsp->numbrushes ) { + BSP_SetError( "%s: bad brushnum", __func__ ); + return qfalse; + } + *out = bsp->brushes + brushnum; + } + return qtrue; +} + + +#if USE_REF +/* +================= +Lightmap +================= +*/ +LOAD( Lightmap ) { +#if USE_REF == REF_SOFT + byte *in; + byte *out; + int i; + + count /= 3; +#endif + + if( !count ) { + return qtrue; + } + + bsp->numlightmapbytes = count; + bsp->lightmap = BSP_Malloc( count ); + +#if USE_REF == REF_SOFT + // convert the 24 bit lighting down to 8 bit + // by taking the brightest component + in = base; + out = bsp->lightmap; + for( i = 0; i < count; i++, in += 3, out++ ) { + if( in[0] > in[1] && in[0] > in[2] ) + *out = in[0]; + else if( in[1] > in[0] && in[1] > in[2] ) + *out = in[1]; + else + *out = in[2]; + } +#else + memcpy( bsp->lightmap, base, count ); +#endif + return qtrue; +} + +/* +================= +Vertices +================= +*/ +LOAD( Vertices ) { + dvertex_t *in; + mvertex_t *out; + int i, j; + + bsp->numvertices = count; + bsp->vertices = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->vertices; + for( i = 0; i < count; i++, out++, in++ ) { + for( j = 0; j < 3; j++ ) { + out->point[j] = LittleFloat( in->point[j] ); + } + } + return qtrue; +} + +/* +================= +Edges +================= +*/ +LOAD( Edges ) { + dedge_t *in; + medge_t *out; + int i, j; + unsigned vertnum; + + bsp->numedges = count; + bsp->edges = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->edges; + for( i = 0; i < count; i++, out++, in++ ) { + for( j = 0; j < 2; j++ ) { + vertnum = LittleShort( in->v[j] ); + if( vertnum >= bsp->numvertices ) { + BSP_SetError( "%s: bad vertnum", __func__ ); + return qfalse; + } + out->v[j] = bsp->vertices + vertnum; + } + } + return qtrue; +} + +/* +================= +SurfEdges +================= +*/ +LOAD( SurfEdges ) { + int *in; + msurfedge_t *out; + int i; + int index, vert; + + bsp->numsurfedges = count; + bsp->surfedges = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->surfedges; + for( i = 0; i < count; i++, out++, in++ ) { + index = ( signed int )LittleLong( *in ); + + vert = 0; + if( index < 0 ) { + index = -index; + vert = 1; + } + + if( index >= bsp->numedges ) { + BSP_SetError( "%s: bad edgenum", __func__ ); + } + + out->edge = bsp->edges + index; + out->vert = vert; + } + return qtrue; +} + +/* +================= +Faces +================= +*/ +LOAD( Faces ) { + dface_t *in; + mface_t *out; + int i; +#if USE_REF == REF_SOFT + int j; +#endif + unsigned texinfo, lightofs; + unsigned firstedge, numedges, lastedge; + unsigned planenum, side; + + bsp->numfaces = count; + bsp->faces = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->faces; + for( i = 0; i < count; i++, in++, out++ ) { + firstedge = LittleLong( in->firstedge ); + numedges = LittleShort( in->numedges ); + lastedge = firstedge + numedges; + if( numedges < 3 || lastedge < firstedge || lastedge > bsp->numsurfedges ) { + BSP_SetError( "%s: bad surfedges", __func__ ); + return qfalse; + } + out->firstsurfedge = bsp->surfedges + firstedge; + out->numsurfedges = numedges; + + planenum = LittleShort( in->planenum ); + if( planenum >= bsp->numplanes ) { + BSP_SetError( "%s: bad planenum", __func__ ); + return qfalse; + } + out->plane = bsp->planes + planenum; + + texinfo = LittleShort( in->texinfo ); + if( texinfo >= bsp->numtexinfo ) { + BSP_SetError( "%s: bad texinfo", __func__ ); + return qfalse; + } + out->texinfo = bsp->texinfo + texinfo; + +#if USE_REF == REF_SOFT + for( j = 0; j < MAX_LIGHTMAPS; j++ ) { + out->styles[j] = in->styles[j]; + } +#endif + + lightofs = LittleLong( in->lightofs ); + if( lightofs == ( uint32_t )-1 || bsp->numlightmapbytes == 0 ) { + out->lightmap = NULL; + } else { +#if USE_REF == REF_SOFT + // lighting info is converted from 24 bit on disk to 8 bit + lightofs /= 3; +#endif + if( lightofs >= bsp->numlightmapbytes ) { + BSP_SetError( "%s: bad lightofs", __func__ ); + return qfalse; + } + out->lightmap = bsp->lightmap + lightofs; + } + + side = LittleShort( in->side ); + out->drawflags = side & DSURF_PLANEBACK; + } + return qtrue; +} + +/* +================= +LeafFaces +================= +*/ +LOAD( LeafFaces ) { + uint16_t *in; + mface_t **out; + int i; + unsigned facenum; + + bsp->numleaffaces = count; + bsp->leaffaces = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->leaffaces; + for( i = 0; i < count; i++, in++, out++ ) { + facenum = LittleShort( *in ); + if( facenum >= bsp->numfaces ) { + BSP_SetError( "%s: bad facenum", __func__ ); + return qfalse; + } + *out = bsp->faces + facenum; + } + return qtrue; +} +#endif + +/* +================= +Leafs +================= +*/ +LOAD( Leafs ) { + dleaf_t *in; + mleaf_t *out; + int i, cluster; + unsigned area, firstleafbrush, numleafbrushes; +#if USE_REF + int j; + unsigned firstleafface, numleaffaces; +#endif + + bsp->numleafs = count; + bsp->leafs = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->leafs; + for( i = 0; i < count; i++, in++, out++ ) { + out->plane = NULL; + out->contents = LittleLong (in->contents); + cluster = ( signed short )LittleShort (in->cluster); + if( bsp->vis && cluster != -1 ) { + if( cluster < 0 || cluster >= bsp->vis->numclusters ) { + BSP_SetError( "%s: bad cluster", __func__ ); + return qfalse; + } + } + out->cluster = cluster; + + area = LittleShort( in->area ); + if( area >= bsp->numareas ) { + BSP_SetError( "%s: bad area", __func__ ); + return qfalse; + } + out->area = area; + + firstleafbrush = LittleShort (in->firstleafbrush); + numleafbrushes = LittleShort (in->numleafbrushes); + if( firstleafbrush + numleafbrushes > bsp->numleafbrushes ) { + BSP_SetError( "%s: bad leafbrushes", __func__ ); + return qfalse; + } + out->firstleafbrush = bsp->leafbrushes + firstleafbrush; + out->numleafbrushes = numleafbrushes; + +#if USE_REF + firstleafface = LittleShort (in->firstleafface); + numleaffaces = LittleShort (in->numleaffaces); + if( firstleafface + numleaffaces > bsp->numleaffaces ) { + BSP_SetError( "%s: bad leaffaces", __func__ ); + return qfalse; + } + out->firstleafface = bsp->leaffaces + firstleafface; + out->numleaffaces = numleaffaces; + + for( j = 0; j < 3; j++ ) { + out->mins[j] = ( signed short )LittleShort( in->mins[j] ); + out->maxs[j] = ( signed short )LittleShort( in->maxs[j] ); + } + + out->parent = NULL; + out->visframe = -1; +#endif + } + + if (bsp->leafs[0].contents != CONTENTS_SOLID) { + BSP_SetError( "%s: map leaf 0 is not CONTENTS_SOLID", __func__ ); + return qfalse; + } + return qtrue; +} + +/* +================= +Nodes +================= +*/ +LOAD( Nodes ) { + dnode_t *in; + uint32_t child; + mnode_t *out; + int i, j; + unsigned planeNum; +#if USE_REF + unsigned firstface, numfaces, lastface; +#endif + + if( !count ) { + BSP_SetError( "%s: map with no nodes", __func__ ); + return qfalse; + } + + bsp->numnodes = count; + bsp->nodes = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->nodes; + for( i = 0; i < count; i++, out++, in++ ) { + planeNum = LittleLong(in->planenum); + if( planeNum >= bsp->numplanes ) { + BSP_SetError( "%s: bad planenum", __func__ ); + return qfalse; + } + out->plane = bsp->planes + planeNum; + + for( j = 0; j < 2; j++ ) { + child = LittleLong( in->children[j] ); + if( child & 0x80000000 ) { + child = ~child; + if( child >= bsp->numleafs ) { + BSP_SetError( "%s: bad leafnum", __func__ ); + return qfalse; + } + out->children[j] = ( mnode_t * )( bsp->leafs + child ); + } else { + if( child >= count ) { + BSP_SetError( "%s: bad nodenum", __func__ ); + return qfalse; + } + out->children[j] = bsp->nodes + child; + } + } + +#if USE_REF + firstface = LittleLong( in->firstface ); + numfaces = LittleLong( in->numfaces ); + lastface = firstface + numfaces; + if( lastface < firstface || lastface > bsp->numfaces ) { + BSP_SetError( "%s: bad faces", __func__ ); + return qfalse; + } + out->firstface = bsp->faces + firstface; + out->numfaces = numfaces; + + for( j = 0; j < 3; j++ ) { + out->mins[j] = ( signed short )LittleShort( in->mins[j] ); + out->maxs[j] = ( signed short )LittleShort( in->maxs[j] ); + } + + out->parent = NULL; + out->visframe = -1; +#endif + } + return qtrue; +} + +/* +================= +Submodels +================= +*/ +LOAD( Submodels ) { + dmodel_t *in; + mmodel_t *out; + int i, j; + unsigned headnode; +#if USE_REF + unsigned firstface, numfaces, lastface; +#endif + + bsp->models = BSP_Malloc( sizeof( *out ) * count ); + bsp->nummodels = count; + + in = base; + out = bsp->models; + 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 >= bsp->numnodes ) { + // FIXME: headnode may be garbage for some models + Com_DPrintf( "%s: bad headnode\n", __func__ ); + out->headnode = NULL; + } else { + out->headnode = bsp->nodes + headnode; + } +#if USE_REF + firstface = LittleLong( in->firstface ); + numfaces = LittleLong( in->numfaces ); + lastface = firstface + numfaces; + if( lastface < firstface || lastface > bsp->numfaces ) { + BSP_SetError( "%s: bad faces", __func__ ); + return qfalse; + } + out->firstface = bsp->faces + firstface; + out->numfaces = numfaces; + + out->radius = RadiusFromBounds( out->mins, out->maxs ); +#endif + } + return qtrue; +} + +/* +================= +AreaPortals +================= +*/ +LOAD( AreaPortals ) { + dareaportal_t *in; + mareaportal_t *out; + int i; + unsigned portalnum, otherarea; + + bsp->numareaportals = count; + bsp->areaportals = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->areaportals; + for( i = 0; i < count; i++, in++, out++ ) { + portalnum = LittleLong (in->portalnum); + if( portalnum >= MAX_MAP_AREAPORTALS ) { + BSP_SetError( "%s: bad portalnum", __func__ ); + return qfalse; + } + out->portalnum = portalnum; + otherarea = LittleLong (in->otherarea); + if( otherarea >= MAX_MAP_AREAS ) { + BSP_SetError( "%s: bad otherarea", __func__ ); + return qfalse; + } + out->otherarea = otherarea; + } + return qtrue; +} + +/* +================= +Areas +================= +*/ +LOAD( Areas ) { + darea_t *in; + marea_t *out; + int i; + unsigned numareaportals, firstareaportal, lastareaportal; + + bsp->numareas = count; + bsp->areas = BSP_Malloc( sizeof( *out ) * count ); + + in = base; + out = bsp->areas; + for( i = 0; i < count; i++, in++, out++ ) { + numareaportals = LittleLong (in->numareaportals); + firstareaportal = LittleLong (in->firstareaportal); + lastareaportal = firstareaportal + numareaportals; + if( lastareaportal < firstareaportal || lastareaportal > bsp->numareaportals ) { + BSP_SetError( "%s: bad areaportals", __func__ ); + return qfalse; + } + out->numareaportals = numareaportals; + out->firstareaportal = firstareaportal; + out->floodvalid = 0; + } + return qtrue; +} + +/* +================= +EntityString +================= +*/ +LOAD( EntityString ) { + // optionally load the entity string from external source + if( map_override->integer ) { + char buffer[MAX_QPATH], *str; + size_t len; + + COM_StripExtension( bsp->name, buffer, sizeof( buffer ) ); + Q_strcat( buffer, sizeof( buffer ), ".ent" ); + len = FS_LoadFile( buffer, ( void ** )&str ); + if( str ) { + Com_DPrintf( "Loaded entity string from %s\n", buffer ); + bsp->entitystring = BSP_Malloc( len + 1 ); + memcpy( bsp->entitystring, str, len + 1 ); + bsp->numentitychars = len; + FS_FreeFile( str ); + return qtrue; + } + } + + bsp->numentitychars = count; + bsp->entitystring = BSP_Malloc( count + 1 ); + memcpy( bsp->entitystring, base, count ); + bsp->entitystring[count] = 0; + return qtrue; +} + + +/* +=============================================================================== + + MAP LOADING + +=============================================================================== +*/ + +typedef struct { + qboolean (*load)( bsp_t *, void *, size_t ); + int lump; + size_t size; + size_t maxcount; +} lump_info_t; + +#define LUMP( Func, Lump, Type ) \ + { BSP_Load##Func, LUMP_##Lump, sizeof( Type ), MAX_MAP_##Lump } + +static const lump_info_t bsp_lumps[] = { + LUMP( Visibility, VISIBILITY, byte ), + LUMP( Texinfo, TEXINFO, dtexinfo_t ), + LUMP( Planes, PLANES, dplane_t ), + LUMP( BrushSides, BRUSHSIDES, dbrushside_t ), + LUMP( Brushes, BRUSHES, dbrush_t ), + LUMP( LeafBrushes, LEAFBRUSHES, uint16_t ), + LUMP( AreaPortals, AREAPORTALS, dareaportal_t ), + LUMP( Areas, AREAS, darea_t ), +#if USE_REF + LUMP( Lightmap, LIGHTING, byte ), + LUMP( Vertices, VERTEXES, dvertex_t ), + LUMP( Edges, EDGES, dedge_t ), + LUMP( SurfEdges, SURFEDGES, uint32_t ), + LUMP( Faces, FACES, dface_t ), + LUMP( LeafFaces, LEAFFACES, uint16_t ), +#endif + LUMP( Leafs, LEAFS, dleaf_t ), + LUMP( Nodes, NODES, dnode_t ), + LUMP( Submodels, MODELS, dmodel_t ), + LUMP( EntityString, ENTSTRING, char ), + { NULL } +}; + +static list_t bsp_cache; +static char bsp_error[MAX_QPATH]; + +static void BSP_List_f( void ) { + bsp_t *bsp; + size_t bytes; + + if( LIST_EMPTY( &bsp_cache ) ) { + Com_Printf( "BSP cache is empty\n" ); + return; + } + + Com_Printf( "------------------\n"); + bytes = 0; + + LIST_FOR_EACH( bsp_t, bsp, &bsp_cache, entry ) { + Com_Printf( "%8"PRIz" : %s (%d refs)\n", + bsp->pool.mapped, bsp->name, bsp->refcount ); + bytes += bsp->pool.mapped; + } + Com_Printf( "Total resident: %"PRIz"\n", bytes ); +} + +static bsp_t *BSP_Find( const char *name ) { + bsp_t *bsp; + + LIST_FOR_EACH( bsp_t, bsp, &bsp_cache, entry ) { + if( !FS_pathcmp( bsp->name, name ) ) { + return bsp; + } + } + return NULL; +} + +static qboolean BSP_SetParent( mnode_t *node ) { + mnode_t *child; + + while( node->plane ) { + child = node->children[0]; + if( child->parent ) { + return qfalse; + } + child->parent = node; + BSP_SetParent( child ); + + child = node->children[1]; + if( child->parent ) { + return qfalse; + } + child->parent = node; + node = child; + } + return qtrue; +} + +void BSP_SetError( const char *fmt, ... ) { + va_list argptr; + + va_start( argptr, fmt ); + Q_vsnprintf( bsp_error, sizeof( bsp_error ), fmt, argptr ); + va_end( argptr ); +} + +const char *BSP_GetError( void ) { + return bsp_error; +} + +void BSP_Free( bsp_t *bsp ) { + if( !bsp ) { + return; + } + if( bsp->refcount <= 0 ) { + Com_Error( ERR_FATAL, "%s: negative refcount", __func__ ); + } + if( --bsp->refcount == 0 ) { + Hunk_Free( &bsp->pool ); + List_Remove( &bsp->entry ); + Z_Free( bsp ); + } +} + + +/* +================== +BSP_Load + +Loads in the map and all submodels +================== +*/ +bsp_t *BSP_Load( const char *name ) { + bsp_t *bsp; + byte *buf; + dheader_t *header; + const lump_info_t *info; + size_t filelen, ofs, len, end, count; + + if( !name || !name[0] ) { + Com_Error( ERR_FATAL, "%s: NULL", __func__ ); + } + + BSP_SetError( "no error" ); + + if( ( bsp = BSP_Find( name ) ) != NULL ) { + bsp->refcount++; + return bsp; + } + + + // + // load the file + // + filelen = FS_LoadFile( name, ( void ** )&buf ); + if( !buf ) { + BSP_SetError( "file not found" ); + return NULL; + } + + len = strlen( name ); + bsp = Z_Mallocz( sizeof( *bsp ) + len ); + memcpy( bsp->name, name, len + 1 ); + bsp->refcount = 1; + + // byte swap and validate the header + header = ( dheader_t * )buf; + if( LittleLong( header->ident ) != IDBSPHEADER ) { + BSP_SetError( "not an IBSP file" ); + goto fail2; + } + if( LittleLong( header->version ) != BSPVERSION ) { + BSP_SetError( "unsupported IBSP version" ); + goto fail2; + } + + // load into hunk + Hunk_Begin( &bsp->pool, 0x1000000 ); + + // calculate the checksum + bsp->checksum = LittleLong( Com_BlockChecksum( buf, filelen ) ); + + // byte swap, validate and load all lumps + for( info = bsp_lumps; info->load; info++ ) { + ofs = LittleLong( header->lumps[info->lump].fileofs ); + len = LittleLong( header->lumps[info->lump].filelen ); + end = ofs + len; + if( end < ofs || end > filelen ) { + BSP_SetError( "lump %d extents are out of bounds", info->lump ); + goto fail1; + } + if( len % info->size ) { + BSP_SetError( "lump %d has funny size", info->lump ); + goto fail1; + } + count = len / info->size; + if( count > info->maxcount ) { + BSP_SetError( "lump %d has too many elements", info->lump ); + goto fail1; + } + if( !info->load( bsp, buf + ofs, count ) ) { + goto fail1; + } + } + + if( !BSP_SetParent( bsp->nodes ) ) { + BSP_SetError( "cycle encountered in BSP graph" ); + goto fail1; + } + + Hunk_End( &bsp->pool ); + + FS_FreeFile( buf ); + + List_Append( &bsp_cache, &bsp->entry ); + return bsp; + +fail1: + Hunk_Free( &bsp->pool ); +fail2: + FS_FreeFile( buf ); + Z_Free( bsp ); + return NULL; +} + +/* +=============================================================================== + +HELPER FUNCTIONS + +=============================================================================== +*/ + +#if USE_REF + +mface_t *BSP_LightPoint( mnode_t *node, vec3_t start, vec3_t end, int *ps, int *pt ) { + vec_t startFrac, endFrac, midFrac; + vec3_t _start, mid; + int side; + mface_t *surf; + mtexinfo_t *texinfo; + int i; + int s, t; + + VectorCopy( start, _start ); + while( node->plane ) { + // calculate distancies + startFrac = PlaneDiffFast( _start, node->plane ); + endFrac = PlaneDiffFast( end, node->plane ); + side = ( startFrac < 0 ); + + if( ( endFrac < 0 ) == side ) { + // both points are one the same side + node = node->children[side]; + continue; + } + + // find crossing point + midFrac = startFrac / ( startFrac - endFrac ); + LerpVector( _start, end, midFrac, mid ); + + // check near side + surf = BSP_LightPoint( node->children[side], _start, mid, ps, pt ); + if( surf ) { + return surf; + } + + for( i = 0, surf = node->firstface; i < node->numfaces; i++, surf++ ) { + texinfo = surf->texinfo; + if( texinfo->c.flags & SURF_NOLM_MASK ) { + continue; + } + if( !surf->lightmap ) { + continue; + } + s = DotProduct( texinfo->axis[0], mid ) + texinfo->offset[0]; + t = DotProduct( texinfo->axis[1], mid ) + texinfo->offset[1]; + + s -= surf->texturemins[0]; + t -= surf->texturemins[1]; + if( s < 0 || t < 0 ) { + continue; + } + if( s > surf->extents[0] || t > surf->extents[1] ) { + continue; + } + + *ps = s; + *pt = t; + + return surf; + } + + // check far side + VectorCopy( mid, _start ); + node = node->children[side^1]; + } + + return NULL; +} + +#endif + +byte *BSP_ClusterVis( bsp_t *bsp, byte *mask, int cluster, int vis ) { + byte *in, *out; + int c; + + if( !bsp || !bsp->vis ) { + return memset( mask, 0xff, MAX_MAP_VIS ); + } + if( cluster == -1 ) { + return memset( mask, 0, bsp->visrowsize ); + } + if( cluster < 0 || cluster >= bsp->vis->numclusters ) { + Com_Error( ERR_DROP, "%s: bad cluster", __func__ ); + } + + // decompress vis + in = ( byte * )bsp->vis + bsp->vis->bitofs[cluster][vis]; + out = mask; + do { + if( *in ) { + *out++ = *in++; + continue; + } + + c = in[1]; + in += 2; + if( ( out - mask ) + c > bsp->visrowsize ) { + c = bsp->visrowsize - ( out - mask ); + Com_WPrintf( "%s: overrun\n", __func__ ); + } + while( c ) { + *out++ = 0; + c--; + } + } while( out - mask < bsp->visrowsize ); + + return mask; +} + +mleaf_t *BSP_PointLeaf( mnode_t *node, vec3_t p ) { + float d; + + while( node->plane ) { + d = PlaneDiffFast( p, node->plane ); + if( d < 0 ) + node = node->children[1]; + else + node = node->children[0]; + } + + return ( mleaf_t * )node; +} + +/* +================== +BSP_InlineModel +================== +*/ +mmodel_t *BSP_InlineModel( bsp_t *bsp, const char *name ) { + int num; + + if( !bsp || !name ) { + Com_Error( ERR_DROP, "%s: NULL", __func__ ); + } + if( name[0] != '*' ) { + Com_Error( ERR_DROP, "%s: bad name: %s", __func__, name ); + } + num = atoi( name + 1 ); + if( num < 1 || num >= bsp->nummodels ) { + Com_Error ( ERR_DROP, "%s: bad number: %d", __func__, num ); + } + + return &bsp->models[num]; +} + +void BSP_Init( void ) { + map_override = Cvar_Get( "map_override", "0", 0 ); + + Cmd_AddCommand( "bsplist", BSP_List_f ); + + List_Init( &bsp_cache ); +} + |