quakeforge/libs/models/brush/model_brush.c
Bill Currie cd159e1cc1 Use LoadBSPMem to load brush models.
Now that Mod_LoadBrushModel uses bspfile.[ch], the engine uses the same
code as the tools to work with bsp files.
2010-10-13 20:52:05 +09:00

899 lines
21 KiB
C

/*
model_brush.c
model loading and caching
Copyright (C) 1996-1997 Id Software, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
// models are the only shared resource between a client and server running
// on the same machine.
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
static __attribute__ ((used)) const char rcsid[] =
"$Id$";
#ifdef HAVE_STRING_H
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#include <math.h>
#include "QF/checksum.h"
#include "QF/cvar.h"
#include "QF/model.h"
#include "QF/qendian.h"
#include "QF/quakefs.h"
#include "QF/render.h"
#include "QF/sys.h"
#include "QF/va.h"
#include "compat.h"
byte mod_novis[MAX_MAP_LEAFS / 8];
VISIBLE cvar_t *gl_sky_divide; //FIXME visibility?
VISIBLE mleaf_t *
Mod_PointInLeaf (const vec3_t p, model_t *model)
{
float d;
mnode_t *node;
mplane_t *plane;
if (!model || !model->nodes)
Sys_Error ("Mod_PointInLeaf: bad model");
node = model->nodes;
while (1) {
if (node->contents < 0)
return (mleaf_t *) node;
plane = node->plane;
d = DotProduct (p, plane->normal) - plane->dist;
if (d >= 0)
node = node->children[0];
else
node = node->children[1];
}
return NULL; // never reached
}
static inline byte *
Mod_DecompressVis (byte * in, model_t *model)
{
static byte decompressed[MAX_MAP_LEAFS / 8];
byte *out;
int row, c;
row = (model->numleafs + 7) >> 3;
out = decompressed;
if (!in) { // no vis info, so make all visible
while (row) {
*out++ = 0xff;
row--;
}
return decompressed;
}
do {
if (*in) {
*out++ = *in++;
continue;
}
c = in[1];
in += 2;
while (c) {
*out++ = 0;
c--;
}
} while (out - decompressed < row);
return decompressed;
}
VISIBLE byte *
Mod_LeafPVS (mleaf_t *leaf, model_t *model)
{
if (leaf == model->leafs)
return mod_novis;
return Mod_DecompressVis (leaf->compressed_vis, model);
}
// BRUSHMODEL LOADING =========================================================
//FIXME SLOW!
static void
mod_unique_miptex_name (texture_t **textures, texture_t *tx, int ind)
{
char name[17];
int num = 0, i;
const char *tag;
strncpy (name, tx->name, 16);
name[16] = 0;
do {
for (i = 0; i < ind; i++)
if (textures[i] && !strcmp (textures[i]->name, tx->name))
break;
if (i == ind)
break;
tag = va ("~%x", num++);
strncpy (tx->name, name, 16);
if (strlen (name) + strlen (tag) <= 15)
strcat (tx->name, tag);
else
strcpy (tx->name + 15 - strlen (tag), tag);
} while (1);
}
static void
Mod_LoadTextures (bsp_t *bsp)
{
dmiptexlump_t *m;
int i, j, pixels, num, max, altmax;
miptex_t *mt;
texture_t *tx, *tx2;
texture_t *anims[10], *altanims[10];
if (!bsp->texdatasize) {
loadmodel->textures = NULL;
return;
}
m = (dmiptexlump_t *) bsp->texdata;
m->nummiptex = LittleLong (m->nummiptex);
loadmodel->numtextures = m->nummiptex;
loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof
(*loadmodel->textures), loadname);
for (i = 0; i < m->nummiptex; i++) {
m->dataofs[i] = LittleLong (m->dataofs[i]);
if (m->dataofs[i] == -1)
continue;
mt = (miptex_t *) ((byte *) m + m->dataofs[i]);
mt->width = LittleLong (mt->width);
mt->height = LittleLong (mt->height);
for (j = 0; j < MIPLEVELS; j++)
mt->offsets[j] = LittleLong (mt->offsets[j]);
if ((mt->width & 15) || (mt->height & 15))
Sys_Error ("Texture %s is not 16 aligned", mt->name);
pixels = mt->width * mt->height / 64 * 85;
tx = Hunk_AllocName (sizeof (texture_t) + pixels, loadname);
loadmodel->textures[i] = tx;
memcpy (tx->name, mt->name, sizeof (tx->name));
mod_unique_miptex_name (loadmodel->textures, tx, i);
memcpy (mt->name, tx->name, sizeof (tx->name));//FIXME ext tex
tx->width = mt->width;
tx->height = mt->height;
for (j = 0; j < MIPLEVELS; j++)
tx->offsets[j] =
mt->offsets[j] + sizeof (texture_t) - sizeof (miptex_t);
// the pixels immediately follow the structures
memcpy (tx + 1, mt + 1, pixels);
if (!strncmp (mt->name, "sky", 3))
loadmodel->skytexture = tx;
else {
Mod_ProcessTexture (mt, tx);
}
}
// sequence the animations
for (i = 0; i < m->nummiptex; i++) {
tx = loadmodel->textures[i];
if (!tx || tx->name[0] != '+')
continue;
if (tx->anim_next)
continue; // already sequenced
// find the number of frames in the animation
memset (anims, 0, sizeof (anims));
memset (altanims, 0, sizeof (altanims));
max = tx->name[1];
altmax = 0;
if (max >= 'a' && max <= 'z')
max -= 'a' - 'A';
if (max >= '0' && max <= '9') {
max -= '0';
altmax = 0;
anims[max] = tx;
max++;
} else if (max >= 'A' && max <= 'J') {
altmax = max - 'A';
max = 0;
altanims[altmax] = tx;
altmax++;
} else
Sys_Error ("Bad animating texture %s", tx->name);
for (j = i + 1; j < m->nummiptex; j++) {
tx2 = loadmodel->textures[j];
if (!tx2 || tx2->name[0] != '+')
continue;
if (strcmp (tx2->name + 2, tx->name + 2))
continue;
num = tx2->name[1];
if (num >= 'a' && num <= 'z')
num -= 'a' - 'A';
if (num >= '0' && num <= '9') {
num -= '0';
anims[num] = tx2;
if (num + 1 > max)
max = num + 1;
} else if (num >= 'A' && num <= 'J') {
num = num - 'A';
altanims[num] = tx2;
if (num + 1 > altmax)
altmax = num + 1;
} else
Sys_Error ("Bad animating texture %s", tx->name);
}
#define ANIM_CYCLE 2
// link them all together
for (j = 0; j < max; j++) {
tx2 = anims[j];
if (!tx2)
Sys_Error ("Missing frame %i of %s", j, tx->name);
tx2->anim_total = max * ANIM_CYCLE;
tx2->anim_min = j * ANIM_CYCLE;
tx2->anim_max = (j + 1) * ANIM_CYCLE;
tx2->anim_next = anims[(j + 1) % max];
if (altmax)
tx2->alternate_anims = altanims[0];
}
for (j = 0; j < altmax; j++) {
tx2 = altanims[j];
if (!tx2)
Sys_Error ("Missing frame %i of %s", j, tx->name);
tx2->anim_total = altmax * ANIM_CYCLE;
tx2->anim_min = j * ANIM_CYCLE;
tx2->anim_max = (j + 1) * ANIM_CYCLE;
tx2->anim_next = altanims[(j + 1) % altmax];
if (max)
tx2->alternate_anims = anims[0];
}
}
}
static void
Mod_LoadVisibility (bsp_t *bsp)
{
if (!bsp->visdatasize) {
loadmodel->visdata = NULL;
return;
}
loadmodel->visdata = Hunk_AllocName (bsp->visdatasize, loadname);
memcpy (loadmodel->visdata, bsp->visdata, bsp->visdatasize);
}
static void
Mod_LoadEntities (bsp_t *bsp)
{
if (!bsp->entdatasize) {
loadmodel->entities = NULL;
return;
}
loadmodel->entities = Hunk_AllocName (bsp->entdatasize, loadname);
memcpy (loadmodel->entities, bsp->entdata, bsp->entdatasize);
}
static void
Mod_LoadVertexes (bsp_t *bsp)
{
dvertex_t *in;
int count, i;
mvertex_t *out;
in = bsp->vertexes;
count = bsp->numvertexes;
out = Hunk_AllocName (count * sizeof (*out), loadname);
loadmodel->vertexes = out;
loadmodel->numvertexes = count;
for (i = 0; i < count; i++, in++, out++) {
out->position[0] = LittleFloat (in->point[0]);
out->position[1] = LittleFloat (in->point[1]);
out->position[2] = LittleFloat (in->point[2]);
}
}
static void
Mod_LoadSubmodels (bsp_t *bsp)
{
dmodel_t *in, *out;
int count, i, j;
in = bsp->models;
count = bsp->nummodels;
out = Hunk_AllocName (count * sizeof (*out), loadname);
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);
}
}
static void
Mod_LoadEdges (bsp_t *bsp)
{
dedge_t *in;
int count, i;
medge_t *out;
in = bsp->edges;
count = bsp->numedges;
out = Hunk_AllocName ((count + 1) * sizeof (*out), loadname);
loadmodel->edges = out;
loadmodel->numedges = count;
for (i = 0; i < count; i++, in++, out++) {
out->v[0] = (unsigned short) LittleShort (in->v[0]);
out->v[1] = (unsigned short) LittleShort (in->v[1]);
}
}
static void
Mod_LoadTexinfo (bsp_t *bsp)
{
float len1, len2;
int count, miptex, i, j;
mtexinfo_t *out;
texinfo_t *in;
in = bsp->texinfo;
count = bsp->numtexinfo;
out = Hunk_AllocName (count * sizeof (*out), loadname);
loadmodel->texinfo = out;
loadmodel->numtexinfo = count;
for (i = 0; i < count; i++, in++, out++) {
for (j = 0; j < 4; j++) {
out->vecs[0][j] = LittleFloat (in->vecs[0][j]);
out->vecs[1][j] = LittleFloat (in->vecs[1][j]);
}
len1 = VectorLength (out->vecs[0]);
len2 = VectorLength (out->vecs[1]);
len1 = (len1 + len2) / 2;
if (len1 < 0.32)
out->mipadjust = 4;
else if (len1 < 0.49)
out->mipadjust = 3;
else if (len1 < 0.99)
out->mipadjust = 2;
else
out->mipadjust = 1;
miptex = LittleLong (in->miptex);
out->flags = LittleLong (in->flags);
if (!loadmodel->textures) {
out->texture = r_notexture_mip; // checkerboard texture
out->flags = 0;
} else {
if (miptex >= loadmodel->numtextures)
Sys_Error ("miptex >= loadmodel->numtextures");
out->texture = loadmodel->textures[miptex];
if (!out->texture) {
out->texture = r_notexture_mip; // texture not found
out->flags = 0;
}
}
}
}
/*
CalcSurfaceExtents
Fills in s->texturemins[] and s->extents[]
*/
static void
CalcSurfaceExtents (msurface_t *s)
{
float mins[2], maxs[2], val;
int e, i, j;
int bmins[2], bmaxs[2];
mtexinfo_t *tex;
mvertex_t *v;
mins[0] = mins[1] = 999999;
maxs[0] = maxs[1] = -99999;
tex = s->texinfo;
for (i = 0; i < s->numedges; i++) {
e = loadmodel->surfedges[s->firstedge + i];
if (e >= 0)
v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
else
v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
for (j = 0; j < 2; j++) {
val = v->position[0] * tex->vecs[j][0] +
v->position[1] * tex->vecs[j][1] +
v->position[2] * tex->vecs[j][2] + tex->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] / 16);
bmaxs[i] = ceil (maxs[i] / 16);
s->texturemins[i] = bmins[i] * 16;
s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
// FIXME even 512 is really too small, need a saner test
if (!(tex->flags & TEX_SPECIAL) && s->extents[i] > 512)
Sys_Error ("Bad surface extents: %d %x %d %d", i, tex->flags,
s->extents[i], LongSwap (s->extents[i]));
}
}
static void
Mod_LoadFaces (bsp_t *bsp)
{
dface_t *in;
int count, planenum, side, surfnum, i;
msurface_t *out;
in = bsp->faces;
count = bsp->numfaces;
out = Hunk_AllocName (count * sizeof (*out), loadname);
loadmodel->surfaces = out;
loadmodel->numsurfaces = count;
for (surfnum = 0; surfnum < count; surfnum++, in++, out++) {
out->firstedge = LittleLong (in->firstedge);
out->numedges = LittleShort (in->numedges);
out->flags = 0;
planenum = LittleShort (in->planenum);
side = LittleShort (in->side);
if (side)
out->flags |= SURF_PLANEBACK;
out->plane = loadmodel->planes + planenum;
out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
CalcSurfaceExtents (out);
// lighting info
for (i = 0; i < MAXLIGHTMAPS; i++)
out->styles[i] = in->styles[i];
i = LittleLong (in->lightofs);
if (i == -1)
out->samples = NULL;
else
out->samples = loadmodel->lightdata + (i * mod_lightmap_bytes);
// set the drawing flags flag
if (!out->texinfo->texture || !out->texinfo->texture->name)
continue; // avoid crashing on null textures
if (!strncmp (out->texinfo->texture->name, "sky", 3)) { // sky
out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED);
if (gl_sky_divide && gl_sky_divide->int_val)
Mod_SubdivideSurface (out);
continue;
}
if (out->texinfo->texture->name[0] == '*') { // turbulent
out->flags |= (SURF_DRAWTURB
| SURF_DRAWTILED
| SURF_LIGHTBOTHSIDES);
for (i = 0; i < 2; i++) {
out->extents[i] = 16384;
out->texturemins[i] = -8192;
}
Mod_SubdivideSurface (out); // cut up polygon for warps
continue;
}
}
}
static void
Mod_SetParent (mnode_t *node, mnode_t *parent)
{
node->parent = parent;
if (node->contents < 0)
return;
Mod_SetParent (node->children[0], node);
Mod_SetParent (node->children[1], node);
}
static void
Mod_LoadNodes (bsp_t *bsp)
{
dnode_t *in;
int count, i, j, p;
mnode_t *out;
in = bsp->nodes;
count = bsp->numnodes;
out = Hunk_AllocName (count * sizeof (*out), loadname);
loadmodel->nodes = out;
loadmodel->numnodes = count;
for (i = 0; i < count; i++, in++, out++) {
for (j = 0; j < 3; j++) {
out->minmaxs[j] = LittleShort (in->mins[j]);
out->minmaxs[3 + j] = LittleShort (in->maxs[j]);
}
p = LittleLong (in->planenum);
out->plane = loadmodel->planes + p;
out->firstsurface = LittleShort (in->firstface);
out->numsurfaces = LittleShort (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));
}
}
Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
}
static void
Mod_LoadLeafs (bsp_t *bsp)
{
dleaf_t *in;
int count, i, j, p;
mleaf_t *out;
qboolean isnotmap = true;
in = bsp->leafs;
count = bsp->numleafs;
out = Hunk_AllocName (count * sizeof (*out), loadname);
loadmodel->leafs = out;
loadmodel->numleafs = count;
// snprintf(s, sizeof (s), "maps/%s.bsp",
// Info_ValueForKey(cl.serverinfo,"map"));
if (!strncmp ("maps/", loadmodel->name, 5))
isnotmap = false;
for (i = 0; i < count; i++, in++, out++) {
for (j = 0; j < 3; j++) {
out->mins[j] = LittleShort (in->mins[j]);
out->maxs[j] = LittleShort (in->maxs[j]);
}
p = LittleLong (in->contents);
out->contents = p;
out->firstmarksurface = loadmodel->marksurfaces +
LittleShort (in->firstmarksurface);
out->nummarksurfaces = LittleShort (in->nummarksurfaces);
p = LittleLong (in->visofs);
if (p == -1)
out->compressed_vis = NULL;
else
out->compressed_vis = loadmodel->visdata + p;
out->efrags = NULL;
for (j = 0; j < 4; j++)
out->ambient_sound_level[j] = in->ambient_level[j];
// gl underwater warp
if (out->contents != CONTENTS_EMPTY) {
for (j = 0; j < out->nummarksurfaces; j++)
out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
}
if (isnotmap) {
for (j = 0; j < out->nummarksurfaces; j++)
out->firstmarksurface[j]->flags |= SURF_DONTWARP;
}
}
}
static void
Mod_LoadClipnodes (bsp_t *bsp)
{
dclipnode_t *in, *out;
hull_t *hull;
int count, i;
in = bsp->clipnodes;
count = bsp->numclipnodes;
out = Hunk_AllocName (count * sizeof (*out), loadname);
loadmodel->clipnodes = out;
loadmodel->numclipnodes = count;
hull = &loadmodel->hulls[1];
loadmodel->hull_list[1] = hull;
hull->clipnodes = out;
hull->firstclipnode = 0;
hull->lastclipnode = count - 1;
hull->planes = loadmodel->planes;
hull->clip_mins[0] = -16;
hull->clip_mins[1] = -16;
hull->clip_mins[2] = -24;
hull->clip_maxs[0] = 16;
hull->clip_maxs[1] = 16;
hull->clip_maxs[2] = 32;
hull = &loadmodel->hulls[2];
loadmodel->hull_list[2] = hull;
hull->clipnodes = out;
hull->firstclipnode = 0;
hull->lastclipnode = count - 1;
hull->planes = loadmodel->planes;
hull->clip_mins[0] = -32;
hull->clip_mins[1] = -32;
hull->clip_mins[2] = -24;
hull->clip_maxs[0] = 32;
hull->clip_maxs[1] = 32;
hull->clip_maxs[2] = 64;
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]);
if ((out->children[0] >= 0
&& (out->children[0] < hull->firstclipnode
|| out->children[0] > hull->lastclipnode))
|| (out->children[1] >= 0
&& (out->children[1] < hull->firstclipnode
|| out->children[1] > hull->lastclipnode)))
Sys_Error ("Mod_LoadClipnodes: bad node number");
}
}
/*
Mod_MakeHull0
Replicate the drawing hull structure as a clipping hull
*/
static void
Mod_MakeHull0 (void)
{
dclipnode_t *out;
hull_t *hull;
int count, i, j;
mnode_t *in, *child;
hull = &loadmodel->hulls[0];
loadmodel->hull_list[0] = hull;
in = loadmodel->nodes;
count = loadmodel->numnodes;
out = Hunk_AllocName (count * sizeof (*out), loadname);
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;
}
}
}
static void
Mod_LoadMarksurfaces (bsp_t *bsp)
{
int count, i, j;
msurface_t **out;
uint16_t *in;
in = bsp->marksurfaces;
count = bsp->nummarksurfaces;
out = Hunk_AllocName (count * sizeof (*out), loadname);
loadmodel->marksurfaces = out;
loadmodel->nummarksurfaces = count;
for (i = 0; i < count; i++) {
j = LittleShort (in[i]);
if (j >= loadmodel->numsurfaces)
Sys_Error ("Mod_ParseMarksurfaces: bad surface number");
out[i] = loadmodel->surfaces + j;
}
}
static void
Mod_LoadSurfedges (bsp_t *bsp)
{
int count, i;
int32_t *in;
int *out;
in = bsp->surfedges;
count = bsp->numsurfedges;
out = Hunk_AllocName (count * sizeof (*out), loadname);
loadmodel->surfedges = out;
loadmodel->numsurfedges = count;
for (i = 0; i < count; i++)
out[i] = LittleLong (in[i]);
}
static void
Mod_LoadPlanes (bsp_t *bsp)
{
dplane_t *in;
int bits, count, i, j;
mplane_t *out;
in = bsp->planes;
count = bsp->numplanes;
out = Hunk_AllocName (count * 2 * sizeof (*out), loadname);
loadmodel->planes = out;
loadmodel->numplanes = count;
for (i = 0; i < count; i++, in++, out++) {
bits = 0;
for (j = 0; j < 3; j++) {
out->normal[j] = LittleFloat (in->normal[j]);
if (out->normal[j] < 0)
bits |= 1 << j;
}
out->dist = LittleFloat (in->dist);
out->type = LittleLong (in->type);
out->signbits = bits;
}
}
static void
do_checksums (const bsp_t *bsp, void *_mod)
{
int i;
model_t *mod = (model_t *) _mod;
byte *base;
base = (byte *) bsp->header;
// checksum all of the map, except for entities
mod->checksum = 0;
mod->checksum2 = 0;
for (i = 0; i < HEADER_LUMPS; i++) {
lump_t *lump = bsp->header->lumps + i;
int csum;
if (i == LUMP_ENTITIES)
continue;
csum = Com_BlockChecksum (base + lump->fileofs, lump->filelen);
mod->checksum ^= csum;
if (i != LUMP_VISIBILITY && i != LUMP_LEAFS && i != LUMP_NODES)
mod->checksum2 ^= csum;
}
}
void
Mod_LoadBrushModel (model_t *mod, void *buffer)
{
dmodel_t *bm;
int i, j;
bsp_t *bsp;
loadmodel->type = mod_brush;
bsp = LoadBSPMem (buffer, qfs_filesize, do_checksums, mod);
// load into heap
Mod_LoadVertexes (bsp);
Mod_LoadEdges (bsp);
Mod_LoadSurfedges (bsp);
Mod_LoadTextures (bsp);
Mod_LoadLighting (bsp);
Mod_LoadPlanes (bsp);
Mod_LoadTexinfo (bsp);
Mod_LoadFaces (bsp);
Mod_LoadMarksurfaces (bsp);
Mod_LoadVisibility (bsp);
Mod_LoadLeafs (bsp);
Mod_LoadNodes (bsp);
Mod_LoadClipnodes (bsp);
Mod_LoadEntities (bsp);
Mod_LoadSubmodels (bsp);
Mod_MakeHull0 ();
mod->numframes = 2; // regular and alternate animation
// set up the submodels (FIXME: this is confusing)
for (i = 0; i < mod->numsubmodels; i++) {
bm = &mod->submodels[i];
mod->hulls[0].firstclipnode = bm->headnode[0];
mod->hull_list[0] = &mod->hulls[0];
for (j = 1; j < MAX_MAP_HULLS; j++) {
mod->hulls[j].firstclipnode = bm->headnode[j];
mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
mod->hull_list[j] = &mod->hulls[j];
}
mod->firstmodelsurface = bm->firstface;
mod->nummodelsurfaces = bm->numfaces;
VectorCopy (bm->maxs, mod->maxs);
VectorCopy (bm->mins, mod->mins);
mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
mod->numleafs = bm->visleafs;
if (i < mod->numsubmodels - 1) {
// duplicate the basic information
char name[10];
snprintf (name, sizeof (name), "*%i", i + 1);
loadmodel = Mod_FindName (name);
*loadmodel = *mod;
strcpy (loadmodel->name, name);
mod = loadmodel;
}
}
}