//======================================================================= // Copyright XashXT Group 2007 © // cm_model.c - collision model //======================================================================= #include "cm_local.h" #include "sprite.h" #include "byteorder.h" #include "mathlib.h" #include "matrix_lib.h" #include "studio.h" #include "wadfile.h" clipmap_t cm; byte *mod_base; static model_t *sv_models[MAX_MODELS]; // server replacement modeltable static model_t cm_inline[MAX_MAP_MODELS]; // inline bsp models static model_t cm_models[MAX_MODELS]; static int cm_nummodels; model_t *loadmodel; model_t *worldmodel; // cvars convar_t *cm_novis; // default hullmins static vec3_t cm_hullmins[4] = { { -16, -16, -36 }, { -16, -16, -18 }, { 0, 0, 0 }, { -32, -32, -32 }, }; // defualt hummaxs static vec3_t cm_hullmaxs[4] = { { 16, 16, 36 }, { 16, 16, 18 }, { 0, 0, 0 }, { 32, 32, 32 }, }; /* =============================================================================== CM COMMON UTILS =============================================================================== */ script_t *CM_GetEntityScript( void ) { string entfilename; script_t *ents; if( !worldmodel ) return NULL; // check for entfile too com.strncpy( entfilename, worldmodel->name, sizeof( entfilename )); FS_StripExtension( entfilename ); FS_DefaultExtension( entfilename, ".ent" ); if(( ents = Com_OpenScript( entfilename, NULL, 0 ))) { MsgDev( D_INFO, "^2Read entity patch:^7 %s\n", entfilename ); return ents; } return cm.entityscript; } void CM_SetupHulls( float mins[4][3], float maxs[4][3] ) { Mem_Copy( mins, cm_hullmins, sizeof( cm_hullmins )); Mem_Copy( maxs, cm_hullmaxs, sizeof( cm_hullmaxs )); } /* ================ CM_StudioBodyVariations ================ */ static int CM_StudioBodyVariations( int handle ) { studiohdr_t *pstudiohdr; mstudiobodyparts_t *pbodypart; int i, count; pstudiohdr = (studiohdr_t *)Mod_Extradata( handle ); if( !pstudiohdr ) return 0; count = 1; pbodypart = (mstudiobodyparts_t *)((byte *)pstudiohdr + pstudiohdr->bodypartindex); // each body part has nummodels variations so there are as many total variations as there // are in a matrix of each part by each other part for( i = 0; i < pstudiohdr->numbodyparts; i++ ) { count = count * pbodypart[i].nummodels; } return count; } /* ================ CM_FreeModel ================ */ static void CM_FreeModel( model_t *mod ) { if( !mod || !mod->mempool ) return; Mem_FreePool( &mod->mempool ); Mem_Set( mod, 0, sizeof( *mod )); } /* =============================================================================== CM INITALIZE\SHUTDOWN =============================================================================== */ bool CM_InitPhysics( void ) { cm_novis = Cvar_Get( "cm_novis", "0", 0, "force to ignore server visibility" ); Mem_Set( cm.nullrow, 0xFF, MAX_MAP_LEAFS / 8 ); return true; } void CM_FreePhysics( void ) { int i; for( i = 0; i < cm_nummodels; i++ ) CM_FreeModel( &cm_models[i] ); } /* =============================================================================== MAP LOADING =============================================================================== */ /* ================= BSP_LoadSubmodels ================= */ static void BSP_LoadSubmodels( dlump_t *l ) { dmodel_t *in; dmodel_t *out; int i, j, count; in = (void *)(mod_base + l->fileofs); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadModels: funny lump size\n" ); count = l->filelen / sizeof( *in ); if( count < 1 ) Host_Error( "Map %s without models\n", loadmodel->name ); if( count > MAX_MAP_MODELS ) Host_Error( "Map %s has too many models\n", loadmodel->name ); // allocate extradata out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); loadmodel->submodels = out; loadmodel->numsubmodels = count; 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] ); } for( j = 0; j < MAX_MAP_HULLS; j++ ) out->headnode[j] = LittleLong( in->headnode[j] ); out->visleafs = LittleLong( in->visleafs ); out->firstface = LittleLong( in->firstface ); out->numfaces = LittleLong( in->numfaces ); } } /* ================= BSP_LoadTextures ================= */ static void BSP_LoadTextures( dlump_t *l ) { dmiptexlump_t *in; mtexture_t *out; mip_t *mt; int i; if( !l->filelen ) { // no textures loadmodel->textures = NULL; return; } in = (void *)(mod_base + l->fileofs); in->nummiptex = LittleLong( in->nummiptex ); loadmodel->numtextures = in->nummiptex; loadmodel->textures = (mtexture_t **)Mem_Alloc( loadmodel->mempool, loadmodel->numtextures * sizeof( mtexture_t* )); for( i = 0; i < loadmodel->numtextures; i++ ) { in->dataofs[i] = LittleLong( in->dataofs[i] ); if( in->dataofs[i] == -1 ) continue; // bad offset ? mt = (mip_t *)((byte *)in + in->dataofs[i] ); out = Mem_Alloc( loadmodel->mempool, sizeof( *out )); loadmodel->textures[i] = out; com.strnlwr( mt->name, out->name, sizeof( out->name )); // out->contents = CM_ContentsFromShader( out->name ); // FIXME: implement } } /* ================= BSP_LoadTexInfo ================= */ static void BSP_LoadTexInfo( const dlump_t *l ) { dtexinfo_t *in; mtexinfo_t *out; int miptex; int i, j, count; uint surfaceParm = 0; in = (void *)(mod_base + l->fileofs); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadTexInfo: funny lump size in %s\n", loadmodel->name ); count = l->filelen / sizeof( *in ); out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for( i = 0; i < count; i++, in++, out++ ) { for( j = 0; j < 8; j++ ) out->vecs[0][j] = LittleFloat( in->vecs[0][j] ); miptex = LittleLong( in->miptex ); if( miptex < 0 || miptex > loadmodel->numtextures ) Host_Error( "BSP_LoadTexInfo: bad miptex number in '%s'\n", loadmodel->name ); out->texture = loadmodel->textures[miptex]; } } /* ================= BSP_LoadLighting ================= */ static void BSP_LoadLighting( const dlump_t *l ) { byte d, *in, *out; int i; if( !l->filelen ) return; in = (void *)(mod_base + l->fileofs); switch( cm.version ) { case Q1BSP_VERSION: // expand the white lighting data loadmodel->lightdata = Mem_Alloc( loadmodel->mempool, l->filelen * 3 ); out = loadmodel->lightdata; for( i = 0; i < l->filelen; i++ ) { d = *in++; *out++ = d; *out++ = d; *out++ = d; } break; case HLBSP_VERSION: // load colored lighting loadmodel->lightdata = Mem_Alloc( loadmodel->mempool, l->filelen ); Mem_Copy( loadmodel->lightdata, in, l->filelen ); break; } } /* ================= BSP_CalcSurfaceExtents Fills in surf->textureMins and surf->extents ================= */ static void BSP_CalcSurfaceExtents( msurface_t *surf ) { float mins[2], maxs[2], val; int bmins[2], bmaxs[2]; int i, j, e; float *v; if( surf->flags & SURF_DRAWTURB ) { surf->extents[0] = surf->extents[1] = 16384; surf->texturemins[0] = surf->texturemins[1] = -8192; return; } mins[0] = mins[1] = 999999; maxs[0] = maxs[1] = -999999; for( i = 0; i < surf->numedges; i++ ) { e = loadmodel->surfedges[surf->firstedge + i]; if( e >= 0 ) v = (float *)&loadmodel->vertexes[loadmodel->edges[e].v[0]]; else v = (float *)&loadmodel->vertexes[loadmodel->edges[-e].v[1]]; for( j = 0; j < 2; j++ ) { val = DotProduct( v, surf->texinfo->vecs[j] ) + surf->texinfo->vecs[j][3]; if( val < mins[j] ) mins[j] = val; if( val > maxs[j] ) maxs[j] = val; } } for( i = 0; i < 2; i++ ) { bmins[i] = floor( mins[i] / LM_SAMPLE_SIZE ); bmaxs[i] = ceil( maxs[i] / LM_SAMPLE_SIZE ); surf->texturemins[i] = bmins[i] * LM_SAMPLE_SIZE; surf->extents[i] = (bmaxs[i] - bmins[i]) * LM_SAMPLE_SIZE; } } /* ================= BSP_LoadSurfaces ================= */ static void BSP_LoadSurfaces( const dlump_t *l ) { dface_t *in; msurface_t *out; int i, count; int lightofs; in = (void *)(mod_base + l->fileofs); if( l->filelen % sizeof( dface_t )) Host_Error( "BSP_LoadFaces: funny lump size in '%s'\n", loadmodel->name ); count = l->filelen / sizeof( dface_t ); loadmodel->numsurfaces = count; loadmodel->surfaces = Mem_Alloc( loadmodel->mempool, count * sizeof( msurface_t )); out = loadmodel->surfaces; for( i = 0; i < count; i++, in++, out++ ) { out->firstedge = LittleLong( in->firstedge ); out->numedges = LittleLong( in->numedges ); if( LittleShort( in->side )) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + LittleLong( in->planenum ); out->texinfo = loadmodel->texinfo + LittleLong( in->texinfo ); // some DMC maps have bad textures if( out->texinfo->texture ) { if( !com.strncmp( out->texinfo->texture->name, "sky", 3 )) out->flags |= (SURF_DRAWSKY|SURF_DRAWTILED); if( out->texinfo->texture->name[0] == '*' || out->texinfo->texture->name[0] == '!' ) out->flags |= (SURF_DRAWTURB|SURF_DRAWTILED); } BSP_CalcSurfaceExtents( out ); if( out->flags & SURF_DRAWTILED ) lightofs = -1; else lightofs = LittleLong( in->lightofs ); if( loadmodel->lightdata && lightofs != -1 ) { if( cm.version == HLBSP_VERSION ) out->samples = loadmodel->lightdata + lightofs; else out->samples = loadmodel->lightdata + (lightofs * 3); } while( out->numstyles < LM_STYLES && in->styles[out->numstyles] != 255 ) { out->styles[out->numstyles] = in->styles[out->numstyles]; out->numstyles++; } } } /* ================= BSP_LoadVertexes ================= */ static void BSP_LoadVertexes( const dlump_t *l ) { dvertex_t *in; float *out; int i, j, count; in = (void *)( mod_base + l->fileofs ); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadVertexes: funny lump size in %s\n", loadmodel->name ); count = l->filelen / sizeof( *in ); loadmodel->numvertexes = count; out = (float *)loadmodel->vertexes = Mem_Alloc( loadmodel->mempool, count * sizeof( vec3_t )); for( i = 0; i < count; i++, in++, out += 3 ) { for( j = 0; j < 3; j++ ) out[j] = LittleFloat( in->point[j] ); } } /* ================= BSP_LoadEdges ================= */ static void BSP_LoadEdges( const dlump_t *l ) { dedge_t *in, *out; int i, count; in = (void *)( mod_base + l->fileofs ); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadEdges: funny lump size in %s\n", loadmodel->name ); count = l->filelen / sizeof( dedge_t ); loadmodel->edges = out = Mem_Alloc( loadmodel->mempool, count * sizeof( dedge_t )); loadmodel->numedges = count; for( i = 0; i < count; i++, in++, out++ ) { out->v[0] = (word)LittleShort( in->v[0] ); out->v[1] = (word)LittleShort( in->v[1] ); } } /* ================= BSP_LoadSurfEdges ================= */ static void BSP_LoadSurfEdges( const dlump_t *l ) { dsurfedge_t *in, *out; int i, count; in = (void *)( mod_base + l->fileofs ); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadSurfEdges: funny lump size in %s\n", loadmodel->name ); count = l->filelen / sizeof( dsurfedge_t ); loadmodel->surfedges = out = Mem_Alloc( loadmodel->mempool, count * sizeof( dsurfedge_t )); loadmodel->numsurfedges = count; for( i = 0; i < count; i++ ) out[i] = LittleLong( in[i] ); } /* ================= BSP_LoadMarkFaces ================= */ static void BSP_LoadMarkFaces( const dlump_t *l ) { dmarkface_t *in; int i, j, count; in = (void *)( mod_base + l->fileofs ); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadMarkFaces: funny lump size in %s\n", loadmodel->name ); count = l->filelen / sizeof( *in ); loadmodel->marksurfaces = Mem_Alloc( loadmodel->mempool, count * sizeof( msurface_t* )); for( i = 0; i < count; i++ ) { j = LittleLong( in[i] ); if( j < 0 || j >= loadmodel->numsurfaces ) Host_Error( "BSP_LoadMarkFaces: bad surface number in '%s'\n", loadmodel->name ); loadmodel->marksurfaces[i] = loadmodel->surfaces + j; } } /* ================= CM_SetParent ================= */ static void CM_SetParent( mnode_t *node, mnode_t *parent ) { node->parent = parent; if( node->contents < 0 ) return; // it's node CM_SetParent( node->children[0], node ); CM_SetParent( node->children[1], node ); } /* ================= BSP_LoadNodes ================= */ static void BSP_LoadNodes( dlump_t *l ) { dnode_t *in; mnode_t *out; int i, j, p; in = (void *)(mod_base + l->fileofs); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadNodes: funny lump size\n" ); loadmodel->numnodes = l->filelen / sizeof( *in ); if( loadmodel->numnodes < 1 ) Host_Error( "Map %s has no nodes\n", loadmodel->name ); out = loadmodel->nodes = (mnode_t *)Mem_Alloc( loadmodel->mempool, loadmodel->numnodes * sizeof( *out )); for( i = 0; i < loadmodel->numnodes; i++, out++, in++ ) { p = LittleLong( in->planenum ); out->plane = loadmodel->planes + p; out->contents = CONTENTS_NODE; out->firstface = loadmodel->surfaces + LittleLong( in->firstface ); out->numfaces = LittleLong( in->numfaces ); for( j = 0; j < 2; j++ ) { p = LittleShort( in->children[j] ); if( p >= 0 ) out->children[j] = loadmodel->nodes + p; else out->children[j] = (mnode_t *)(loadmodel->leafs + ( -1 - p )); } } // sets nodes and leafs CM_SetParent( loadmodel->nodes, NULL ); } /* ================= BSP_LoadLeafs ================= */ static void BSP_LoadLeafs( dlump_t *l ) { dleaf_t *in; mleaf_t *out; int i, j, p, count; in = (void *)(mod_base + l->fileofs); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadLeafs: funny lump size\n" ); count = l->filelen / sizeof( *in ); if( count < 1 ) Host_Error( "Map %s with no leafs\n", loadmodel->name ); out = (mleaf_t *)Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); loadmodel->leafs = out; loadmodel->numleafs = count; for( i = 0; i < count; i++, in++, out++ ) { p = LittleLong( in->contents ); out->contents = p; out->plane = NULL; // differentiate to nodes p = LittleLong( in->visofs ); if( p == -1 ) out->visdata = NULL; else out->visdata = cm.pvs + p; // will be initialized later out->pasdata = NULL; for( j = 0; j < 4; j++ ) out->ambient_sound_level[j] = in->ambient_level[j]; out->firstmarksurface = loadmodel->marksurfaces + LittleShort( in->firstmarksurface ); out->nummarksurfaces = LittleShort( in->nummarksurfaces ); } if( loadmodel->leafs[0].contents != CONTENTS_SOLID ) Host_Error( "BSP_LoadLeafs: Map %s has leaf 0 is not CONTENTS_SOLID\n", loadmodel->name ); } /* ================= BSP_LoadPlanes ================= */ static void BSP_LoadPlanes( dlump_t *l ) { dplane_t *in; mplane_t *out; int i, j, count; in = (void *)(mod_base + l->fileofs); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadPlanes: funny lump size\n" ); count = l->filelen / sizeof( *in ); if( count < 1 ) Host_Error( "Map %s with no planes\n", loadmodel->name ); out = (mplane_t *)Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); loadmodel->planes = out; loadmodel->numplanes = count; for( i = 0; i < count; i++, in++, out++ ) { for( j = 0; j < 3; j++ ) { out->normal[j] = LittleFloat( in->normal[j] ); if( out->normal[j] < 0.0f ) out->signbits |= 1<dist = LittleFloat( in->dist ); out->type = LittleLong( in->type ); } } /* ================= BSP_LoadVisibility ================= */ static void BSP_LoadVisibility( dlump_t *l ) { if( !l->filelen ) { MsgDev( D_WARN, "map ^2%s^7 has no visibility\n", loadmodel->name ); cm.pvs = cm.phs = NULL; return; } cm.pvs = Mem_Alloc( loadmodel->mempool, l->filelen ); Mem_Copy( cm.pvs, (void *)(mod_base + l->fileofs), l->filelen ); } /* ================= BSP_LoadEntityString ================= */ static void BSP_LoadEntityString( dlump_t *l ) { byte *in; in = (void *)(mod_base + l->fileofs); cm.entityscript = Com_OpenScript( LUMP_ENTITIES, in, l->filelen ); } /* ================= BSP_LoadClipnodes ================= */ static void BSP_LoadClipnodes( dlump_t *l ) { dclipnode_t *in, *out; int i, count; hull_t *hull; in = (void *)(mod_base + l->fileofs); if( l->filelen % sizeof( *in )) Host_Error( "BSP_LoadClipnodes: funny lump size\n" ); count = l->filelen / sizeof( *in ); out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); loadmodel->clipnodes = out; loadmodel->numclipnodes = count; // hulls[0] is a point hull, always zeroed hull = &loadmodel->hulls[1]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; hull->planes = loadmodel->planes; VectorCopy( GI->client_mins[1], hull->clip_mins ); // copy human hull VectorCopy( GI->client_maxs[1], hull->clip_maxs ); VectorSubtract( hull->clip_maxs, hull->clip_mins, cm.hull_sizes[1] ); hull = &loadmodel->hulls[2]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; hull->planes = loadmodel->planes; VectorCopy( GI->client_mins[2], hull->clip_mins ); // copy large hull VectorCopy( GI->client_maxs[2], hull->clip_maxs ); VectorSubtract( hull->clip_maxs, hull->clip_mins, cm.hull_sizes[2] ); hull = &loadmodel->hulls[3]; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; hull->planes = loadmodel->planes; VectorCopy( GI->client_mins[3], hull->clip_mins ); // copy head hull VectorCopy( GI->client_maxs[3], hull->clip_maxs ); VectorSubtract( hull->clip_maxs, hull->clip_mins, cm.hull_sizes[3] ); for( i = 0; i < count; i++, out++, in++ ) { out->planenum = LittleLong( in->planenum ); out->children[0] = LittleShort( in->children[0] ); out->children[1] = LittleShort( in->children[1] ); } } /* ================= CM_MakeHull0 Duplicate the drawing hull structure as a clipping hull ================= */ static void CM_MakeHull0( void ) { mnode_t *in, *child; dclipnode_t *out; hull_t *hull; int i, j, count; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; out = Mem_Alloc( loadmodel->mempool, count * sizeof( *out )); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; hull->planes = loadmodel->planes; for( i = 0; i < count; i++, out++, in++ ) { out->planenum = in->plane - loadmodel->planes; for( j = 0; j < 2; j++ ) { child = in->children[j]; if( child->contents < 0 ) out->children[j] = child->contents; else out->children[j] = child - loadmodel->nodes; } } } /* ================= CM_BrushModel ================= */ static void CM_BrushModel( model_t *mod, byte *buffer ) { dheader_t *header; dmodel_t *bm; int i, j; header = (dheader_t *)buffer; i = LittleLong( header->version ); switch( i ) { case Q1BSP_VERSION: case HLBSP_VERSION: break; default: MsgDev( D_ERROR, "CM_BrushModel: %s has wrong version number (%i should be %i)", loadmodel->name, i, HLBSP_VERSION ); return; } // will be merged later loadmodel->type = mod_brush; cm.version = i; // swap all the lumps mod_base = (byte *)header; for( i = 0; i < sizeof( dheader_t ) / 4; i++ ) ((int *)header)[i] = LittleLong((( int *)header)[i] ); loadmodel->mempool = Mem_AllocPool( va( "sv: ^2%s^7", loadmodel->name )); // load into heap if( header->lumps[LUMP_PLANES].filelen % sizeof( dplane_t )) { // blue-shift swapped lumps BSP_LoadEntityString( &header->lumps[LUMP_PLANES] ); BSP_LoadPlanes( &header->lumps[LUMP_ENTITIES] ); } else { // normal half-life lumps BSP_LoadEntityString( &header->lumps[LUMP_ENTITIES] ); BSP_LoadPlanes( &header->lumps[LUMP_PLANES] ); } BSP_LoadVertexes( &header->lumps[LUMP_VERTEXES] ); BSP_LoadEdges( &header->lumps[LUMP_EDGES] ); BSP_LoadSurfEdges( &header->lumps[LUMP_SURFEDGES] ); BSP_LoadTextures( &header->lumps[LUMP_TEXTURES] ); BSP_LoadLighting( &header->lumps[LUMP_LIGHTING] ); BSP_LoadVisibility( &header->lumps[LUMP_VISIBILITY] ); BSP_LoadTexInfo( &header->lumps[LUMP_TEXINFO] ); BSP_LoadSurfaces( &header->lumps[LUMP_FACES] ); BSP_LoadMarkFaces( &header->lumps[LUMP_MARKSURFACES] ); BSP_LoadLeafs( &header->lumps[LUMP_LEAFS] ); BSP_LoadNodes( &header->lumps[LUMP_NODES] ); BSP_LoadClipnodes( &header->lumps[LUMP_CLIPNODES] ); BSP_LoadSubmodels( &header->lumps[LUMP_MODELS] ); CM_MakeHull0 (); loadmodel->numframes = 2; // regular and alternate animation cm.version = 0; // set up the submodels (FIXME: this is confusing) for( i = 0; i < mod->numsubmodels; i++ ) { model_t *starmod; bm = &mod->submodels[i]; starmod = &cm_inline[i]; *starmod = *loadmodel; starmod->type = mod_brush; starmod->hulls[0].firstclipnode = bm->headnode[0]; for( j = 1; j < MAX_MAP_HULLS; j++ ) { starmod->hulls[j].firstclipnode = bm->headnode[j]; starmod->hulls[j].lastclipnode = mod->numclipnodes - 1; } starmod->firstmodelsurface = bm->firstface; starmod->nummodelsurfaces = bm->numfaces; starmod->numleafs = bm->visleafs + 1; // include solid leaf VectorCopy( bm->maxs, starmod->maxs ); VectorCopy( bm->mins, starmod->mins ); // copy worldinfo back to cm_models[0] if( i == 0 ) *loadmodel = *starmod; starmod->numleafs = bm->visleafs; com.sprintf( starmod->name, "*%i", i + 1 ); } } static void CM_StudioModel( model_t *mod, byte *buffer ) { studiohdr_t *phdr; mstudioseqdesc_t *pseqdesc; phdr = (studiohdr_t *)buffer; if( phdr->version != STUDIO_VERSION ) { MsgDev( D_ERROR, "CM_StudioModel: %s has wrong version number (%i should be %i)\n", loadmodel->name, phdr->version, STUDIO_VERSION ); return; } loadmodel->type = mod_studio; pseqdesc = (mstudioseqdesc_t *)((byte *)phdr + phdr->seqindex); loadmodel->numframes = pseqdesc[0].numframes; loadmodel->registration_sequence = cm.registration_sequence; loadmodel->mempool = Mem_AllocPool( va("^2%s^7", loadmodel->name )); loadmodel->extradata = Mem_Alloc( loadmodel->mempool, LittleLong( phdr->length )); Mem_Copy( loadmodel->extradata, buffer, LittleLong( phdr->length )); // setup bounding box VectorCopy( phdr->bbmin, loadmodel->mins ); VectorCopy( phdr->bbmax, loadmodel->maxs ); } static void CM_SpriteModel( model_t *mod, byte *buffer ) { dsprite_t *phdr; phdr = (dsprite_t *)buffer; if( phdr->version != SPRITE_VERSION ) { MsgDev( D_ERROR, "CM_SpriteModel: %s has wrong version number (%i should be %i)\n", loadmodel->name, phdr->version, SPRITE_VERSION ); return; } loadmodel->type = mod_sprite; loadmodel->numframes = phdr->numframes; loadmodel->registration_sequence = cm.registration_sequence; // setup bounding box loadmodel->mins[0] = loadmodel->mins[1] = -phdr->bounds[0] / 2; loadmodel->maxs[0] = loadmodel->maxs[1] = phdr->bounds[0] / 2; loadmodel->mins[2] = -phdr->bounds[1] / 2; loadmodel->maxs[2] = phdr->bounds[1] / 2; } static model_t *CM_ModForName( const char *name, bool world ) { byte *buf; model_t *mod; int i, size; if( !name || !name[0] ) return NULL; // fast check for worldmodel if( !com.strcmp( name, cm_models[0].name )) return &cm_models[0]; // check for submodel if( name[0] == '*' ) { i = com.atoi( name + 1 ); if( i < 1 || !worldmodel || i >= worldmodel->numsubmodels ) { MsgDev( D_ERROR, "CM_InlineModel: bad submodel number %d\n", i ); return NULL; } return &cm_inline[i]; } // search the currently loaded models for( i = 0, mod = cm_models; i < cm_nummodels; i++, mod++ ) { if( !mod->name[0] ) continue; if( !com.strcmp( name, mod->name )) { // prolonge registration mod->registration_sequence = cm.registration_sequence; return mod; } } // find a free model slot spot for( i = 0, mod = cm_models; i < cm_nummodels; i++, mod++ ) if( !mod->name[0] ) break; // free spot if( i == cm_nummodels ) { if( cm_nummodels == MAX_MODELS ) Host_Error( "Mod_ForName: MAX_MODELS limit exceeded\n" ); cm_nummodels++; } buf = FS_LoadFile( name, &size ); if( !buf ) { MsgDev( D_ERROR, "CM_LoadModel: %s couldn't load\n", name ); return NULL; } // if it's world - calc the map checksum if( world ) cm.checksum = LittleLong( Com_BlockChecksum( buf, size )); MsgDev( D_NOTE, "CM_LoadModel: %s\n", name ); com.strncpy( mod->name, name, sizeof( mod->name )); mod->registration_sequence = cm.registration_sequence; // register mod mod->type = mod_bad; loadmodel = mod; // call the apropriate loader switch( LittleLong( *(uint *)buf )) { case IDSTUDIOHEADER: CM_StudioModel( mod, buf ); break; case IDSPRITEHEADER: CM_SpriteModel( mod, buf ); break; default: CM_BrushModel( mod, buf ); break; } Mem_Free( buf ); if( mod->type == mod_bad ) { CM_FreeModel( mod ); // check for loading problems if( world ) Host_Error( "Mod_ForName: %s unknown format\n", name ); else MsgDev( D_ERROR, "Mod_ForName: %s unknown format\n", name ); return NULL; } return mod; } static void CM_FreeWorld( void ) { if( worldmodel ) CM_FreeModel( &cm_models[0] ); if( cm.entityscript ) { Com_CloseScript( cm.entityscript ); cm.entityscript = NULL; } worldmodel = NULL; } /* ================== CM_BeginRegistration Loads in the map and all submodels ================== */ void CM_BeginRegistration( const char *name, bool clientload, uint *checksum ) { // now replacement table is invalidate Mem_Set( sv_models, 0, sizeof( sv_models )); if( !com.strlen( name )) { CM_FreeWorld (); sv_models[1] = NULL; // no worldmodel if( checksum ) *checksum = 0; return; } if( !com.strcmp( cm_models[0].name, name )) { // singleplayer mode: server already loading map if( checksum ) *checksum = cm.checksum; if( !clientload ) { // reset the entity script Com_ResetScript( cm.entityscript ); } sv_models[1] = cm_models; // make link to world // still have the right version return; } CM_FreeWorld (); cm.registration_sequence++; // all models are invalid // load the newmap worldmodel = CM_ModForName( name, true ); if( !worldmodel ) Host_Error( "Couldn't load %s\n", name ); sv_models[1] = cm_models; // make link to world if( checksum ) *checksum = cm.checksum; CM_CalcPHS (); } void CM_EndRegistration( void ) { model_t *mod; int i; for( i = 0, mod = cm_models; i < cm_nummodels; i++, mod++ ) { if( !mod->name[0] ) continue; if( mod->registration_sequence != cm.registration_sequence ) CM_FreeModel( mod ); } } /* ================== CM_ClipHandleToModel ================== */ model_t *CM_ClipHandleToModel( int handle ) { if( handle < 0 || handle > MAX_MODELS ) { Host_Error( "CM_ClipHandleToModel: bad handle #%i\n", handle ); return NULL; } return sv_models[handle]; } /* =================== Mod_Extradata =================== */ void *Mod_Extradata( int handle ) { model_t *mod = CM_ClipHandleToModel( handle ); if( mod && mod->type == mod_studio ) return mod->extradata; return NULL; } /* =================== Mod_GetFrames =================== */ void Mod_GetFrames( int handle, int *numFrames ) { model_t *mod = CM_ClipHandleToModel( handle ); if( !numFrames ) return; if( !mod ) { *numFrames = 1; return; } if( mod->type == mod_sprite ) *numFrames = mod->numframes; else if( mod->type == mod_studio ) *numFrames = CM_StudioBodyVariations( handle ); if( *numFrames < 1 ) *numFrames = 1; } /* =================== CM_GetModelType =================== */ modtype_t CM_GetModelType( int handle ) { model_t *mod = CM_ClipHandleToModel( handle ); if( !mod ) return mod_bad; return mod->type; } /* =================== Mod_GetBounds =================== */ void Mod_GetBounds( int handle, vec3_t mins, vec3_t maxs ) { model_t *cmod = CM_ClipHandleToModel( handle ); if( cmod ) { if( mins ) VectorCopy( cmod->mins, mins ); if( maxs ) VectorCopy( cmod->maxs, maxs ); } else { MsgDev( D_ERROR, "Mod_GetBounds: NULL model %i\n", handle ); if( mins ) VectorClear( mins ); if( maxs ) VectorClear( maxs ); } } bool CM_RegisterModel( const char *name, int index ) { model_t *mod; if( index < 0 || index > MAX_MODELS ) return false; // this array used for acess to servermodels mod = CM_ModForName( name, false ); sv_models[index] = mod; return (mod != NULL); }