diff options
author | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
---|---|---|
committer | Andrey Nazarov <skuller@skuller.net> | 2007-08-14 20:18:08 +0000 |
commit | f294db4ccf45f6274e65260dd6f9a2c5faa94313 (patch) | |
tree | e8cf1ba2bfe9c8417eec17faf912442f52fc4ef2 /source/r_bsp.c |
Initial import of the new Q2PRO tree.
Diffstat (limited to 'source/r_bsp.c')
-rw-r--r-- | source/r_bsp.c | 763 |
1 files changed, 763 insertions, 0 deletions
diff --git a/source/r_bsp.c b/source/r_bsp.c new file mode 100644 index 0000000..4f31803 --- /dev/null +++ b/source/r_bsp.c @@ -0,0 +1,763 @@ +/* +Copyright (C) 2003-2006 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. + +*/ + +// +// r_bsp.c -- map loading functions common for both renderers +// + +#include "config.h" +#include "q_shared.h" +#include "com_public.h" +#include "q_files.h" +#include "q_list.h" +#include "r_shared.h" + +bspModel_t r_world; + +bspLeaf_t *Bsp_FindLeaf( vec3_t origin ) { + bspNode_t *node; + cplane_t *plane; + vec_t dot; + + node = r_world.nodes; + do { + plane = node->plane; + dot = DotProduct( plane->normal, origin ) - plane->dist; + if( dot >= 0 ) { + node = node->children[0]; + } else { + node = node->children[1]; + } + } while( node->plane ); + + return ( bspLeaf_t * )node; +} + +byte *Bsp_ClusterPVS( int clusterNum ) { + if( !r_world.vis || clusterNum == -1 ) { + return NULL; + } + + return r_world.vis + clusterNum * r_world.rowsize; +} + + +/* ========================================================================= */ + +#define Bsp_Malloc( size ) sys.HunkAlloc( &r_world.pool, size ) + +static byte *loadData; + +static void Bsp_DecompressVis( byte *src, byte *dst, uint32 rowsize ) { + uint32 count; + + do { + if( *src ) { + *dst++ = *src++; + rowsize--; + } else { + src++; count = *src++; + if( count > rowsize ) { + Com_WPrintf( "Bsp_DecompressVis: overrun\n" ); + count = rowsize; + } + rowsize -= count; + while( count-- ) { + *dst++ = 0; + } + } + } while( rowsize ); +} + +static int Bsp_LoadVis( lump_t *lump ) { + dvis_t *src_vis; + byte *dst, *src; + uint32 numClusters, rowsize; + uint32 offset; + int i; + + if( !lump->filelen ) { + r_world.vis = NULL; + r_world.numClusters = 0; + return 0; + } + + if( lump->filelen < sizeof( *src ) ) { + Com_EPrintf( "LoadVis: bad lump size\n" ); + return -1; + } + + src_vis = ( dvis_t * )( loadData + lump->fileofs ); + numClusters = LittleLong( src_vis->numclusters ); + if( !numClusters ) { + r_world.vis = 0; + r_world.numClusters = 0; + return 0; /* it is OK to have a map without vis */ + } + if( numClusters > MAX_MAP_LEAFS/8 ) { + Com_EPrintf( "LoadVis: bad number of clusters\n" ); + return -1; + } + + rowsize = ( numClusters + 7 ) >> 3; + r_world.numClusters = numClusters; + r_world.rowsize = rowsize; + r_world.vis = Bsp_Malloc( numClusters * rowsize ); + + dst = r_world.vis; + for( i = 0; i < numClusters; i++ ) { + offset = LittleLong( src_vis->bitofs[i][DVIS_PVS] ); + if( offset >= lump->filelen ) { + Com_EPrintf( "LoadVis: bad offset\n" ); + return -1; + } + + src = ( byte * )src_vis + offset; + Bsp_DecompressVis( src, dst, rowsize ); + dst += rowsize; + } + + return 0; +} + +static int Bsp_LoadVertices( lump_t *lump ) { + int count; + dvertex_t *src; + dvertex_t *dst; + int i; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadVertices: bad lump size\n" ); + return -1; + } + + r_world.vertices = Bsp_Malloc( ( count + EXTRA_VERTICES ) * sizeof( *dst ) ); + r_world.numVertices = count; + + src = ( dvertex_t * )( loadData + lump->fileofs ); + dst = r_world.vertices; + for( i = 0; i < count; i++ ) { + dst->point[0] = LittleFloat( src->point[0] ); + dst->point[1] = LittleFloat( src->point[1] ); + dst->point[2] = LittleFloat( src->point[2] ); + + src++; dst++; + } + + return 0; +} + +static int Bsp_LoadEdges( lump_t *lump ) { + int count; + dedge_t *src; + dedge_t *dst; + int i; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadEdges: bad lump size\n" ); + return -1; + } + + r_world.edges = Bsp_Malloc( ( count + EXTRA_EDGES ) * sizeof( *dst ) ); + r_world.numEdges = count; + + src = ( dedge_t * )( loadData + lump->fileofs ); + dst = r_world.edges; + for( i = 0; i < count; i++ ) { + dst->v[0] = LittleShort( src->v[0] ); + dst->v[1] = LittleShort( src->v[1] ); + + src++; dst++; + } + + return 0; +} + +static int Bsp_LoadSurfEdges( lump_t *lump ) { + int count; + int *src; + int *dst; + int i; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadSurfEdges: bad lump size\n" ); + return -1; + } + + r_world.surfEdges = Bsp_Malloc( ( count + EXTRA_SURFEDGES ) * sizeof( *dst ) ); + r_world.numSurfEdges = count; + + src = ( int * )( loadData + lump->fileofs ); + dst = r_world.surfEdges; + for( i = 0; i < count; i++ ) { + *dst++ = LittleLong( *src++ ); + } + + return 0; +} + +static int Bsp_LoadTexinfo( lump_t *lump ) { + int count; + int i; + texinfo_t *src; + bspTexinfo_t *dst, *tex; + image_t *texture; + char path[MAX_QPATH]; + int animNext; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadTexinfo: bad lump size\n" ); + return -1; + } + + r_world.numTexinfos = count; + r_world.texinfos = Bsp_Malloc( sizeof( *dst ) * count ); + + src = ( texinfo_t * )( loadData + lump->fileofs ); + dst = r_world.texinfos; + for( i = 0; i < count; i++ ) { + Q_strncpyz( dst->name, src->texture, sizeof( dst->name ) ); + dst->flags = LittleLong( src->flags ); + + dst->axis[0][0] = LittleFloat( src->vecs[0][0] ); + dst->axis[0][1] = LittleFloat( src->vecs[0][1] ); + dst->axis[0][2] = LittleFloat( src->vecs[0][2] ); + + dst->axis[1][0] = LittleFloat( src->vecs[1][0] ); + dst->axis[1][1] = LittleFloat( src->vecs[1][1] ); + dst->axis[1][2] = LittleFloat( src->vecs[1][2] ); + + dst->offset[0] = LittleFloat( src->vecs[0][3] ); + dst->offset[1] = LittleFloat( src->vecs[1][3] ); + +#ifdef OPENGL_RENDERER + upload_texinfo = dst; + + VectorNormalize2( dst->axis[0], dst->normalizedAxis[0] ); + VectorNormalize2( dst->axis[1], dst->normalizedAxis[1] ); +#endif + + animNext = LittleLong( src->nexttexinfo ); + if( animNext > 0 ) { + if( animNext >= count ) { + Com_EPrintf( "Bsp_LoadTexinfo: bad anim chain\n" ); + return -1; + } + dst->animNext = r_world.texinfos + animNext; + } else { + dst->animNext = NULL; + } + + Com_sprintf( path, sizeof( path ), "textures/%s.wal", dst->name ); + texture = R_FindImage( path, it_wall ); + if( texture ) { + dst->image = texture; + } else { + dst->image = r_notexture; + } + +#ifdef OPENGL_RENDERER + upload_texinfo = NULL; +#endif + + src++; dst++; + } + + dst = r_world.texinfos; + for( i = 0; i < count; i++ ) { + dst->numFrames = 1; + for( tex = dst->animNext; tex && tex != dst; tex = tex->animNext ) { + dst->numFrames++; + } + dst++; + } + + return 0; +} + +static int Bsp_LoadFaces( lump_t *lump ) { + int count; + dface_t *src_face; + bspSurface_t *dst_face; + int i, j; + uint32 texinfoNum; + uint32 lightmapOffset; + uint32 firstEdge, numEdges; + uint32 planeNum; + + count = lump->filelen / sizeof( *src_face ); + if( lump->filelen % sizeof( *src_face ) ) { + Com_EPrintf( "LoadFace: bad faces lump size\n" ); + return -1; + } + + r_world.surfaces = Bsp_Malloc( ( count + EXTRA_SURFACES ) * sizeof( *dst_face ) ); + r_world.numSurfaces = count; + + src_face = ( dface_t * )( loadData + lump->fileofs ); + dst_face = r_world.surfaces; + for( i = 0; i < count; i++ ) { + firstEdge = LittleLong( src_face->firstedge ); + numEdges = LittleShort( src_face->numedges ); + if( numEdges < 3 || numEdges > MAX_MAP_SURFEDGES ) { + Com_EPrintf( "LoadFace: bad surfedges\n" ); + return -1; + } + if( firstEdge + numEdges > r_world.numSurfEdges ) { + Com_EPrintf( "LoadFace: surfedges out of bounds\n" ); + return -1; + } + + planeNum = LittleShort( src_face->planenum ); + if( planeNum >= r_world.numPlanes ) { + Com_EPrintf( "LoadFace: bad planenum\n" ); + return -1; + } + + texinfoNum = LittleShort( src_face->texinfo ); + if( texinfoNum >= r_world.numTexinfos ) { + Com_EPrintf( "LoadFace: bad texinfo\n" ); + return -1; + } + + lightmapOffset = LittleLong( src_face->lightofs ); + if( lightmapOffset == ( uint32 )-1 || r_world.lightmapSize == 0 ) { + dst_face->lightmap = NULL; + } else { + if( lightmapOffset >= r_world.lightmapSize ) { + Com_EPrintf( "LoadFace: bad lightofs\n" ); + return -1; + } + dst_face->lightmap = r_world.lightmap + lightmapOffset; + } + + dst_face->texinfo = r_world.texinfos + texinfoNum; + j = LittleShort( src_face->side ); + dst_face->side = j & 1; + dst_face->index = i; + dst_face->type = DSURF_POLY; + dst_face->plane = r_world.planes + planeNum; + dst_face->firstSurfEdge = r_world.surfEdges + firstEdge; + dst_face->numSurfEdges = numEdges; + +#ifdef OPENGL_RENDERER + if( GL_PostProcessSurface( dst_face ) ) { + return -1; + } +#endif + + src_face++; dst_face++; + } + + + return 0; +} + +static int Bsp_LoadLeafFaces( lump_t *lump ) { + int count; + int i; + uint32 faceNum; + uint16 *src; + bspSurface_t **dst; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadLeafFaces: bad lump size\n" ); + return -1; + } + + r_world.numLeafFaces = count; + r_world.leafFaces = Bsp_Malloc( sizeof( *dst ) * count ); + + src = ( uint16 * )( loadData + lump->fileofs ); + dst = r_world.leafFaces; + for( i = 0; i < count; i++ ) { + faceNum = LittleShort( *src ); + if( faceNum >= r_world.numSurfaces ) { + Com_EPrintf( "LoadLeafFaces: bad face index\n" ); + return -1; + } + *dst = r_world.surfaces + faceNum; + + src++; dst++; + } + + return 0; +} + +static int Bsp_LoadLeafs( lump_t *lump ) { + int count; + int i; + uint32 cluster, area; + uint32 firstLeafFace; + uint32 numLeafFaces; + dleaf_t *src; + bspLeaf_t *dst; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadLeafs: bad lump size\n" ); + return -1; + } + + r_world.numLeafs = count; + r_world.leafs = Bsp_Malloc( sizeof( *dst ) * count ); + + src = ( dleaf_t * )( loadData + lump->fileofs ); + dst = r_world.leafs; + for( i = 0; i < count; i++ ) { + dst->index = i; + dst->plane = NULL; + dst->parent = NULL; + dst->visframe = -1; + + area = LittleShort( src->area ); + if( area >= MAX_MAP_AREAS ) { + Com_EPrintf( "LoadLeafs: bad area num\n" ); + return -1; + } + dst->area = area; + dst->contents = LittleLong( src->contents ); + + cluster = LittleShort( src->cluster ); + if( cluster == ( uint16 )-1 || r_world.numClusters == 0 ) { + dst->cluster = -1; + } else { + if( cluster >= r_world.numClusters ) { + Com_EPrintf( "LoadLeafs: bad cluster num\n" ); + return -1; + } + dst->cluster = cluster; + } + + firstLeafFace = LittleShort( src->firstleafface ); + numLeafFaces = LittleShort( src->numleaffaces ); + + if( firstLeafFace + numLeafFaces > r_world.numLeafFaces ) { + Com_EPrintf( "LoadLeafs: bad leafface\n" ); + return -1; + } + + dst->firstLeafFace = r_world.leafFaces + firstLeafFace; + dst->numLeafFaces = numLeafFaces; + + LSV( mins ); + LSV( maxs ); + + src++; dst++; + } + + return 0; +} + +static int Bsp_LoadPlanes( lump_t *lump ) { + int count; + int i; + dplane_t *src; + cplane_t *dst; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadPlanes: bad lump size\n" ); + return -1; + } + + r_world.numPlanes = count; + r_world.planes = Bsp_Malloc( sizeof( *dst ) * count ); + + src = ( dplane_t * )( loadData + lump->fileofs ); + dst = r_world.planes; + for( i = 0; i < count; i++ ) { + LV( normal ); + LF( dist ); + SetPlaneType( dst ); + SetPlaneSignbits( dst ); + + src++; dst++; + } + + return 0; +} + +static int Bsp_LoadNodes( lump_t *lump ) { + int count; + int i, j; + uint32 planenum; + uint32 child; + dnode_t *src; + bspNode_t *dst; + uint32 firstFace, numFaces; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadNodes: bad lump size\n" ); + return -1; + } + + r_world.numNodes = count; + r_world.nodes = Bsp_Malloc( sizeof( *dst ) * count ); + + src = ( dnode_t * )( loadData + lump->fileofs ); + dst = r_world.nodes; + for( i = 0; i < count; i++ ) { + dst->index = i; + dst->parent = NULL; + dst->visframe = -1; + + /* load splitting plane index */ + planenum = LittleLong( src->planenum ); + if( planenum >= r_world.numPlanes ) { + Com_EPrintf( "LoadNodes: bad planenum\n" ); + return -1; + } + dst->plane = r_world.planes + planenum; + + /* load children indices */ + for( j = 0; j < 2; j++ ) { + child = LittleLong( src->children[j] ); + if( child & 0x80000000 ) { + /* leaf index */ + child = ~child; + if( child >= r_world.numLeafs ) { + Com_EPrintf( "LoadNodes: bad leafnum\n" ); + return -1; + } + dst->children[j] = ( bspNode_t * )( r_world.leafs + child ); + } else { + /* node index */ + if( child >= count ) { + Com_EPrintf( "LoadNodes: bad nodenum\n" ); + return -1; + } + dst->children[j] = r_world.nodes + child; + } + } + + firstFace = LittleShort( src->firstface ); + numFaces = LittleShort( src->numfaces ); + + if( firstFace + numFaces > r_world.numSurfaces ) { + Com_EPrintf( "LoadNodes: bad faces\n" ); + return -1; + } + + dst->firstFace = r_world.surfaces + firstFace; + dst->numFaces = numFaces; + + LSV( mins ); + LSV( maxs ); + + src++; dst++; + } + + return 0; +} + +#if 0 +int Bsp_LoadEntityString( lump_t *lump ) { + if( !lump->filelen ) { + return 0; + } + + r_world.entityString = Bsp_Malloc( lump->filelen + 1 ); + memcpy( r_world.entityString, loadData + lump->fileofs, lump->filelen ); + r_world.entityString[lump->filelen] = 0; + + return 0; +} +#endif + +int Bsp_LoadLightMap( lump_t *lump ) { + if( !lump->filelen ) { + return 0; + } + + r_world.lightmap = Bsp_Malloc( lump->filelen ); + memcpy( r_world.lightmap, loadData + lump->fileofs, lump->filelen ); + r_world.lightmapSize = lump->filelen; + + return 0; +} + +static int Bsp_LoadSubmodels( lump_t *lump ) { + int count; + int i; + dmodel_t *src; + bspSubmodel_t *dst; + uint32 firstFace, numFaces; + uint32 headnode; + + count = lump->filelen / sizeof( *src ); + if( lump->filelen % sizeof( *src ) ) { + Com_EPrintf( "LoadSubmodels: bad lump size\n" ); + return -1; + } + + r_world.numSubmodels = count; + r_world.submodels = Bsp_Malloc( sizeof( *dst ) * count ); + + src = ( dmodel_t * )( loadData + lump->fileofs ); + dst = r_world.submodels; + for( i = 0; i < count; i++ ) { + firstFace = LittleLong( src->firstface ); + numFaces = LittleLong( src->numfaces ); + if( firstFace + numFaces > r_world.numSurfaces ) { + Com_EPrintf( "LoadSubmodels: bad faces\n" ); + return -1; + } + + headnode = LittleLong( src->headnode ); + if( headnode >= r_world.numNodes ) { + /* FIXME: headnode may be garbage for some models */ + Com_DPrintf( "LoadSubmodels: bad headnode for model %d\n", i ); + dst->headnode = NULL; + } else { + dst->headnode = r_world.nodes + headnode; + } + + dst->type = MODEL_BSP; + dst->firstFace = r_world.surfaces + firstFace; + dst->numFaces = numFaces; + + LV( mins ); + LV( maxs ); + LV( origin ); + + dst->radius = RadiusFromBounds( dst->mins, dst->maxs ); + + src++; dst++; + } + + return 0; +} + + +static void Bsp_SetParent( bspNode_t *node ) { + bspNode_t *child; + + while( node->plane ) { + child = node->children[0]; + child->parent = node; + Bsp_SetParent( child ); + + child = node->children[1]; + child->parent = node; + node = child; + } + +} + +void Bsp_FreeWorld( void ) { + if( r_world.name[0] ) { + sys.HunkFree( &r_world.pool ); + memset( &r_world, 0, sizeof( r_world ) ); + } +} + +qboolean Bsp_LoadWorld( const char *path ) { + dheader_t header; + lump_t *lump; + int i; + byte *data; + int length; + + length = fs.LoadFileEx( path, ( void ** )&data, FS_FLAG_CACHE ); + if( !data ) { + return qfalse; + } + + if( length < sizeof( header ) ) { + Com_EPrintf( "Bsp_Load: file length < header length\n" ); + goto fail; + } + + /* byte swap and validate the header */ + header = *( dheader_t * )data; + header.ident = LittleLong( header.ident ); + header.version = LittleLong( header.version ); + if( header.ident != IDBSPHEADER ) { + Com_EPrintf( "Bsp_Load: not an IBSP file: %x != %x\n", + header.ident, IDBSPHEADER ); + goto fail; + } + if( header.version != BSPVERSION ) { + Com_EPrintf( "Bsp_Load: wrong version number: %u != %u\n", + header.version, BSPVERSION ); + goto fail; + } + + /* byte swap and validate lumps */ + lump = header.lumps; + for( i = 0; i < HEADER_LUMPS; i++ ) { + lump->fileofs = LittleLong( lump->fileofs ); + lump->filelen = LittleLong( lump->filelen ); + if( lump->fileofs + lump->filelen > length ) { + Com_EPrintf( "Bsp_Load: lump #%d out of bounds\n", i ); + goto fail; + } + lump++; + } + + loadData = data; + + + /* reserve 16 MB of virtual memory */ + sys.HunkBegin( &r_world.pool, 0x1000000 ); + +#define BSP_LOAD( Func, Lump ) \ + if( Bsp_Load##Func( &header.lumps[LUMP_##Lump] ) ) { \ + sys.HunkFree( &r_world.pool ); \ + goto fail; \ + } + + BSP_LOAD( Vis, VISIBILITY ) + BSP_LOAD( Vertices, VERTEXES ) + BSP_LOAD( Edges, EDGES ) + BSP_LOAD( SurfEdges, SURFEDGES ) + BSP_LOAD( Texinfo, TEXINFO ) + BSP_LOAD( LightMap, LIGHTING ) + BSP_LOAD( Planes, PLANES ) + BSP_LOAD( Faces, FACES ) + BSP_LOAD( LeafFaces, LEAFFACES ) + BSP_LOAD( Leafs, LEAFS ) + BSP_LOAD( Nodes, NODES ) + BSP_LOAD( Submodels, MODELS ) +// BSP2_LOAD( EntityString, ENTITIES ) + + fs.FreeFile( data ); + + sys.HunkEnd( &r_world.pool ); + + Bsp_SetParent( r_world.nodes ); + + strcpy( r_world.name, path ); + + return qtrue; + +fail: + fs.FreeFile( data ); + return qfalse; +} + + |