This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/engine/common/model.c

1438 lines
33 KiB
C

//=======================================================================
// Copyright XashXT Group 2007 ©
// cm_model.c - collision model
//=======================================================================
#include "cm_local.h"
#include "sprite.h"
#include "mathlib.h"
#include "matrix_lib.h"
#include "studio.h"
#include "wadfile.h"
#include "world.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 *sv_novis; // disable server culling entities by vis
convar_t *gl_subdivide_size;
// 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( model_t *mod )
{
studiohdr_t *pstudiohdr;
mstudiobodyparts_t *pbodypart;
int i, count;
pstudiohdr = (studiohdr_t *)Mod_Extradata( mod );
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;
}
/*
==================
Mod_PointInLeaf
==================
*/
mleaf_t *Mod_PointInLeaf( const vec3_t p, mnode_t *node )
{
mleaf_t *leaf;
// find which leaf the point is in
while( node->contents >= 0 )
node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p, node->plane->normal)) < node->plane->dist];
leaf = (mleaf_t *)node;
return leaf;
}
int Mod_PointLeafnum( const vec3_t p )
{
// map not loaded
if ( !worldmodel ) return 0;
return Mod_PointInLeaf( p, worldmodel->nodes ) - worldmodel->leafs - 1;
}
/*
======================================================================
LEAF LISTING
======================================================================
*/
static void Mod_BoxLeafnums_r( leaflist_t *ll, mnode_t *node )
{
mplane_t *plane;
int s;
while( 1 )
{
if( node->contents == CONTENTS_SOLID )
return;
if( node->contents < 0 )
{
mleaf_t *leaf = (mleaf_t *)node;
// it's a leaf!
if( ll->count >= ll->maxcount )
{
ll->overflowed = true;
return;
}
ll->list[ll->count++] = leaf - worldmodel->leafs - 1;
return;
}
plane = node->plane;
s = BOX_ON_PLANE_SIDE( ll->mins, ll->maxs, plane );
if( s == 1 )
{
node = node->children[0];
}
else if( s == 2 )
{
node = node->children[1];
}
else
{
// go down both
if( ll->topnode == -1 )
ll->topnode = node - worldmodel->nodes;
Mod_BoxLeafnums_r( ll, node->children[0] );
node = node->children[1];
}
}
}
/*
==================
Mod_BoxLeafnums
==================
*/
int Mod_BoxLeafnums( const vec3_t mins, const vec3_t maxs, short *list, int listsize, int *topnode )
{
leaflist_t ll;
if( !worldmodel ) return 0;
cm.checkcount++;
VectorCopy( mins, ll.mins );
VectorCopy( maxs, ll.maxs );
ll.count = 0;
ll.maxcount = listsize;
ll.list = list;
ll.topnode = -1;
ll.overflowed = false;
Mod_BoxLeafnums_r( &ll, worldmodel->nodes );
if( topnode ) *topnode = ll.topnode;
return ll.count;
}
/*
=============
Mod_BoxVisible
Returns true if any leaf in boxspace
is potentially visible
=============
*/
qboolean Mod_BoxVisible( const vec3_t mins, const vec3_t maxs, const byte *visbits )
{
short leafList[MAX_BOX_LEAFS];
int i, count;
if( !visbits || !mins || !maxs )
return true;
// FIXME: Could save a loop here by traversing the tree in this routine like the code above
count = Mod_BoxLeafnums( mins, maxs, leafList, MAX_BOX_LEAFS, NULL );
for( i = 0; i < count; i++ )
{
int leafnum = leafList[i];
if( visbits[leafnum>>3] & (1<<( leafnum & 7 )))
return true;
}
return false;
}
/*
===============================================================================
POINT TESTING IN HULLS
===============================================================================
*/
/*
==================
Mod_HullPointContents
==================
*/
int Mod_HullPointContents( hull_t *hull, int num, const vec3_t p )
{
while( num >= 0 )
num = hull->clipnodes[num].children[(hull->planes[hull->clipnodes[num].planenum].type < 3 ? p[hull->planes[hull->clipnodes[num].planenum].type] : DotProduct (hull->planes[hull->clipnodes[num].planenum].normal, p)) < hull->planes[hull->clipnodes[num].planenum].dist];
return num;
}
/*
==================
Mod_PointContents
==================
*/
int Mod_PointContents( const vec3_t p )
{
if( !worldmodel ) return 0;
return Mod_HullPointContents( &worldmodel->hulls[0], 0, p );
}
/*
==================
Mod_AmbientLevels
grab the ambient sound levels for current point
==================
*/
void Mod_AmbientLevels( const vec3_t p, byte *pvolumes )
{
mleaf_t *leaf;
if( !worldmodel || !p || !pvolumes )
return;
leaf = Mod_PointInLeaf( p, worldmodel->nodes );
*(int *)pvolumes = *(int *)leaf->ambient_sound_level;
}
/*
================
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
===============================================================================
*/
void Mod_Init( void )
{
sv_novis = Cvar_Get( "sv_novis", "0", 0, "force to ignore server visibility" );
gl_subdivide_size = Cvar_Get( "gl_subdivide_size", "128", CVAR_ARCHIVE, "how large water polygons should be" );
cm.studiopool = Mem_AllocPool( "Studio Cache" );
Mem_Set( cm.nullrow, 0xFF, MAX_MAP_LEAFS / 8 );
}
void Mod_Shutdown( void )
{
int i;
for( i = 0; i < cm_nummodels; i++ )
CM_FreeModel( &cm_models[i] );
Mem_FreePool( &cm.studiopool );
}
/*
===============================================================================
MAP LOADING
===============================================================================
*/
/*
=================
BSP_LoadSubmodels
=================
*/
static void Mod_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( "Mod_LoadBModel: 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] = in->mins[j] - 1;
out->maxs[j] = in->maxs[j] + 1;
out->origin[j] = in->origin[j];
}
for( j = 0; j < MAX_MAP_HULLS; j++ )
out->headnode[j] = in->headnode[j];
out->visleafs = in->visleafs;
out->firstface = in->firstface;
out->numfaces = in->numfaces;
}
}
/*
=================
BSP_LoadTextures
=================
*/
static void BSP_LoadTextures( dlump_t *l )
{
dmiptexlump_t *in;
texture_t *out;
mip_t *mt;
int i;
if( !l->filelen )
{
// no textures
loadmodel->textures = NULL;
return;
}
in = (void *)(mod_base + l->fileofs);
loadmodel->numtextures = in->nummiptex;
loadmodel->textures = (texture_t **)Mem_Alloc( loadmodel->mempool, loadmodel->numtextures * sizeof( texture_t* ));
for( i = 0; i < loadmodel->numtextures; 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] = in->vecs[0][j];
miptex = 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;
color24 *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 = (color24 *)Mem_Alloc( loadmodel->mempool, l->filelen * sizeof( color24 ));
out = loadmodel->lightdata;
for( i = 0; i < l->filelen; i++, out++ )
{
d = *in++;
out->r = d;
out->g = d;
out->b = 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 = in->firstedge;
out->numedges = in->numedges;
if( in->side ) out->flags |= SURF_PLANEBACK;
out->plane = loadmodel->planes + in->planenum;
out->texinfo = loadmodel->texinfo + 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 = in->lightofs;
if( loadmodel->lightdata && lightofs != -1 )
{
if( cm.version == HLBSP_VERSION )
out->samples = loadmodel->lightdata + lightofs;
else out->samples = loadmodel->lightdata + (lightofs * 3);
}
}
}
/*
=================
BSP_LoadVertexes
=================
*/
static void BSP_LoadVertexes( const dlump_t *l )
{
dvertex_t *in;
mvertex_t *out;
int i, 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 = loadmodel->vertexes = Mem_Alloc( loadmodel->mempool, count * sizeof( mvertex_t ));
for( i = 0; i < count; i++, in++, out++ )
{
VectorCopy( in->point, out->position );
}
}
/*
=================
BSP_LoadEdges
=================
*/
static void BSP_LoadEdges( const dlump_t *l )
{
dedge_t *in;
medge_t *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( *in );
loadmodel->edges = out = Mem_Alloc( loadmodel->mempool, count * sizeof( medge_t ));
loadmodel->numedges = count;
for( i = 0; i < count; i++, in++, out++ )
{
out->v[0] = (word)in->v[0];
out->v[1] = (word)in->v[1];
}
}
/*
=================
BSP_LoadSurfEdges
=================
*/
static void BSP_LoadSurfEdges( const dlump_t *l )
{
dsurfedge_t *in, *out;
int 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;
Mem_Copy( out, in, count * sizeof( dsurfedge_t ));
}
/*
=================
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 = 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 = in->planenum;
out->plane = loadmodel->planes + p;
out->firstsurface = in->firstface;
out->numsurfaces = in->numfaces;
for( j = 0; j < 2; j++ )
{
p = 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 = in->contents;
out->contents = p;
p = 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 + in->firstmarksurface;
out->nummarksurfaces = 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] = in->normal[j];
if( out->normal[j] < 0.0f ) out->signbits |= 1<<j;
}
out->dist = in->dist;
out->type = 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);
loadmodel->entities = Mem_Alloc( loadmodel->mempool, l->filelen + 1 );
Mem_Copy( loadmodel->entities, mod_base + l->fileofs, l->filelen );
cm.entityscript = Com_OpenScript( "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 = in->planenum;
out->children[0] = in->children[0];
out->children[1] = 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 = 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;
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] );
Mod_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->cache.data = Mem_Alloc( loadmodel->mempool, phdr->length );
Mem_Copy( loadmodel->cache.data, buffer, 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;
}
model_t *CM_ModForName( const char *name, qboolean 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 )
{
if( world ) Host_Error( "Mod_ForName: %s couldn't load\n", name );
else MsgDev( D_ERROR, "Mod_ForName: %s couldn't load\n", name );
return NULL;
}
// if it's world - calc the map checksum
if( world ) CRC32_MapFile( &cm.checksum, name );
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( *(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;
// purge all submodels
Mem_EmptyPool( cm.studiopool );
}
/*
==================
CM_BeginRegistration
Loads in the map and all submodels
==================
*/
void CM_BeginRegistration( const char *name, qboolean 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];
}
/*
==================
CM_ClipHandleToModel
==================
*/
int CM_ClipModelToHandle( const model_t *pmodel )
{
int i;
if( !pmodel ) return 0;
for( i = 0; i < MAX_MODELS; i++ )
if( sv_models[i] == pmodel )
return i;
return 0;
}
/*
===================
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( mod );
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 );
}
}
qboolean 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 );
}
/*
===============
Mod_Calloc
===============
*/
void *Mod_Calloc( int number, size_t size )
{
if( number <= 0 || size <= 0 ) return NULL;
return Mem_Alloc( cm.studiopool, number * size );
}
/*
===============
Mod_CacheCheck
===============
*/
void *Mod_CacheCheck( cache_user_t *c )
{
return Cache_Check( cm.studiopool, c );
}
/*
===============
Mod_LoadCacheFile
===============
*/
void Mod_LoadCacheFile( const char *path, cache_user_t *cu )
{
byte *buf;
string filepath;
size_t i, size;
ASSERT( cu != NULL );
if( !path || !path[0] ) return;
// replace all '\' with '/'
for( i = ( path[0] == '/' ||path[0] == '\\' ), size = 0; path[i] && ( size < sizeof( filepath )-1 ); i++ )
{
if( path[i] == '\\' ) filepath[size++] = '/';
else filepath[size++] = path[i];
}
if( !size ) return;
filepath[size] = 0;
buf = FS_LoadFile( filepath, &size );
cu->data = Mem_Alloc( cm.studiopool, size );
Mem_Copy( cu->data, buf, size );
Mem_Free( buf );
}
/*
===============
Mod_Extradata
===============
*/
void *Mod_Extradata( model_t *mod )
{
if( mod && mod->type == mod_studio )
return mod->cache.data;
return NULL;
}