yquake2remaster/src/refresh/r_model.c
Yamagi Burmeister 1bee58840d Silence a gcc48 warning due to some "creative" array usage
To my understanding this code and the old code do exactly the same, this
one is just more readable. Therefor this change should be a no-op.
2013-04-07 13:43:17 +02:00

1079 lines
20 KiB
C

/*
* Copyright (C) 1997-2001 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 the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* =======================================================================
*
* Model loading and caching. Includes the .bsp file format
*
* =======================================================================
*/
#include "header/local.h"
#define MAX_MOD_KNOWN 512
model_t *loadmodel;
int modfilelen;
byte mod_novis[MAX_MAP_LEAFS / 8];
model_t mod_known[MAX_MOD_KNOWN];
int mod_numknown;
int registration_sequence;
byte *mod_base;
void LoadSP2(model_t *mod, void *buffer);
void Mod_LoadBrushModel(model_t *mod, void *buffer);
void LoadMD2(model_t *mod, void *buffer);
model_t *Mod_LoadModel(model_t *mod, qboolean crash);
void LM_BuildPolygonFromSurface(msurface_t *fa);
void LM_CreateSurfaceLightmap(msurface_t *surf);
void LM_EndBuildingLightmaps(void);
void LM_BeginBuildingLightmaps(model_t *m);
/* the inline * models from the current map are kept seperate */
model_t mod_inline[MAX_MOD_KNOWN];
mleaf_t *
Mod_PointInLeaf(vec3_t p, model_t *model)
{
mnode_t *node;
float d;
cplane_t *plane;
if (!model || !model->nodes)
{
ri.Sys_Error(ERR_DROP, "Mod_PointInLeaf: bad model");
}
node = model->nodes;
while (1)
{
if (node->contents != -1)
{
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 */
}
byte *
Mod_DecompressVis(byte *in, model_t *model)
{
static byte decompressed[MAX_MAP_LEAFS / 8];
int c;
byte *out;
int row;
row = (model->vis->numclusters + 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;
}
byte *
Mod_ClusterPVS(int cluster, model_t *model)
{
if ((cluster == -1) || !model->vis)
{
return mod_novis;
}
return Mod_DecompressVis((byte *)model->vis +
model->vis->bitofs[cluster][DVIS_PVS],
model);
}
void
Mod_Modellist_f(void)
{
int i;
model_t *mod;
int total;
total = 0;
ri.Con_Printf(PRINT_ALL, "Loaded models:\n");
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
if (!mod->name[0])
{
continue;
}
ri.Con_Printf(PRINT_ALL, "%8i : %s\n", mod->extradatasize, mod->name);
total += mod->extradatasize;
}
ri.Con_Printf(PRINT_ALL, "Total resident: %i\n", total);
}
void
Mod_Init(void)
{
memset(mod_novis, 0xff, sizeof(mod_novis));
}
/*
* Loads in a model for the given name
*/
model_t *
Mod_ForName(char *name, qboolean crash)
{
model_t *mod;
unsigned *buf;
int i;
if (!name[0])
{
ri.Sys_Error(ERR_DROP, "Mod_ForName: NULL name");
}
/* inline models are grabbed only from worldmodel */
if (name[0] == '*')
{
i = (int)strtol(name + 1, (char **)NULL, 10);
if ((i < 1) || !r_worldmodel || (i >= r_worldmodel->numsubmodels))
{
ri.Sys_Error(ERR_DROP, "bad inline model number");
}
return &mod_inline[i];
}
/* search the currently loaded models */
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
if (!mod->name[0])
{
continue;
}
if (!strcmp(mod->name, name))
{
return mod;
}
}
/* find a free model slot spot */
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
if (!mod->name[0])
{
break; /* free spot */
}
}
if (i == mod_numknown)
{
if (mod_numknown == MAX_MOD_KNOWN)
{
ri.Sys_Error(ERR_DROP, "mod_numknown == MAX_MOD_KNOWN");
}
mod_numknown++;
}
strcpy(mod->name, name);
/* load the file */
modfilelen = ri.FS_LoadFile(mod->name, (void **)&buf);
if (!buf)
{
if (crash)
{
ri.Sys_Error(ERR_DROP, "Mod_NumForName: %s not found", mod->name);
}
memset(mod->name, 0, sizeof(mod->name));
return NULL;
}
loadmodel = mod;
/* call the apropriate loader */
switch (LittleLong(*(unsigned *)buf))
{
case IDALIASHEADER:
loadmodel->extradata = Hunk_Begin(0x200000);
LoadMD2(mod, buf);
break;
case IDSPRITEHEADER:
loadmodel->extradata = Hunk_Begin(0x10000);
LoadSP2(mod, buf);
break;
case IDBSPHEADER:
loadmodel->extradata = Hunk_Begin(0x1000000);
Mod_LoadBrushModel(mod, buf);
break;
default:
ri.Sys_Error(ERR_DROP,
"Mod_NumForName: unknown fileid for %s",
mod->name);
break;
}
loadmodel->extradatasize = Hunk_End();
ri.FS_FreeFile(buf);
return mod;
}
void
Mod_LoadLighting(lump_t *l)
{
if (!l->filelen)
{
loadmodel->lightdata = NULL;
return;
}
loadmodel->lightdata = Hunk_Alloc(l->filelen);
memcpy(loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
}
void
Mod_LoadVisibility(lump_t *l)
{
int i;
if (!l->filelen)
{
loadmodel->vis = NULL;
return;
}
loadmodel->vis = Hunk_Alloc(l->filelen);
memcpy(loadmodel->vis, mod_base + l->fileofs, l->filelen);
loadmodel->vis->numclusters = LittleLong(loadmodel->vis->numclusters);
for (i = 0; i < loadmodel->vis->numclusters; i++)
{
loadmodel->vis->bitofs[i][0] = LittleLong(loadmodel->vis->bitofs[i][0]);
loadmodel->vis->bitofs[i][1] = LittleLong(loadmodel->vis->bitofs[i][1]);
}
}
void
Mod_LoadVertexes(lump_t *l)
{
dvertex_t *in;
mvertex_t *out;
int i, count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
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]);
}
}
float
Mod_RadiusFromBounds(vec3_t mins, vec3_t maxs)
{
int i;
vec3_t corner;
for (i = 0; i < 3; i++)
{
corner[i] = fabs(mins[i]) > fabs(maxs[i]) ? fabs(mins[i]) : fabs(maxs[i]);
}
return VectorLength(corner);
}
void
Mod_LoadSubmodels(lump_t *l)
{
dmodel_t *in;
mmodel_t *out;
int i, j, count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(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]);
}
out->radius = Mod_RadiusFromBounds(out->mins, out->maxs);
out->headnode = LittleLong(in->headnode);
out->firstface = LittleLong(in->firstface);
out->numfaces = LittleLong(in->numfaces);
}
}
void
Mod_LoadEdges(lump_t *l)
{
dedge_t *in;
medge_t *out;
int i, count;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc((count + 1) * sizeof(*out));
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]);
}
}
void
Mod_LoadTexinfo(lump_t *l)
{
texinfo_t *in;
mtexinfo_t *out, *step;
int i, j, count;
char name[MAX_QPATH];
int next;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
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]);
}
out->flags = LittleLong(in->flags);
next = LittleLong(in->nexttexinfo);
if (next > 0)
{
out->next = loadmodel->texinfo + next;
}
else
{
out->next = NULL;
}
Com_sprintf(name, sizeof(name), "textures/%s.wal", in->texture);
out->image = R_FindImage(name, it_wall);
if (!out->image)
{
ri.Con_Printf(PRINT_ALL, "Couldn't load %s\n", name);
out->image = r_notexture;
}
}
/* count animation frames */
for (i = 0; i < count; i++)
{
out = &loadmodel->texinfo[i];
out->numframes = 1;
for (step = out->next; step && step != out; step = step->next)
{
out->numframes++;
}
}
}
/*
* Fills in s->texturemins[] and s->extents[]
*/
void
Mod_CalcSurfaceExtents(msurface_t *s)
{
float mins[2], maxs[2], val;
int i, j, e;
mvertex_t *v;
mtexinfo_t *tex;
int bmins[2], bmaxs[2];
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;
}
}
void
Mod_LoadFaces(lump_t *l)
{
dface_t *in;
msurface_t *out;
int i, count, surfnum;
int planenum, side;
int ti;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
loadmodel->surfaces = out;
loadmodel->numsurfaces = count;
currentmodel = loadmodel;
LM_BeginBuildingLightmaps(loadmodel);
for (surfnum = 0; surfnum < count; surfnum++, in++, out++)
{
out->firstedge = LittleLong(in->firstedge);
out->numedges = LittleShort(in->numedges);
out->flags = 0;
out->polys = NULL;
planenum = LittleShort(in->planenum);
side = LittleShort(in->side);
if (side)
{
out->flags |= SURF_PLANEBACK;
}
out->plane = loadmodel->planes + planenum;
ti = LittleShort(in->texinfo);
if ((ti < 0) || (ti >= loadmodel->numtexinfo))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: bad texinfo number");
}
out->texinfo = loadmodel->texinfo + ti;
Mod_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;
}
/* set the drawing flags */
if (out->texinfo->flags & SURF_WARP)
{
out->flags |= SURF_DRAWTURB;
for (i = 0; i < 2; i++)
{
out->extents[i] = 16384;
out->texturemins[i] = -8192;
}
R_SubdivideSurface(out); /* cut up polygon for warps */
}
/* create lightmaps and polygons */
if (!(out->texinfo->flags &
(SURF_SKY | SURF_TRANS33 | SURF_TRANS66 | SURF_WARP)))
{
LM_CreateSurfaceLightmap(out);
}
if (!(out->texinfo->flags & SURF_WARP))
{
LM_BuildPolygonFromSurface(out);
}
}
LM_EndBuildingLightmaps();
}
void
Mod_SetParent(mnode_t *node, mnode_t *parent)
{
node->parent = parent;
if (node->contents != -1)
{
return;
}
Mod_SetParent(node->children[0], node);
Mod_SetParent(node->children[1], node);
}
void
Mod_LoadNodes(lump_t *l)
{
int i, j, count, p;
dnode_t *in;
mnode_t *out;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
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);
out->contents = -1; /* differentiate from leafs */
for (j = 0; j < 2; j++)
{
p = LittleLong(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 */
}
void
Mod_LoadLeafs(lump_t *l)
{
dleaf_t *in;
mleaf_t *out;
int i, j, count, p;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
loadmodel->leafs = out;
loadmodel->numleafs = 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->contents);
out->contents = p;
out->cluster = LittleShort(in->cluster);
out->area = LittleShort(in->area);
out->firstmarksurface = loadmodel->marksurfaces +
LittleShort(in->firstleafface);
out->nummarksurfaces = LittleShort(in->numleaffaces);
}
}
void
Mod_LoadMarksurfaces(lump_t *l)
{
int i, j, count;
short *in;
msurface_t **out;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * sizeof(*out));
loadmodel->marksurfaces = out;
loadmodel->nummarksurfaces = count;
for (i = 0; i < count; i++)
{
j = LittleShort(in[i]);
if ((j < 0) || (j >= loadmodel->numsurfaces))
{
ri.Sys_Error(ERR_DROP, "Mod_ParseMarksurfaces: bad surface number");
}
out[i] = loadmodel->surfaces + j;
}
}
void
Mod_LoadSurfedges(lump_t *l)
{
int i, count;
int *in, *out;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
if ((count < 1) || (count >= MAX_MAP_SURFEDGES))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: bad surfedges count in %s: %i",
loadmodel->name, count);
}
out = Hunk_Alloc(count * sizeof(*out));
loadmodel->surfedges = out;
loadmodel->numsurfedges = count;
for (i = 0; i < count; i++)
{
out[i] = LittleLong(in[i]);
}
}
void
Mod_LoadPlanes(lump_t *l)
{
int i, j;
cplane_t *out;
dplane_t *in;
int count;
int bits;
in = (void *)(mod_base + l->fileofs);
if (l->filelen % sizeof(*in))
{
ri.Sys_Error(ERR_DROP, "MOD_LoadBmodel: funny lump size in %s",
loadmodel->name);
}
count = l->filelen / sizeof(*in);
out = Hunk_Alloc(count * 2 * sizeof(*out));
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;
}
}
void
Mod_LoadBrushModel(model_t *mod, void *buffer)
{
int i;
dheader_t *header;
mmodel_t *bm;
loadmodel->type = mod_brush;
if (loadmodel != mod_known)
{
ri.Sys_Error(ERR_DROP, "Loaded a brush model after the world");
}
header = (dheader_t *)buffer;
i = LittleLong(header->version);
if (i != BSPVERSION)
{
ri.Sys_Error(ERR_DROP, "Mod_LoadBrushModel: %s has wrong version number (%i should be %i)",
mod->name, i, BSPVERSION);
}
/* swap all the lumps */
mod_base = (byte *)header;
for (i = 0; i < sizeof(dheader_t) / 4; i++)
{
((int *)header)[i] = LittleLong(((int *)header)[i]);
}
/* load into heap */
Mod_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
Mod_LoadEdges(&header->lumps[LUMP_EDGES]);
Mod_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
Mod_LoadLighting(&header->lumps[LUMP_LIGHTING]);
Mod_LoadPlanes(&header->lumps[LUMP_PLANES]);
Mod_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
Mod_LoadFaces(&header->lumps[LUMP_FACES]);
Mod_LoadMarksurfaces(&header->lumps[LUMP_LEAFFACES]);
Mod_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
Mod_LoadLeafs(&header->lumps[LUMP_LEAFS]);
Mod_LoadNodes(&header->lumps[LUMP_NODES]);
Mod_LoadSubmodels(&header->lumps[LUMP_MODELS]);
mod->numframes = 2; /* regular and alternate animation */
/* set up the submodels */
for (i = 0; i < mod->numsubmodels; i++)
{
model_t *starmod;
bm = &mod->submodels[i];
starmod = &mod_inline[i];
*starmod = *loadmodel;
starmod->firstmodelsurface = bm->firstface;
starmod->nummodelsurfaces = bm->numfaces;
starmod->firstnode = bm->headnode;
if (starmod->firstnode >= loadmodel->numnodes)
{
ri.Sys_Error(ERR_DROP, "Inline model %i has bad firstnode", i);
}
VectorCopy(bm->maxs, starmod->maxs);
VectorCopy(bm->mins, starmod->mins);
starmod->radius = bm->radius;
if (i == 0)
{
*loadmodel = *starmod;
}
starmod->numleafs = bm->visleafs;
}
}
void
Mod_Free(model_t *mod)
{
Hunk_Free(mod->extradata);
memset(mod, 0, sizeof(*mod));
}
void
Mod_FreeAll(void)
{
int i;
for (i = 0; i < mod_numknown; i++)
{
if (mod_known[i].extradatasize)
{
Mod_Free(&mod_known[i]);
}
}
}
/*
* Specifies the model that will be used as the world
*/
void
R_BeginRegistration(char *model)
{
char fullname[MAX_QPATH];
cvar_t *flushmap;
registration_sequence++;
r_oldviewcluster = -1; /* force markleafs */
Com_sprintf(fullname, sizeof(fullname), "maps/%s.bsp", model);
/* explicitly free the old map if different
this guarantees that mod_known[0] is the
world map */
flushmap = ri.Cvar_Get("flushmap", "0", 0);
if (strcmp(mod_known[0].name, fullname) || flushmap->value)
{
Mod_Free(&mod_known[0]);
}
r_worldmodel = Mod_ForName(fullname, true);
r_viewcluster = -1;
}
struct model_s *
R_RegisterModel(char *name)
{
model_t *mod;
int i;
dsprite_t *sprout;
dmdl_t *pheader;
mod = Mod_ForName(name, false);
if (mod)
{
mod->registration_sequence = registration_sequence;
/* register any images used by the models */
if (mod->type == mod_sprite)
{
sprout = (dsprite_t *)mod->extradata;
for (i = 0; i < sprout->numframes; i++)
{
mod->skins[i] = R_FindImage(sprout->frames[i].name, it_sprite);
}
}
else if (mod->type == mod_alias)
{
pheader = (dmdl_t *)mod->extradata;
for (i = 0; i < pheader->num_skins; i++)
{
mod->skins[i] = R_FindImage((char *)pheader + pheader->ofs_skins +
i * MAX_SKINNAME, it_skin);
}
mod->numframes = pheader->num_frames;
}
else if (mod->type == mod_brush)
{
for (i = 0; i < mod->numtexinfo; i++)
{
mod->texinfo[i].image->registration_sequence =
registration_sequence;
}
}
}
return mod;
}
void
R_EndRegistration(void)
{
int i;
model_t *mod;
for (i = 0, mod = mod_known; i < mod_numknown; i++, mod++)
{
if (!mod->name[0])
{
continue;
}
if (mod->registration_sequence != registration_sequence)
{
/* don't need this model */
Mod_Free(mod);
}
}
R_FreeUnusedImages();
}