/* 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 #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include "QF/checksum.h" #include "QF/cvar.h" #include "QF/dstring.h" #include "QF/model.h" #include "QF/qendian.h" #include "QF/quakefs.h" #include "QF/render.h" #include "QF/set.h" #include "QF/sys.h" #include "QF/va.h" #include "QF/plugin/vid_render.h" #include "compat.h" #include "mod_internal.h" VISIBLE int mod_sky_divide; //FIXME visibility? VISIBLE int mod_lightmap_bytes = 1; //FIXME should this be visible? VISIBLE mleaf_t * Mod_PointInLeaf (const vec3_t p, model_t *model) { float d; mnode_t *node; plane_t *plane; if (!model || !model->brush.nodes) Sys_Error ("Mod_PointInLeaf: bad model"); node = model->brush.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 void Mod_DecompressVis_set (const byte *in, const mod_brush_t *brush, byte defvis, set_t *pvs) { byte *out = (byte *) pvs->map; byte *start = out; int row, c; // Ensure the set repesents visible leafs rather than invisible leafs. pvs->inverted = 0; row = (brush->visleafs + 7) >> 3; if (!in) { // no vis info, so make all visible while (row) { *out++ = defvis; row--; } return; } do { if (*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while (c) { *out++ = 0; c--; } } while (out - start < row); } static inline void Mod_DecompressVis_mix (const byte *in, const mod_brush_t *brush, byte defvis, set_t *pvs) { byte *out = (byte *) pvs->map; byte *start = out; int row, c; //FIXME should pvs->inverted be checked and the vis bits used to remove // set bits? row = (brush->visleafs + 7) >> 3; if (!in) { // no vis info, so make all visible while (row) { *out++ |= defvis; row--; } return; } do { if (*in) { *out++ |= *in++; continue; } c = in[1]; in += 2; out += c; } while (out - start < row); } VISIBLE set_t * Mod_LeafPVS (const mleaf_t *leaf, const model_t *model) { static set_t *novis; static set_t *decompressed; unsigned numvis = model->brush.visleafs; if (leaf == model->brush.leafs) { if (!novis) { novis = set_new_size (numvis); } if (!novis->map[0] || SET_SIZE (numvis) > novis->size) { unsigned excess = SET_SIZE (numvis) - numvis; set_expand (novis, numvis); memset (novis->map, 0xff, SET_WORDS (novis) * sizeof (*novis->map)); novis->map[SET_WORDS (novis) - 1] &= (~SET_ZERO) >> excess; } return novis; } if (!decompressed) { decompressed = set_new (); } set_expand (decompressed, numvis); Mod_DecompressVis_set (leaf->compressed_vis, &model->brush, 0xff, decompressed); return decompressed; } VISIBLE void Mod_LeafPVS_set (const mleaf_t *leaf, const model_t *model, byte defvis, set_t *out) { unsigned numvis = model->brush.visleafs; set_expand (out, numvis); if (leaf == model->brush.leafs) { unsigned excess = SET_SIZE (numvis) - numvis; memset (out->map, defvis, SET_WORDS (out) * sizeof (*out->map)); out->map[SET_WORDS (out) - 1] &= (~SET_ZERO) >> excess; return; } Mod_DecompressVis_set (leaf->compressed_vis, &model->brush, defvis, out); } VISIBLE void Mod_LeafPVS_mix (const mleaf_t *leaf, const model_t *model, byte defvis, set_t *out) { unsigned numvis = model->brush.visleafs; set_expand (out, numvis); if (leaf == model->brush.leafs) { unsigned excess = SET_SIZE (numvis) - numvis; byte *o = (byte *) out->map; for (int i = SET_WORDS (out) * sizeof (*out->map); i-- > 0; ) { *o++ |= defvis; } out->map[SET_WORDS (out) - 1] &= (~SET_ZERO) >> excess; return; } Mod_DecompressVis_mix (leaf->compressed_vis, &model->brush, defvis, out); } // BRUSHMODEL LOADING ========================================================= //FIXME SLOW! However, it doesn't seem to be a big issue. Leave alone? 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 (0, "~%x", num++); strncpy (tx->name, name, 16); tx->name[15] = 0; if (strlen (name) + strlen (tag) <= 15) strcat (tx->name, tag); else strcpy (tx->name + 15 - strlen (tag), tag); } while (1); } static void Mod_LoadTextures (model_t *mod, bsp_t *bsp) { dmiptexlump_t *m; int pixels, num, max, altmax; miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10], *altanims[10]; mod_brush_t *brush = &mod->brush; if (!bsp->texdatasize) { brush->textures = NULL; return; } m = (dmiptexlump_t *) bsp->texdata; brush->numtextures = m->nummiptex; brush->textures = Hunk_AllocName (0, m->nummiptex * sizeof (*brush->textures), mod->name); for (uint32_t i = 0; i < m->nummiptex; i++) { if (m->dataofs[i] == ~0u) continue; mt = (miptex_t *) ((byte *) m + m->dataofs[i]); mt->width = LittleLong (mt->width); mt->height = LittleLong (mt->height); for (int 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 (0, sizeof (texture_t) + pixels, mod->name); brush->textures[i] = tx; memcpy (tx->name, mt->name, sizeof (tx->name)); mod_unique_miptex_name (brush->textures, tx, i); tx->width = mt->width; tx->height = mt->height; for (int 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)) brush->skytexture = tx; } if (mod_funcs && mod_funcs->Mod_ProcessTexture) { size_t render_size = mod_funcs->texture_render_size; byte *render_data = 0; if (render_size) { render_data = Hunk_AllocName (0, m->nummiptex * render_size, mod->name); } for (uint32_t i = 0; i < m->nummiptex; i++) { if (!(tx = brush->textures[i])) { continue; } tx->render = render_data; render_data += render_size; mod_funcs->Mod_ProcessTexture (mod, tx); } // signal the end of the textures mod_funcs->Mod_ProcessTexture (mod, 0); } // sequence the animations for (uint32_t i = 0; i < m->nummiptex; i++) { tx = brush->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)); // convert to uppercase, avoiding toupper (table lookup, // localization issues, etc) #define QTOUPPER(x) ((x) - ('a' - 'A')) max = tx->name[1]; if (max >= 'a' && max <= 'z') max = QTOUPPER (max); 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 (uint32_t j = i + 1; j < m->nummiptex; j++) { tx2 = brush->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 = QTOUPPER (num); 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 (int 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 (int 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 (model_t *mod, bsp_t *bsp) { if (!bsp->visdatasize) { mod->brush.visdata = NULL; return; } mod->brush.visdata = Hunk_AllocName (0, bsp->visdatasize, mod->name); memcpy (mod->brush.visdata, bsp->visdata, bsp->visdatasize); } static void Mod_LoadEntities (model_t *mod, bsp_t *bsp) { if (!bsp->entdatasize) { mod->brush.entities = NULL; return; } mod->brush.entities = Hunk_AllocName (0, bsp->entdatasize, mod->name); memcpy (mod->brush.entities, bsp->entdata, bsp->entdatasize); } static void Mod_LoadVertexes (model_t *mod, bsp_t *bsp) { dvertex_t *in; int count, i; mvertex_t *out; in = bsp->vertexes; count = bsp->numvertexes; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); mod->brush.vertexes = out; mod->brush.numvertexes = count; for (i = 0; i < count; i++, in++, out++) VectorCopy (in->point, out->position); } static void Mod_LoadSubmodels (model_t *mod, bsp_t *bsp) { dmodel_t *in, *out; int count, i, j; mod_brush_t *brush = &mod->brush; in = bsp->models; count = bsp->nummodels; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); brush->submodels = out; brush->numsubmodels = count; for (i = 0; i < count; i++, in++, out++) { static vec3_t offset = {1, 1, 1}; // spread the mins / maxs by a pixel VectorSubtract (in->mins, offset, out->mins); VectorAdd (in->maxs, offset, out->maxs); VectorCopy (in->origin, out->origin); 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; } out = brush->submodels; if (out->visleafs > 8192) Sys_MaskPrintf (SYS_warn, "%i visleafs exceeds standard limit of 8192.\n", out->visleafs); } static void Mod_LoadEdges (model_t *mod, bsp_t *bsp) { dedge_t *in; int count, i; medge_t *out; in = bsp->edges; count = bsp->numedges; out = Hunk_AllocName (0, (count + 1) * sizeof (*out), mod->name); mod->brush.edges = out; mod->brush.numedges = count; for (i = 0; i < count; i++, in++, out++) { out->v[0] = in->v[0]; out->v[1] = in->v[1]; } } static void Mod_LoadTexinfo (model_t *mod, bsp_t *bsp) { float len1, len2; unsigned count, miptex, i, j; mtexinfo_t *out; texinfo_t *in; in = bsp->texinfo; count = bsp->numtexinfo; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); mod->brush.texinfo = out; mod->brush.numtexinfo = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 4; j++) { out->vecs[0][j] = in->vecs[0][j]; out->vecs[1][j] = 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 = in->miptex; out->flags = in->flags; if (!mod->brush.textures) { out->texture = r_notexture_mip; // checkerboard texture out->flags = 0; } else { if (miptex >= mod->brush.numtextures) Sys_Error ("miptex >= mod->brush.numtextures"); out->texture = mod->brush.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 (model_t *mod, msurface_t *s) { float mins[2], maxs[2], val; int e, i, j; int bmins[2], bmaxs[2]; mtexinfo_t *tex; mvertex_t *v; mod_brush_t *brush = &mod->brush; mins[0] = mins[1] = 999999; maxs[0] = maxs[1] = -99999; tex = s->texinfo; for (i = 0; i < s->numedges; i++) { e = brush->surfedges[s->firstedge + i]; if (e >= 0) v = &brush->vertexes[brush->edges[e].v[0]]; else v = &brush->vertexes[brush->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 2000 is really too small, need a saner test if (!(tex->flags & TEX_SPECIAL) && s->extents[i] > 2000) Sys_Error ("Bad surface extents: %d %x %d %d", i, tex->flags, s->extents[i], LongSwap (s->extents[i])); } } static void Mod_LoadFaces (model_t *mod, bsp_t *bsp) { dface_t *in; int count, planenum, side, surfnum, i; msurface_t *out; mod_brush_t *brush = &mod->brush; in = bsp->faces; count = bsp->numfaces; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); if (count > 32767) { Sys_MaskPrintf (SYS_warn, "%i faces exceeds standard limit of 32767.\n", count); } brush->surfaces = out; brush->numsurfaces = count; for (surfnum = 0; surfnum < count; surfnum++, in++, out++) { out->firstedge = in->firstedge; out->numedges = in->numedges; out->flags = 0; planenum = in->planenum; side = in->side; if (side) out->flags |= SURF_PLANEBACK; out->plane = brush->planes + planenum; out->texinfo = brush->texinfo + in->texinfo; CalcSurfaceExtents (mod, out); // lighting info for (i = 0; i < MAXLIGHTMAPS; i++) out->styles[i] = in->styles[i]; i = in->lightofs; if (i == -1) out->samples = NULL; else out->samples = brush->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 (mod_sky_divide) { if (mod_funcs && mod_funcs->Mod_SubdivideSurface) { mod_funcs->Mod_SubdivideSurface (mod, 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; } if (mod_funcs && mod_funcs->Mod_SubdivideSurface) { // cut up polygon for warps mod_funcs->Mod_SubdivideSurface (mod, out); } continue; } } } static void Mod_SetParent (mod_brush_t *brush, mnode_t *node, mnode_t *parent) { if (node->contents < 0) { brush->leaf_parents[(mleaf_t *)node - brush->leafs] = parent; return; } brush->node_parents[node - brush->nodes] = parent; Mod_SetParent (brush, node->children[0], node); Mod_SetParent (brush, node->children[1], node); } static void Mod_SetLeafFlags (mod_brush_t *brush) { for (unsigned i = 0; i < brush->modleafs; i++) { int flags = 0; mleaf_t *leaf = &brush->leafs[i]; msurface_t **msurf = brush->marksurfaces + leaf->firstmarksurface; for (int j = 0; j < leaf->nummarksurfaces; j++) { msurface_t *surf = *msurf++; flags |= surf->flags; } brush->leaf_flags[i] = flags; } } static void Mod_LoadNodes (model_t *mod, bsp_t *bsp) { dnode_t *in; int count, i, j, p; mnode_t *out; mod_brush_t *brush = &mod->brush; in = bsp->nodes; count = bsp->numnodes; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); if (count > 32767) { Sys_MaskPrintf (SYS_warn, "%i nodes exceeds standard limit of 32767.\n", count); } brush->nodes = out; brush->numnodes = count; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->minmaxs[j] = in->mins[j]; out->minmaxs[3 + j] = in->maxs[j]; } p = in->planenum; out->plane = brush->planes + p; out->firstsurface = in->firstface; out->numsurfaces = in->numfaces; for (j = 0; j < 2; j++) { p = in->children[j]; // this check is for extended bsp 29 files if (p >= 0) { out->children[j] = brush->nodes + p; } else { p = ~p; if ((unsigned) p < brush->modleafs) { out->children[j] = (mnode_t *) (brush->leafs + p); } else { Sys_Printf ("Mod_LoadNodes: invalid leaf index %i " "(file has only %i leafs)\n", p, brush->modleafs); //map it to the solid leaf out->children[j] = (mnode_t *)(brush->leafs); } } } } size_t size = (brush->modleafs + brush->numnodes) * sizeof (mnode_t *); size += brush->modleafs * sizeof (int); brush->node_parents = Hunk_AllocName (0, size, mod->name); brush->leaf_parents = brush->node_parents + brush->numnodes; brush->leaf_flags = (int *) (brush->leaf_parents + brush->modleafs); Mod_SetParent (brush, brush->nodes, NULL); // sets nodes and leafs Mod_SetLeafFlags (brush); } static void Mod_LoadLeafs (model_t *mod, bsp_t *bsp) { dleaf_t *in; int count, i, j, p; mleaf_t *out; qboolean isnotmap = true; mod_brush_t *brush = &mod->brush; in = bsp->leafs; count = bsp->numleafs; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); brush->leafs = out; brush->modleafs = count; // snprintf(s, sizeof (s), "maps/%s.bsp", // Info_ValueForKey(cl.serverinfo,"map")); if (!strncmp ("maps/", mod->path, 5)) isnotmap = false; for (i = 0; i < count; i++, in++, out++) { for (j = 0; j < 3; j++) { out->mins[j] = in->mins[j]; out->maxs[j] = in->maxs[j]; } p = in->contents; out->contents = p; out->firstmarksurface = in->firstmarksurface; out->nummarksurfaces = in->nummarksurfaces; p = in->visofs; if (p == -1) out->compressed_vis = NULL; else out->compressed_vis = brush->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) { msurface_t **msurf = brush->marksurfaces + out->firstmarksurface; for (j = 0; j < out->nummarksurfaces; j++) { msurface_t *surf = *msurf++; surf->flags |= SURF_UNDERWATER; } } if (isnotmap) { msurface_t **msurf = brush->marksurfaces + out->firstmarksurface; for (j = 0; j < out->nummarksurfaces; j++) { msurface_t *surf = *msurf++; surf->flags |= SURF_DONTWARP; } } } } static void Mod_LoadClipnodes (model_t *mod, bsp_t *bsp) { dclipnode_t *in; mclipnode_t *out; hull_t *hull; int count, i; mod_brush_t *brush = &mod->brush; in = bsp->clipnodes; count = bsp->numclipnodes; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); if (count > 32767) { Sys_MaskPrintf (SYS_warn, "%i clilpnodes exceeds standard limit of 32767.\n", count); } brush->clipnodes = out; brush->numclipnodes = count; hull = &brush->hulls[1]; brush->hull_list[1] = hull; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; hull->planes = brush->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 = &brush->hulls[2]; brush->hull_list[2] = hull; hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; hull->planes = brush->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 = in->planenum; if (out->planenum >= brush->numplanes) Sys_Error ("Mod_LoadClipnodes: planenum out of bounds"); out->children[0] = in->children[0]; out->children[1] = in->children[1]; // these checks are for extended bsp 29 files if (out->children[0] >= count) out->children[0] -= 65536; if (out->children[1] >= count) out->children[1] -= 65536; 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 (model_t *mod) { mclipnode_t *out; hull_t *hull; int count, i, j; mnode_t *in, *child; mod_brush_t *brush = &mod->brush; hull = &brush->hulls[0]; brush->hull_list[0] = hull; in = brush->nodes; count = brush->numnodes; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count - 1; hull->planes = brush->planes; for (i = 0; i < count; i++, out++, in++) { out->planenum = in->plane - brush->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 - brush->nodes; } } } static void Mod_LoadMarksurfaces (model_t *mod, bsp_t *bsp) { unsigned count, i, j; msurface_t **out; uint32_t *in; mod_brush_t *brush = &mod->brush; in = bsp->marksurfaces; count = bsp->nummarksurfaces; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); if (count > 32767) { Sys_MaskPrintf (SYS_warn, "%i marksurfaces exceeds standard limit of 32767.\n", count); } brush->marksurfaces = out; brush->nummarksurfaces = count; for (i = 0; i < count; i++) { j = in[i]; if (j >= brush->numsurfaces) Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); out[i] = brush->surfaces + j; } } static void Mod_LoadSurfedges (model_t *mod, bsp_t *bsp) { int count, i; int32_t *in; int *out; mod_brush_t *brush = &mod->brush; in = bsp->surfedges; count = bsp->numsurfedges; out = Hunk_AllocName (0, count * sizeof (*out), mod->name); brush->surfedges = out; brush->numsurfedges = count; for (i = 0; i < count; i++) out[i] = in[i]; } static void Mod_LoadPlanes (model_t *mod, bsp_t *bsp) { dplane_t *in; int bits, count, i, j; plane_t *out; mod_brush_t *brush = &mod->brush; in = bsp->planes; count = bsp->numplanes; out = Hunk_AllocName (0, count * 2 * sizeof (*out), mod->name); brush->planes = out; brush->numplanes = count; for (i = 0; i < count; i++, in++, out++) { bits = 0; for (j = 0; j < 3; j++) { out->normal[j] = in->normal[j]; if (out->normal[j] < 0) bits |= 1 << j; } out->dist = in->dist; out->type = 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; mod_brush_t *brush = &mod->brush; base = (byte *) bsp->header; // checksum all of the map, except for entities brush->checksum = 0; brush->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); brush->checksum ^= csum; if (i != LUMP_VISIBILITY && i != LUMP_LEAFS && i != LUMP_NODES) brush->checksum2 ^= csum; } } static void recurse_draw_tree (mod_brush_t *brush, mnode_t *node, int depth) { if (!node || node->contents < 0) { if (depth > brush->depth) brush->depth = depth; return; } recurse_draw_tree (brush, node->children[0], depth + 1); recurse_draw_tree (brush, node->children[1], depth + 1); } static void Mod_FindDrawDepth (mod_brush_t *brush) { brush->depth = 0; recurse_draw_tree (brush, brush->nodes, 1); } void Mod_LoadBrushModel (model_t *mod, void *buffer) { dmodel_t *bm; unsigned i, j; bsp_t *bsp; mod->type = mod_brush; bsp = LoadBSPMem (buffer, qfs_filesize, do_checksums, mod); // load into heap Mod_LoadVertexes (mod, bsp); Mod_LoadEdges (mod, bsp); Mod_LoadSurfedges (mod, bsp); Mod_LoadTextures (mod, bsp); if (mod_funcs && mod_funcs->Mod_LoadLighting) { mod_funcs->Mod_LoadLighting (mod, bsp); } Mod_LoadPlanes (mod, bsp); Mod_LoadTexinfo (mod, bsp); Mod_LoadFaces (mod, bsp); Mod_LoadMarksurfaces (mod, bsp); Mod_LoadVisibility (mod, bsp); Mod_LoadLeafs (mod, bsp); Mod_LoadNodes (mod, bsp); Mod_LoadClipnodes (mod, bsp); Mod_LoadEntities (mod, bsp); Mod_LoadSubmodels (mod, bsp); BSP_Free(bsp); Mod_MakeHull0 (mod); Mod_FindDrawDepth (&mod->brush); for (i = 0; i < MAX_MAP_HULLS; i++) Mod_FindClipDepth (&mod->brush.hulls[i]); mod->numframes = 2; // regular and alternate animation // set up the submodels (FIXME: this is confusing) for (i = 0; i < mod->brush.numsubmodels; i++) { bm = &mod->brush.submodels[i]; mod->brush.hulls[0].firstclipnode = bm->headnode[0]; mod->brush.hull_list[0] = &mod->brush.hulls[0]; for (j = 1; j < MAX_MAP_HULLS; j++) { mod->brush.hulls[j].firstclipnode = bm->headnode[j]; mod->brush.hulls[j].lastclipnode = mod->brush.numclipnodes - 1; mod->brush.hull_list[j] = &mod->brush.hulls[j]; } mod->brush.firstmodelsurface = bm->firstface; mod->brush.nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); mod->radius = RadiusFromBounds (mod->mins, mod->maxs); mod->brush.visleafs = bm->visleafs; // The bsp file has leafs for all submodes and hulls, so update the // leaf count for this model to be the correct number (which is one // more than the number of visible leafs) mod->brush.modleafs = bm->visleafs + 1; if (i < mod->brush.numsubmodels - 1) { // duplicate the basic information char name[12]; snprintf (name, sizeof (name), "*%i", i + 1); model_t *m = Mod_FindName (name); *m = *mod; strcpy (m->path, name); mod = m; // make sure clear is called only for the main model m->clear = 0; m->data = 0; } } }