/* Copyright (C) 1996-2001 Id Software, Inc. Copyright (C) 2002-2009 John Fitzgibbons and others Copyright (C) 2011 O.Sezer 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. */ // models.c -- model loading and caching // models are the only shared resource between a client and server running // on the same machine. #include "quakedef.h" model_t *loadmodel; char loadname[32]; // for hunk tags void Mod_LoadSpriteModel (model_t *mod, void *buffer); void Mod_LoadBrushModel (model_t *mod, void *buffer); void Mod_LoadAliasModel (model_t *mod, void *buffer); model_t *Mod_LoadModel (model_t *mod, qboolean crash); cvar_t external_ents = {"external_ents", "1", CVAR_ARCHIVE}; byte mod_novis[MAX_MAP_LEAFS/8]; #define MAX_MOD_KNOWN 2048 //johnfitz -- was 512 model_t mod_known[MAX_MOD_KNOWN]; int mod_numknown; texture_t *r_notexture_mip; //johnfitz -- moved here from r_main.c texture_t *r_notexture_mip2; //johnfitz -- used for non-lightmapped surfs with a missing texture /* =============== Mod_Init =============== */ void Mod_Init (void) { Cvar_RegisterVariable (&gl_subdivide_size); Cvar_RegisterVariable (&external_ents); memset (mod_novis, 0xff, sizeof(mod_novis)); //johnfitz -- create notexture miptex r_notexture_mip = (texture_t *) Hunk_AllocName (sizeof(texture_t), "r_notexture_mip"); strcpy (r_notexture_mip->name, "notexture"); r_notexture_mip->height = r_notexture_mip->width = 32; r_notexture_mip2 = (texture_t *) Hunk_AllocName (sizeof(texture_t), "r_notexture_mip2"); strcpy (r_notexture_mip2->name, "notexture2"); r_notexture_mip2->height = r_notexture_mip2->width = 32; //johnfitz } /* =============== Mod_Extradata Caches the data if needed =============== */ void *Mod_Extradata (model_t *mod) { void *r; r = Cache_Check (&mod->cache); if (r) return r; Mod_LoadModel (mod, true); if (!mod->cache.data) Sys_Error ("Mod_Extradata: caching failed"); return mod->cache.data; } /* =============== Mod_PointInLeaf =============== */ mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model) { mnode_t *node; float d; 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 } /* =================== Mod_DecompressVis =================== */ byte *Mod_DecompressVis (byte *in, model_t *model) { static byte decompressed[MAX_MAP_LEAFS/8]; int c; byte *out; int row; row = (model->numleafs+7)>>3; out = decompressed; #if 0 memcpy (out, in, row); #else 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); #endif return decompressed; } byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model) { if (leaf == model->leafs) return mod_novis; return Mod_DecompressVis (leaf->compressed_vis, model); } /* =================== Mod_ClearAll =================== */ void Mod_ClearAll (void) { int i; model_t *mod; for (i=0 , mod=mod_known ; itype != mod_alias) { mod->needload = true; TexMgr_FreeTexturesForOwner (mod); //johnfitz } } /* ================== Mod_FindName ================== */ model_t *Mod_FindName (const char *name) { int i; model_t *mod; if (!name[0]) Sys_Error ("Mod_FindName: NULL name"); //johnfitz -- was "Mod_ForName" // // search the currently loaded models // for (i=0 , mod=mod_known ; iname, name) ) break; if (i == mod_numknown) { if (mod_numknown == MAX_MOD_KNOWN) Sys_Error ("mod_numknown == MAX_MOD_KNOWN"); q_strlcpy (mod->name, name, MAX_QPATH); mod->needload = true; mod_numknown++; } return mod; } /* ================== Mod_TouchModel ================== */ void Mod_TouchModel (const char *name) { model_t *mod; mod = Mod_FindName (name); if (!mod->needload) { if (mod->type == mod_alias) Cache_Check (&mod->cache); } } /* ================== Mod_LoadModel Loads a model into the cache ================== */ model_t *Mod_LoadModel (model_t *mod, qboolean crash) { byte *buf; byte stackbuf[1024]; // avoid dirtying the cache heap int mod_type; if (!mod->needload) { if (mod->type == mod_alias) { if (Cache_Check (&mod->cache)) return mod; } else return mod; // not cached at all } // // because the world is so huge, load it one piece at a time // if (!crash) { } // // load the file // buf = COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf), & mod->path_id); if (!buf) { if (crash) Sys_Error ("Mod_LoadModel: %s not found", mod->name); //johnfitz -- was "Mod_NumForName" return NULL; } // // allocate a new model // COM_FileBase (mod->name, loadname, sizeof(loadname)); loadmodel = mod; // // fill it in // // call the apropriate loader mod->needload = false; mod_type = (buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24)); switch (mod_type) { case IDPOLYHEADER: Mod_LoadAliasModel (mod, buf); break; case IDSPRITEHEADER: Mod_LoadSpriteModel (mod, buf); break; default: Mod_LoadBrushModel (mod, buf); break; } return mod; } /* ================== Mod_ForName Loads in a model for the given name ================== */ model_t *Mod_ForName (const char *name, qboolean crash) { model_t *mod; mod = Mod_FindName (name); return Mod_LoadModel (mod, crash); } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ byte *mod_base; /* ================= Mod_CheckFullbrights -- johnfitz ================= */ qboolean Mod_CheckFullbrights (byte *pixels, int count) { int i; for (i = 0; i < count; i++) if (*pixels++ > 223) return true; return false; } /* ================= Mod_LoadTextures ================= */ void Mod_LoadTextures (lump_t *l) { int i, j, pixels, num, maxanim, altmax; miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10]; texture_t *altanims[10]; dmiptexlump_t *m; //johnfitz -- more variables char texturename[64]; int nummiptex; src_offset_t offset; int mark, fwidth, fheight; char filename[MAX_OSPATH], filename2[MAX_OSPATH], mapname[MAX_OSPATH]; byte *data; extern byte *hunk_base; //johnfitz //johnfitz -- don't return early if no textures; still need to create dummy texture if (!l->filelen) { Con_Printf ("Mod_LoadTextures: no textures in bsp file\n"); nummiptex = 0; m = NULL; // avoid bogus compiler warning } else { m = (dmiptexlump_t *)(mod_base + l->fileofs); m->nummiptex = LittleLong (m->nummiptex); nummiptex = m->nummiptex; } //johnfitz loadmodel->numtextures = nummiptex + 2; //johnfitz -- need 2 dummy texture chains for missing textures loadmodel->textures = (texture_t **) Hunk_AllocName (loadmodel->numtextures * sizeof(*loadmodel->textures) , loadname); for (i=0 ; idataofs[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 ; joffsets[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 = (texture_t *) Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); loadmodel->textures[i] = tx; memcpy (tx->name, mt->name, sizeof(tx->name)); tx->width = mt->width; tx->height = mt->height; for (j=0 ; joffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t); // the pixels immediately follow the structures memcpy ( tx+1, mt+1, pixels); tx->update_warp = false; //johnfitz tx->warpimage = NULL; //johnfitz tx->fullbright = NULL; //johnfitz //johnfitz -- lots of changes if (!isDedicated) //no texture uploading for dedicated server { if (!Q_strncasecmp(tx->name,"sky",3)) //sky texture //also note -- was Q_strncmp, changed to match qbsp Sky_LoadTexture (tx); else if (tx->name[0] == '*') //warping texture { //external textures -- first look in "textures/mapname/" then look in "textures/" mark = Hunk_LowMark(); COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname)); q_snprintf (filename, sizeof(filename), "textures/%s/#%s", mapname, tx->name+1); //this also replaces the '*' with a '#' data = Image_LoadImage (filename, &fwidth, &fheight); if (!data) { q_snprintf (filename, sizeof(filename), "textures/#%s", tx->name+1); data = Image_LoadImage (filename, &fwidth, &fheight); } //now load whatever we found if (data) //load external image { q_strlcpy (texturename, filename, sizeof(texturename)); tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, fwidth, fheight, SRC_RGBA, data, filename, 0, TEXPREF_NONE); } else //use the texture from the bsp file { q_snprintf (texturename, sizeof(texturename), "%s:%s", loadmodel->name, tx->name); offset = (src_offset_t)(mt+1) - (src_offset_t)mod_base; tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_NONE); } //now create the warpimage, using dummy data from the hunk to create the initial image Hunk_Alloc (gl_warpimagesize*gl_warpimagesize*4); //make sure hunk is big enough so we don't reach an illegal address Hunk_FreeToLowMark (mark); q_snprintf (texturename, sizeof(texturename), "%s_warp", texturename); tx->warpimage = TexMgr_LoadImage (loadmodel, texturename, gl_warpimagesize, gl_warpimagesize, SRC_RGBA, hunk_base, "", (src_offset_t)hunk_base, TEXPREF_NOPICMIP | TEXPREF_WARPIMAGE); tx->update_warp = true; } else //regular texture { //external textures -- first look in "textures/mapname/" then look in "textures/" mark = Hunk_LowMark (); COM_StripExtension (loadmodel->name + 5, mapname, sizeof(mapname)); q_snprintf (filename, sizeof(filename), "textures/%s/%s", mapname, tx->name); data = Image_LoadImage (filename, &fwidth, &fheight); if (!data) { q_snprintf (filename, sizeof(filename), "textures/%s", tx->name); data = Image_LoadImage (filename, &fwidth, &fheight); } //now load whatever we found if (data) //load external image { tx->gltexture = TexMgr_LoadImage (loadmodel, filename, fwidth, fheight, SRC_RGBA, data, filename, 0, TEXPREF_MIPMAP); //now try to load glow/luma image from the same place Hunk_FreeToLowMark (mark); q_snprintf (filename2, sizeof(filename2), "%s_glow", filename); data = Image_LoadImage (filename2, &fwidth, &fheight); if (!data) q_snprintf (filename2, sizeof(filename2), "%s_luma", filename); data = Image_LoadImage (filename2, &fwidth, &fheight); if (data) tx->fullbright = TexMgr_LoadImage (loadmodel, filename2, fwidth, fheight, SRC_RGBA, data, filename, 0, TEXPREF_MIPMAP ); } else //use the texture from the bsp file { q_snprintf (texturename, sizeof(texturename), "%s:%s", loadmodel->name, tx->name); offset = (src_offset_t)(mt+1) - (src_offset_t)mod_base; if (Mod_CheckFullbrights ((byte *)(tx+1), pixels)) { tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_NOBRIGHT); q_snprintf (texturename, sizeof(texturename), "%s:%s_glow", loadmodel->name, tx->name); tx->fullbright = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP | TEXPREF_FULLBRIGHT); } else { tx->gltexture = TexMgr_LoadImage (loadmodel, texturename, tx->width, tx->height, SRC_INDEXED, (byte *)(tx+1), loadmodel->name, offset, TEXPREF_MIPMAP); } } Hunk_FreeToLowMark (mark); } } //johnfitz } //johnfitz -- last 2 slots in array should be filled with dummy textures loadmodel->textures[loadmodel->numtextures-2] = r_notexture_mip; //for lightmapped surfs loadmodel->textures[loadmodel->numtextures-1] = r_notexture_mip2; //for SURF_DRAWTILED surfs // // sequence the animations // for (i=0 ; itextures[i]; if (!tx || tx->name[0] != '+') continue; if (tx->anim_next) continue; // allready sequenced // find the number of frames in the animation memset (anims, 0, sizeof(anims)); memset (altanims, 0, sizeof(altanims)); maxanim = tx->name[1]; altmax = 0; if (maxanim >= 'a' && maxanim <= 'z') maxanim -= 'a' - 'A'; if (maxanim >= '0' && maxanim <= '9') { maxanim -= '0'; altmax = 0; anims[maxanim] = tx; maxanim++; } else if (maxanim >= 'A' && maxanim <= 'J') { altmax = maxanim - 'A'; maxanim = 0; altanims[altmax] = tx; altmax++; } else Sys_Error ("Bad animating texture %s", tx->name); for (j=i+1 ; jtextures[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 > maxanim) maxanim = 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 ; jname); tx2->anim_total = maxanim * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = anims[ (j+1)%maxanim ]; if (altmax) tx2->alternate_anims = altanims[0]; } for (j=0 ; jname); 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 (maxanim) tx2->alternate_anims = anims[0]; } } } /* ================= Mod_LoadLighting -- johnfitz -- replaced with lit support code via lordhavoc ================= */ void Mod_LoadLighting (lump_t *l) { int i, mark; byte *in, *out, *data; byte d; char litfilename[MAX_OSPATH]; unsigned int path_id; loadmodel->lightdata = NULL; // LordHavoc: check for a .lit file q_strlcpy(litfilename, loadmodel->name, sizeof(litfilename)); COM_StripExtension(litfilename, litfilename, sizeof(litfilename)); q_strlcat(litfilename, ".lit", sizeof(litfilename)); mark = Hunk_LowMark(); data = (byte*) COM_LoadHunkFile (litfilename, &path_id); if (data) { // use lit file only from the same gamedir as the map // itself or from a searchpath with higher priority. if (path_id < loadmodel->path_id) { Hunk_FreeToLowMark(mark); Con_Printf("ignored %s from a gamedir with lower priority\n", litfilename); } else if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T') { i = LittleLong(((int *)data)[1]); if (i == 1) { Con_DPrintf("%s loaded", litfilename); loadmodel->lightdata = data + 8; return; } else { Hunk_FreeToLowMark(mark); Con_Printf("Unknown .lit file version (%d)\n", i); } } else { Hunk_FreeToLowMark(mark); Con_Printf("Corrupt .lit file (old version?), ignoring\n"); } } // LordHavoc: no .lit found, expand the white lighting data to color if (!l->filelen) return; loadmodel->lightdata = (byte *) Hunk_AllocName ( l->filelen*3, litfilename); in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write out = loadmodel->lightdata; memcpy (in, mod_base + l->fileofs, l->filelen); for (i = 0;i < l->filelen;i++) { d = *in++; *out++ = d; *out++ = d; *out++ = d; } } /* ================= Mod_LoadVisibility ================= */ void Mod_LoadVisibility (lump_t *l) { if (!l->filelen) { loadmodel->visdata = NULL; return; } loadmodel->visdata = (byte *) Hunk_AllocName ( l->filelen, loadname); memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadEntities ================= */ void Mod_LoadEntities (lump_t *l) { char entfilename[MAX_QPATH]; char *ents; int mark; unsigned int path_id; if (! external_ents.value) goto _load_embedded; q_strlcpy(entfilename, loadmodel->name, sizeof(entfilename)); COM_StripExtension(entfilename, entfilename, sizeof(entfilename)); q_strlcat(entfilename, ".ent", sizeof(entfilename)); Con_DPrintf("trying to load %s\n", entfilename); mark = Hunk_LowMark(); ents = (char *) COM_LoadHunkFile (entfilename, &path_id); if (ents) { // use ent file only from the same gamedir as the map // itself or from a searchpath with higher priority. if (path_id < loadmodel->path_id) { Hunk_FreeToLowMark(mark); Con_Printf("ignored %s from a gamedir with lower priority\n", entfilename); } else { loadmodel->entities = ents; Con_DPrintf("Loaded external entity file %s\n", entfilename); return; } } _load_embedded: if (!l->filelen) { loadmodel->entities = NULL; return; } loadmodel->entities = (char *) Hunk_AllocName ( l->filelen, loadname); memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadVertexes ================= */ void Mod_LoadVertexes (lump_t *l) { dvertex_t *in; mvertex_t *out; int i, count; in = (dvertex_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mvertex_t *) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->vertexes = out; loadmodel->numvertexes = count; for ( i=0 ; iposition[0] = LittleFloat (in->point[0]); out->position[1] = LittleFloat (in->point[1]); out->position[2] = LittleFloat (in->point[2]); } } /* ================= Mod_LoadEdges ================= */ void Mod_LoadEdges (lump_t *l) { dedge_t *in; medge_t *out; int i, count; in = (dedge_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (medge_t *) Hunk_AllocName ( (count + 1) * sizeof(*out), loadname); loadmodel->edges = out; loadmodel->numedges = count; for ( i=0 ; iv[0] = (unsigned short)LittleShort(in->v[0]); out->v[1] = (unsigned short)LittleShort(in->v[1]); } } /* ================= Mod_LoadTexinfo ================= */ void Mod_LoadTexinfo (lump_t *l) { texinfo_t *in; mtexinfo_t *out; int i, j, count, miptex; float len1, len2; int missing = 0; //johnfitz in = (texinfo_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mtexinfo_t *) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for ( i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][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; #if 0 if (len1 + len2 < 0.001) out->mipadjust = 1; // don't crash else out->mipadjust = 1 / floor( (len1+len2)/2 + 0.1 ); #endif miptex = LittleLong (in->miptex); out->flags = LittleLong (in->flags); //johnfitz -- rewrote this section if (miptex >= loadmodel->numtextures-1 || !loadmodel->textures[miptex]) { if (out->flags & TEX_SPECIAL) out->texture = loadmodel->textures[loadmodel->numtextures-1]; else out->texture = loadmodel->textures[loadmodel->numtextures-2]; out->flags |= TEX_MISSING; missing++; } else { out->texture = loadmodel->textures[miptex]; } //johnfitz } //johnfitz: report missing textures if (missing && loadmodel->numtextures > 1) Con_Printf ("Mod_LoadTexinfo: %d texture(s) missing from BSP file\n", missing); //johnfitz } /* ================ CalcSurfaceExtents Fills in s->texturemins[] and s->extents[] ================ */ void 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 ; inumedges ; 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; if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 2000) //johnfitz -- was 512 in glquake, 256 in winquake Sys_Error ("Bad surface extents"); } } /* ================ Mod_PolyForUnlitSurface -- johnfitz -- creates polys for unlightmapped surfaces (sky and water) TODO: merge this into BuildSurfaceDisplayList? ================ */ void Mod_PolyForUnlitSurface (msurface_t *fa) { vec3_t verts[64]; int numverts, i, lindex; float *vec; glpoly_t *poly; float texscale; if (fa->flags & (SURF_DRAWTURB | SURF_DRAWSKY)) texscale = (1.0/128.0); //warp animation repeats every 128 else texscale = (1.0/32.0); //to match r_notexture_mip // convert edges back to a normal polygon numverts = 0; for (i=0 ; inumedges ; i++) { lindex = loadmodel->surfedges[fa->firstedge + i]; if (lindex > 0) vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position; else vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position; VectorCopy (vec, verts[numverts]); numverts++; } //create the poly poly = (glpoly_t *) Hunk_Alloc (sizeof(glpoly_t) + (numverts-4) * VERTEXSIZE*sizeof(float)); poly->next = NULL; fa->polys = poly; poly->numverts = numverts; for (i=0, vec=(float *)verts; iverts[i]); poly->verts[i][3] = DotProduct(vec, fa->texinfo->vecs[0]) * texscale; poly->verts[i][4] = DotProduct(vec, fa->texinfo->vecs[1]) * texscale; } } /* ================= Mod_CalcSurfaceBounds -- johnfitz -- calculate bounding box for per-surface frustum culling ================= */ void Mod_CalcSurfaceBounds (msurface_t *s) { int i, e; mvertex_t *v; s->mins[0] = s->mins[1] = s->mins[2] = 9999; s->maxs[0] = s->maxs[1] = s->maxs[2] = -9999; for (i=0 ; inumedges ; 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]]; if (s->mins[0] > v->position[0]) s->mins[0] = v->position[0]; if (s->mins[1] > v->position[1]) s->mins[1] = v->position[1]; if (s->mins[2] > v->position[2]) s->mins[2] = v->position[2]; if (s->maxs[0] < v->position[0]) s->maxs[0] = v->position[0]; if (s->maxs[1] < v->position[1]) s->maxs[1] = v->position[1]; if (s->maxs[2] < v->position[2]) s->maxs[2] = v->position[2]; } } /* ================= Mod_LoadFaces ================= */ void Mod_LoadFaces (lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; in = (dface_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t *) Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz -- warn mappers about exceeding old limits if (count > 32767) Con_Warning ("%i faces exceeds standard limit of 32767.\n", count); //johnfitz loadmodel->surfaces = out; loadmodel->numsurfaces = count; for ( surfnum=0 ; surfnumfirstedge = 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); Mod_CalcSurfaceBounds (out); //johnfitz -- for per-surface frustum culling // lighting info for (i=0 ; istyles[i] = in->styles[i]; i = LittleLong(in->lightofs); if (i == -1) out->samples = NULL; else out->samples = loadmodel->lightdata + (i * 3); //johnfitz -- lit support via lordhavoc (was "+ i") //johnfitz -- this section rewritten if (!Q_strncasecmp(out->texinfo->texture->name,"sky",3)) // sky surface //also note -- was Q_strncmp, changed to match qbsp { out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); Mod_PolyForUnlitSurface (out); //no more subdivision } else if (out->texinfo->texture->name[0] == '*') // warp surface { out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); Mod_PolyForUnlitSurface (out); GL_SubdivideSurface (out); } else if (out->texinfo->flags & TEX_MISSING) // texture is missing from bsp { if (out->samples) //lightmapped { out->flags |= SURF_NOTEXTURE; } else // not lightmapped { out->flags |= (SURF_NOTEXTURE | SURF_DRAWTILED); Mod_PolyForUnlitSurface (out); } } //johnfitz } } /* ================= Mod_SetParent ================= */ 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); } /* ================= Mod_LoadNodes ================= */ void Mod_LoadNodes (lump_t *l) { int i, j, count, p; dnode_t *in; mnode_t *out; in = (dnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz -- warn mappers about exceeding old limits if (count > 32767) Con_Warning ("%i nodes exceeds standard limit of 32767.\n", count); //johnfitz loadmodel->nodes = out; loadmodel->numnodes = count; for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->planenum); out->plane = loadmodel->planes + p; out->firstsurface = (unsigned short)LittleShort (in->firstface); //johnfitz -- explicit cast as unsigned short out->numsurfaces = (unsigned short)LittleShort (in->numfaces); //johnfitz -- explicit cast as unsigned short for (j=0 ; j<2 ; j++) { //johnfitz -- hack to handle nodes > 32k, adapted from darkplaces p = (unsigned short)LittleShort(in->children[j]); if (p < count) out->children[j] = loadmodel->nodes + p; else { p = 65535 - p; //note this uses 65535 intentionally, -1 is leaf 0 if (p < loadmodel->numleafs) out->children[j] = (mnode_t *)(loadmodel->leafs + p); else { Con_Printf("Mod_LoadNodes: invalid leaf index %i (file has only %i leafs)\n", p, loadmodel->numleafs); out->children[j] = (mnode_t *)(loadmodel->leafs); //map it to the solid leaf } } //johnfitz } } Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } /* ================= Mod_LoadLeafs ================= */ void Mod_LoadLeafs (lump_t *l) { dleaf_t *in; mleaf_t *out; int i, j, count, p; in = (dleaf_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mleaf_t *) Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz if (count > 32767) Host_Error ("Mod_LoadLeafs: %i leafs exceeds limit of 32767.\n", count); //johnfitz loadmodel->leafs = out; loadmodel->numleafs = count; for ( i=0 ; iminmaxs[j] = LittleShort (in->mins[j]); out->minmaxs[3+j] = LittleShort (in->maxs[j]); } p = LittleLong(in->contents); out->contents = p; out->firstmarksurface = loadmodel->marksurfaces + (unsigned short)LittleShort(in->firstmarksurface); //johnfitz -- unsigned short out->nummarksurfaces = (unsigned short)LittleShort(in->nummarksurfaces); //johnfitz -- unsigned short 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]; //johnfitz -- removed code to mark surfaces as SURF_UNDERWATER } } /* ================= Mod_LoadClipnodes ================= */ void Mod_LoadClipnodes (lump_t *l) { dclipnode_t *in; mclipnode_t *out; //johnfitz -- was dclipnode_t int i, count; hull_t *hull; in = (dclipnode_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname); //johnfitz -- warn about exceeding old limits if (count > 32767) Con_Warning ("%i clipnodes exceeds standard limit of 32767.\n", count); //johnfitz loadmodel->clipnodes = out; loadmodel->numclipnodes = count; hull = &loadmodel->hulls[1]; 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]; 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 ; iplanenum = LittleLong(in->planenum); //johnfitz -- bounds check if (out->planenum < 0 || out->planenum >= loadmodel->numplanes) Host_Error ("Mod_LoadClipnodes: planenum out of bounds"); //johnfitz //johnfitz -- support clipnodes > 32k out->children[0] = (unsigned short)LittleShort(in->children[0]); out->children[1] = (unsigned short)LittleShort(in->children[1]); if (out->children[0] >= count) out->children[0] -= 65536; if (out->children[1] >= count) out->children[1] -= 65536; //johnfitz } } /* ================= Mod_MakeHull0 Duplicate the drawing hull structure as a clipping hull ================= */ void Mod_MakeHull0 (void) { mnode_t *in, *child; mclipnode_t *out; //johnfitz -- was dclipnode_t int i, j, count; hull_t *hull; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; out = (mclipnode_t *) Hunk_AllocName ( count*sizeof(*out), loadname); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; for (i=0 ; iplanenum = 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; } } } /* ================= Mod_LoadMarksurfaces ================= */ void Mod_LoadMarksurfaces (lump_t *l) { int i, j, count; short *in; msurface_t **out; in = (short *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (msurface_t **) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; //johnfitz -- warn mappers about exceeding old limits if (count > 32767) Con_Warning ("%i marksurfaces exceeds standard limit of 32767.\n", count); //johnfitz for ( i=0 ; i= loadmodel->numsurfaces) Sys_Error ("Mod_ParseMarksurfaces: bad surface number"); out[i] = loadmodel->surfaces + j; } } /* ================= Mod_LoadSurfedges ================= */ void Mod_LoadSurfedges (lump_t *l) { int i, count; int *in, *out; in = (int *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (int *) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->surfedges = out; loadmodel->numsurfedges = count; for ( i=0 ; ifileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (mplane_t *) Hunk_AllocName ( count*2*sizeof(*out), loadname); loadmodel->planes = out; loadmodel->numplanes = count; for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); if (out->normal[j] < 0) bits |= 1<dist = LittleFloat (in->dist); out->type = LittleLong (in->type); out->signbits = bits; } } /* ================= RadiusFromBounds ================= */ float 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); } /* ================= Mod_LoadSubmodels ================= */ void Mod_LoadSubmodels (lump_t *l) { dmodel_t *in; dmodel_t *out; int i, j, count; in = (dmodel_t *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name); count = l->filelen / sizeof(*in); out = (dmodel_t *) Hunk_AllocName ( count*sizeof(*out), loadname); loadmodel->submodels = out; loadmodel->numsubmodels = count; for ( i=0 ; imins[j] = LittleFloat (in->mins[j]) - 1; out->maxs[j] = LittleFloat (in->maxs[j]) + 1; out->origin[j] = LittleFloat (in->origin[j]); } for (j=0 ; jheadnode[j] = LittleLong (in->headnode[j]); out->visleafs = LittleLong (in->visleafs); out->firstface = LittleLong (in->firstface); out->numfaces = LittleLong (in->numfaces); } // johnfitz -- check world visleafs -- adapted from bjp out = loadmodel->submodels; if (out->visleafs > MAX_MAP_LEAFS) Sys_Error ("Mod_LoadSubmodels: too many visleafs (%d, max = %d) in %s", out->visleafs, MAX_MAP_LEAFS, loadmodel->name); if (out->visleafs > 8192) Con_Warning ("%i visleafs exceeds standard limit of 8192.\n", out->visleafs); //johnfitz } /* ================= Mod_BoundsFromClipNode -- johnfitz update the model's clipmins and clipmaxs based on each node's plane. This works because of the way brushes are expanded in hull generation. Each brush will include all six axial planes, which bound that brush. Therefore, the bounding box of the hull can be constructed entirely from axial planes found in the clipnodes for that hull. ================= */ void Mod_BoundsFromClipNode (model_t *mod, int hull, int nodenum) { mplane_t *plane; mclipnode_t *node; if (nodenum < 0) return; //hit a leafnode node = &mod->clipnodes[nodenum]; plane = mod->hulls[hull].planes + node->planenum; switch (plane->type) { case PLANE_X: if (plane->signbits == 1) mod->clipmins[0] = q_min(mod->clipmins[0], -plane->dist - mod->hulls[hull].clip_mins[0]); else mod->clipmaxs[0] = q_max(mod->clipmaxs[0], plane->dist - mod->hulls[hull].clip_maxs[0]); break; case PLANE_Y: if (plane->signbits == 2) mod->clipmins[1] = q_min(mod->clipmins[1], -plane->dist - mod->hulls[hull].clip_mins[1]); else mod->clipmaxs[1] = q_max(mod->clipmaxs[1], plane->dist - mod->hulls[hull].clip_maxs[1]); break; case PLANE_Z: if (plane->signbits == 4) mod->clipmins[2] = q_min(mod->clipmins[2], -plane->dist - mod->hulls[hull].clip_mins[2]); else mod->clipmaxs[2] = q_max(mod->clipmaxs[2], plane->dist - mod->hulls[hull].clip_maxs[2]); break; default: //skip nonaxial planes; don't need them break; } Mod_BoundsFromClipNode (mod, hull, node->children[0]); Mod_BoundsFromClipNode (mod, hull, node->children[1]); } /* ================= Mod_LoadBrushModel ================= */ void Mod_LoadBrushModel (model_t *mod, void *buffer) { int i, j; dheader_t *header; dmodel_t *bm; float radius; //johnfitz loadmodel->type = mod_brush; header = (dheader_t *)buffer; i = LittleLong (header->version); if (i != BSPVERSION) Sys_Error ("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 ; ilumps[LUMP_VERTEXES]); Mod_LoadEdges (&header->lumps[LUMP_EDGES]); Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]); Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]); 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_MARKSURFACES]); Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]); Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]); Mod_LoadNodes (&header->lumps[LUMP_NODES]); Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]); Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]); Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]); Mod_MakeHull0 (); mod->numframes = 2; // regular and alternate animation // // set up the submodels (FIXME: this is confusing) // // johnfitz -- okay, so that i stop getting confused every time i look at this loop, here's how it works: // we're looping through the submodels starting at 0. Submodel 0 is the main model, so we don't have to // worry about clobbering data the first time through, since it's the same data. At the end of the loop, // we create a new copy of the data to use the next time through. for (i=0 ; inumsubmodels ; i++) { bm = &mod->submodels[i]; mod->hulls[0].firstclipnode = bm->headnode[0]; for (j=1 ; jhulls[j].firstclipnode = bm->headnode[j]; mod->hulls[j].lastclipnode = mod->numclipnodes-1; } mod->firstmodelsurface = bm->firstface; mod->nummodelsurfaces = bm->numfaces; VectorCopy (bm->maxs, mod->maxs); VectorCopy (bm->mins, mod->mins); //johnfitz -- calculate rotate bounds and yaw bounds radius = RadiusFromBounds (mod->mins, mod->maxs); mod->rmaxs[0] = mod->rmaxs[1] = mod->rmaxs[2] = mod->ymaxs[0] = mod->ymaxs[1] = mod->ymaxs[2] = radius; mod->rmins[0] = mod->rmins[1] = mod->rmins[2] = mod->ymins[0] = mod->ymins[1] = mod->ymins[2] = -radius; //johnfitz //johnfitz -- correct physics cullboxes so that outlying clip brushes on doors and stuff are handled right if (i > 0 || strcmp(mod->name, sv.modelname) != 0) //skip submodel 0 of sv.worldmodel, which is the actual world { // start with the hull0 bounds VectorCopy (mod->maxs, mod->clipmaxs); VectorCopy (mod->mins, mod->clipmins); // process hull1 (we don't need to process hull2 becuase there's // no such thing as a brush that appears in hull2 but not hull1) //Mod_BoundsFromClipNode (mod, 1, mod->hulls[1].firstclipnode); // (disabled for now becuase it fucks up on rotating models) } //johnfitz mod->numleafs = bm->visleafs; if (i < mod->numsubmodels-1) { // duplicate the basic information char name[10]; sprintf (name, "*%i", i+1); loadmodel = Mod_FindName (name); *loadmodel = *mod; strcpy (loadmodel->name, name); mod = loadmodel; } } } /* ============================================================================== ALIAS MODELS ============================================================================== */ aliashdr_t *pheader; stvert_t stverts[MAXALIASVERTS]; mtriangle_t triangles[MAXALIASTRIS]; // a pose is a single set of vertexes. a frame may be // an animating sequence of poses trivertx_t *poseverts[MAXALIASFRAMES]; int posenum; byte **player_8bit_texels_tbl; byte *player_8bit_texels; /* ================= Mod_LoadAliasFrame ================= */ void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame) { trivertx_t *pinframe; int i; daliasframe_t *pdaliasframe; pdaliasframe = (daliasframe_t *)pin; strcpy (frame->name, pdaliasframe->name); frame->firstpose = posenum; frame->numposes = 1; for (i=0 ; i<3 ; i++) { // these are byte values, so we don't have to worry about // endianness frame->bboxmin.v[i] = pdaliasframe->bboxmin.v[i]; frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i]; } pinframe = (trivertx_t *)(pdaliasframe + 1); poseverts[posenum] = pinframe; posenum++; pinframe += pheader->numverts; return (void *)pinframe; } /* ================= Mod_LoadAliasGroup ================= */ void *Mod_LoadAliasGroup (void * pin, maliasframedesc_t *frame) { daliasgroup_t *pingroup; int i, numframes; daliasinterval_t *pin_intervals; void *ptemp; pingroup = (daliasgroup_t *)pin; numframes = LittleLong (pingroup->numframes); frame->firstpose = posenum; frame->numposes = numframes; for (i=0 ; i<3 ; i++) { // these are byte values, so we don't have to worry about endianness frame->bboxmin.v[i] = pingroup->bboxmin.v[i]; frame->bboxmax.v[i] = pingroup->bboxmax.v[i]; } pin_intervals = (daliasinterval_t *)(pingroup + 1); frame->interval = LittleFloat (pin_intervals->interval); pin_intervals += numframes; ptemp = (void *)pin_intervals; for (i=0 ; inumverts; } return ptemp; } //========================================================= /* ================= Mod_FloodFillSkin Fill background pixels so mipmapping doesn't have haloes - Ed ================= */ typedef struct { short x, y; } floodfill_t; // must be a power of 2 #define FLOODFILL_FIFO_SIZE 0x1000 #define FLOODFILL_FIFO_MASK (FLOODFILL_FIFO_SIZE - 1) #define FLOODFILL_STEP( off, dx, dy ) \ do { \ if (pos[off] == fillcolor) \ { \ pos[off] = 255; \ fifo[inpt].x = x + (dx), fifo[inpt].y = y + (dy); \ inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; \ } \ else if (pos[off] != 255) fdc = pos[off]; \ } while (0) void Mod_FloodFillSkin( byte *skin, int skinwidth, int skinheight ) { byte fillcolor = *skin; // assume this is the pixel to fill floodfill_t fifo[FLOODFILL_FIFO_SIZE]; int inpt = 0, outpt = 0; int filledcolor = -1; int i; if (filledcolor == -1) { filledcolor = 0; // attempt to find opaque black for (i = 0; i < 256; ++i) if (d_8to24table[i] == (255 << 0)) // alpha 1.0 { filledcolor = i; break; } } // can't fill to filled color or to transparent color (used as visited marker) if ((fillcolor == filledcolor) || (fillcolor == 255)) { //printf( "not filling skin from %d to %d\n", fillcolor, filledcolor ); return; } fifo[inpt].x = 0, fifo[inpt].y = 0; inpt = (inpt + 1) & FLOODFILL_FIFO_MASK; while (outpt != inpt) { int x = fifo[outpt].x, y = fifo[outpt].y; int fdc = filledcolor; byte *pos = &skin[x + skinwidth * y]; outpt = (outpt + 1) & FLOODFILL_FIFO_MASK; if (x > 0) FLOODFILL_STEP( -1, -1, 0 ); if (x < skinwidth - 1) FLOODFILL_STEP( 1, 1, 0 ); if (y > 0) FLOODFILL_STEP( -skinwidth, 0, -1 ); if (y < skinheight - 1) FLOODFILL_STEP( skinwidth, 0, 1 ); skin[x + skinwidth * y] = fdc; } } /* =============== Mod_LoadAllSkins =============== */ void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype) { int i, j, k, size, groupskins; char name[32]; byte *skin, *texels; daliasskingroup_t *pinskingroup; daliasskininterval_t *pinskinintervals; char fbr_mask_name[64]; //johnfitz -- added for fullbright support src_offset_t offset; //johnfitz skin = (byte *)(pskintype + 1); if (numskins < 1 || numskins > MAX_SKINS) Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); size = pheader->skinwidth * pheader->skinheight; for (i=0 ; itype == ALIAS_SKIN_SINGLE) { Mod_FloodFillSkin( skin, pheader->skinwidth, pheader->skinheight ); // save 8 bit texels for the player model to remap texels = (byte *) Hunk_AllocName(size, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype + 1), size); //johnfitz -- rewritten q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, i); offset = (src_offset_t)(pskintype+1) - (src_offset_t)mod_base; if (Mod_CheckFullbrights ((byte *)(pskintype+1), size)) { pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_NOBRIGHT); q_snprintf (fbr_mask_name, sizeof(fbr_mask_name), "%s:frame%i_glow", loadmodel->name, i); pheader->fbtextures[i][0] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_FULLBRIGHT); } else { pheader->gltextures[i][0] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype+1), loadmodel->name, offset, TEXPREF_PAD); pheader->fbtextures[i][0] = NULL; } pheader->gltextures[i][3] = pheader->gltextures[i][2] = pheader->gltextures[i][1] = pheader->gltextures[i][0]; pheader->fbtextures[i][3] = pheader->fbtextures[i][2] = pheader->fbtextures[i][1] = pheader->fbtextures[i][0]; //johnfitz pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + size); } else { // animating skin group. yuck. pskintype++; pinskingroup = (daliasskingroup_t *)pskintype; groupskins = LittleLong (pinskingroup->numskins); pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); pskintype = (daliasskintype_t *)(pinskinintervals + groupskins); for (j=0 ; jskinwidth, pheader->skinheight ); if (j == 0) { texels = (byte *) Hunk_AllocName(size, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype), size); } //johnfitz -- rewritten q_snprintf (name, sizeof(name), "%s:frame%i_%i", loadmodel->name, i,j); offset = (src_offset_t)(pskintype) - (src_offset_t)mod_base; //johnfitz if (Mod_CheckFullbrights ((byte *)(pskintype), size)) { pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_NOBRIGHT); q_snprintf (fbr_mask_name, sizeof(fbr_mask_name), "%s:frame%i_%i_glow", loadmodel->name, i,j); pheader->fbtextures[i][j&3] = TexMgr_LoadImage (loadmodel, fbr_mask_name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_FULLBRIGHT); } else { pheader->gltextures[i][j&3] = TexMgr_LoadImage (loadmodel, name, pheader->skinwidth, pheader->skinheight, SRC_INDEXED, (byte *)(pskintype), loadmodel->name, offset, TEXPREF_PAD); pheader->fbtextures[i][j&3] = NULL; } //johnfitz pskintype = (daliasskintype_t *)((byte *)(pskintype) + size); } k = j; for (/* */; j < 4; j++) pheader->gltextures[i][j&3] = pheader->gltextures[i][j - k]; } } return (void *)pskintype; } //========================================================================= /* ================= Mod_CalcAliasBounds -- johnfitz -- calculate bounds of alias model for nonrotated, yawrotated, and fullrotated cases ================= */ void Mod_CalcAliasBounds (aliashdr_t *a) { int i,j,k; float dist, yawradius, radius; vec3_t v; //clear out all data for (i=0; i<3;i++) { loadmodel->mins[i] = loadmodel->ymins[i] = loadmodel->rmins[i] = 999999; loadmodel->maxs[i] = loadmodel->ymaxs[i] = loadmodel->rmaxs[i] = -999999; radius = yawradius = 0; } //process verts for (i=0 ; inumposes; i++) for (j=0; jnumverts; j++) { for (k=0; k<3;k++) v[k] = poseverts[i][j].v[k] * pheader->scale[k] + pheader->scale_origin[k]; for (k=0; k<3;k++) { loadmodel->mins[k] = q_min(loadmodel->mins[k], v[k]); loadmodel->maxs[k] = q_max(loadmodel->maxs[k], v[k]); } dist = v[0] * v[0] + v[1] * v[1]; if (yawradius < dist) yawradius = dist; dist += v[2] * v[2]; if (radius < dist) radius = dist; } //rbounds will be used when entity has nonzero pitch or roll radius = sqrt(radius); loadmodel->rmins[0] = loadmodel->rmins[1] = loadmodel->rmins[2] = -radius; loadmodel->rmaxs[0] = loadmodel->rmaxs[1] = loadmodel->rmaxs[2] = radius; //ybounds will be used when entity has nonzero yaw yawradius = sqrt(yawradius); loadmodel->ymins[0] = loadmodel->ymins[1] = -yawradius; loadmodel->ymaxs[0] = loadmodel->ymaxs[1] = yawradius; loadmodel->ymins[2] = loadmodel->mins[2]; loadmodel->ymaxs[2] = loadmodel->maxs[2]; } /* ================= Mod_SetExtraFlags -- johnfitz -- set up extra flags that aren't in the mdl ================= */ void Mod_SetExtraFlags (model_t *mod) { extern cvar_t r_nolerp_list; const char *s; char tmp[MAX_QPATH]; int i; if (!mod || !mod->name || mod->type != mod_alias) return; mod->flags &= 0xFF; //only preserve first byte // nolerp flag s = r_nolerp_list.string; while (*s) { // make a copy until the next comma or end of string i = 0; while (*s && *s != ',') { if (i < MAX_QPATH - 1) tmp[i++] = *s; s++; } tmp[i] = '\0'; //compare it to the model name if (!strcmp(mod->name, tmp)) { mod->flags |= MOD_NOLERP; break; } //search forwards to the next comma or end of string while (*s && *s == ',') s++; } // noshadow flag (TODO: make this a cvar list) if (!strcmp (mod->name, "progs/flame2.mdl") || !strcmp (mod->name, "progs/flame.mdl") || !strcmp (mod->name, "progs/bolt1.mdl") || !strcmp (mod->name, "progs/bolt2.mdl") || !strcmp (mod->name, "progs/bolt3.mdl") || !strcmp (mod->name, "progs/laser.mdl")) mod->flags |= MOD_NOSHADOW; // fullbright hack (TODO: make this a cvar list) if (!strcmp (mod->name, "progs/flame2.mdl") || !strcmp (mod->name, "progs/flame.mdl") || !strcmp (mod->name, "progs/boss.mdl")) mod->flags |= MOD_FBRIGHTHACK; } /* ================= Mod_LoadAliasModel ================= */ void Mod_LoadAliasModel (model_t *mod, void *buffer) { int i, j; mdl_t *pinmodel; stvert_t *pinstverts; dtriangle_t *pintriangles; int version, numframes; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; int start, end, total; start = Hunk_LowMark (); pinmodel = (mdl_t *)buffer; mod_base = (byte *)buffer; //johnfitz version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) Sys_Error ("%s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); // // allocate space for a working header, plus all the data except the frames, // skin and group info // size = sizeof (aliashdr_t) + (LittleLong (pinmodel->numframes) - 1) * sizeof (pheader->frames[0]); pheader = (aliashdr_t *) Hunk_AllocName (size, loadname); mod->flags = LittleLong (pinmodel->flags); // // endian-adjust and copy the data, starting with the alias model header // pheader->boundingradius = LittleFloat (pinmodel->boundingradius); pheader->numskins = LittleLong (pinmodel->numskins); pheader->skinwidth = LittleLong (pinmodel->skinwidth); pheader->skinheight = LittleLong (pinmodel->skinheight); if (pheader->skinheight > MAX_LBM_HEIGHT) Sys_Error ("model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); pheader->numverts = LittleLong (pinmodel->numverts); if (pheader->numverts <= 0) Sys_Error ("model %s has no vertices", mod->name); if (pheader->numverts > MAXALIASVERTS) Sys_Error ("model %s has too many vertices", mod->name); pheader->numtris = LittleLong (pinmodel->numtris); if (pheader->numtris <= 0) Sys_Error ("model %s has no triangles", mod->name); pheader->numframes = LittleLong (pinmodel->numframes); numframes = pheader->numframes; if (numframes < 1) Sys_Error ("Mod_LoadAliasModel: Invalid # of frames: %d\n", numframes); pheader->size = LittleFloat (pinmodel->size) * ALIAS_BASE_SIZE_RATIO; mod->synctype = (synctype_t) LittleLong (pinmodel->synctype); mod->numframes = pheader->numframes; for (i=0 ; i<3 ; i++) { pheader->scale[i] = LittleFloat (pinmodel->scale[i]); pheader->scale_origin[i] = LittleFloat (pinmodel->scale_origin[i]); pheader->eyeposition[i] = LittleFloat (pinmodel->eyeposition[i]); } // // load the skins // pskintype = (daliasskintype_t *)&pinmodel[1]; pskintype = (daliasskintype_t *) Mod_LoadAllSkins (pheader->numskins, pskintype); // // load base s and t vertices // pinstverts = (stvert_t *)pskintype; for (i=0 ; inumverts ; i++) { stverts[i].onseam = LittleLong (pinstverts[i].onseam); stverts[i].s = LittleLong (pinstverts[i].s); stverts[i].t = LittleLong (pinstverts[i].t); } // // load triangle lists // pintriangles = (dtriangle_t *)&pinstverts[pheader->numverts]; for (i=0 ; inumtris ; i++) { triangles[i].facesfront = LittleLong (pintriangles[i].facesfront); for (j=0 ; j<3 ; j++) { triangles[i].vertindex[j] = LittleLong (pintriangles[i].vertindex[j]); } } // // load the frames // posenum = 0; pframetype = (daliasframetype_t *)&pintriangles[pheader->numtris]; for (i=0 ; itype); if (frametype == ALIAS_SINGLE) pframetype = (daliasframetype_t *) Mod_LoadAliasFrame (pframetype + 1, &pheader->frames[i]); else pframetype = (daliasframetype_t *) Mod_LoadAliasGroup (pframetype + 1, &pheader->frames[i]); } pheader->numposes = posenum; mod->type = mod_alias; Mod_SetExtraFlags (mod); //johnfitz Mod_CalcAliasBounds (pheader); //johnfitz // // build the draw lists // GL_MakeAliasModelDisplayLists (mod, pheader); // // move the complete, relocatable alias model to the cache // end = Hunk_LowMark (); total = end - start; Cache_Alloc (&mod->cache, total, loadname); if (!mod->cache.data) return; memcpy (mod->cache.data, pheader, total); Hunk_FreeToLowMark (start); } //============================================================================= /* ================= Mod_LoadSpriteFrame ================= */ void * Mod_LoadSpriteFrame (void * pin, mspriteframe_t **ppframe, int framenum) { dspriteframe_t *pinframe; mspriteframe_t *pspriteframe; int width, height, size, origin[2]; char name[64]; src_offset_t offset; //johnfitz pinframe = (dspriteframe_t *)pin; width = LittleLong (pinframe->width); height = LittleLong (pinframe->height); size = width * height; pspriteframe = (mspriteframe_t *) Hunk_AllocName (sizeof (mspriteframe_t),loadname); *ppframe = pspriteframe; pspriteframe->width = width; pspriteframe->height = height; origin[0] = LittleLong (pinframe->origin[0]); origin[1] = LittleLong (pinframe->origin[1]); pspriteframe->up = origin[1]; pspriteframe->down = origin[1] - height; pspriteframe->left = origin[0]; pspriteframe->right = width + origin[0]; //johnfitz -- image might be padded pspriteframe->smax = (float)width/(float)TexMgr_PadConditional(width); pspriteframe->tmax = (float)height/(float)TexMgr_PadConditional(height); //johnfitz q_snprintf (name, sizeof(name), "%s:frame%i", loadmodel->name, framenum); offset = (src_offset_t)(pinframe+1) - (src_offset_t)mod_base; //johnfitz pspriteframe->gltexture = TexMgr_LoadImage (loadmodel, name, width, height, SRC_INDEXED, (byte *)(pinframe + 1), loadmodel->name, offset, TEXPREF_PAD | TEXPREF_ALPHA | TEXPREF_NOPICMIP); //johnfitz -- TexMgr return (void *)((byte *)pinframe + sizeof (dspriteframe_t) + size); } /* ================= Mod_LoadSpriteGroup ================= */ void * Mod_LoadSpriteGroup (void * pin, mspriteframe_t **ppframe, int framenum) { dspritegroup_t *pingroup; mspritegroup_t *pspritegroup; int i, numframes; dspriteinterval_t *pin_intervals; float *poutintervals; void *ptemp; pingroup = (dspritegroup_t *)pin; numframes = LittleLong (pingroup->numframes); pspritegroup = (mspritegroup_t *) Hunk_AllocName (sizeof (mspritegroup_t) + (numframes - 1) * sizeof (pspritegroup->frames[0]), loadname); pspritegroup->numframes = numframes; *ppframe = (mspriteframe_t *)pspritegroup; pin_intervals = (dspriteinterval_t *)(pingroup + 1); poutintervals = (float *) Hunk_AllocName (numframes * sizeof (float), loadname); pspritegroup->intervals = poutintervals; for (i=0 ; iinterval); if (*poutintervals <= 0.0) Sys_Error ("Mod_LoadSpriteGroup: interval<=0"); poutintervals++; pin_intervals++; } ptemp = (void *)pin_intervals; for (i=0 ; iframes[i], framenum * 100 + i); } return ptemp; } /* ================= Mod_LoadSpriteModel ================= */ void Mod_LoadSpriteModel (model_t *mod, void *buffer) { int i; int version; dsprite_t *pin; msprite_t *psprite; int numframes; int size; dspriteframetype_t *pframetype; pin = (dsprite_t *)buffer; mod_base = (byte *)buffer; //johnfitz version = LittleLong (pin->version); if (version != SPRITE_VERSION) Sys_Error ("%s has wrong version number " "(%i should be %i)", mod->name, version, SPRITE_VERSION); numframes = LittleLong (pin->numframes); size = sizeof (msprite_t) + (numframes - 1) * sizeof (psprite->frames); psprite = (msprite_t *) Hunk_AllocName (size, loadname); mod->cache.data = psprite; psprite->type = LittleLong (pin->type); psprite->maxwidth = LittleLong (pin->width); psprite->maxheight = LittleLong (pin->height); psprite->beamlength = LittleFloat (pin->beamlength); mod->synctype = (synctype_t) LittleLong (pin->synctype); psprite->numframes = numframes; mod->mins[0] = mod->mins[1] = -psprite->maxwidth/2; mod->maxs[0] = mod->maxs[1] = psprite->maxwidth/2; mod->mins[2] = -psprite->maxheight/2; mod->maxs[2] = psprite->maxheight/2; // // load the frames // if (numframes < 1) Sys_Error ("Mod_LoadSpriteModel: Invalid # of frames: %d\n", numframes); mod->numframes = numframes; pframetype = (dspriteframetype_t *)(pin + 1); for (i=0 ; itype); psprite->frames[i].type = frametype; if (frametype == SPR_SINGLE) { pframetype = (dspriteframetype_t *) Mod_LoadSpriteFrame (pframetype + 1, &psprite->frames[i].frameptr, i); } else { pframetype = (dspriteframetype_t *) Mod_LoadSpriteGroup (pframetype + 1, &psprite->frames[i].frameptr, i); } } mod->type = mod_sprite; } //============================================================================= /* ================ Mod_Print ================ */ void Mod_Print (void) { int i; model_t *mod; Con_SafePrintf ("Cached models:\n"); //johnfitz -- safeprint instead of print for (i=0, mod=mod_known ; i < mod_numknown ; i++, mod++) { Con_SafePrintf ("%8p : %s\n", mod->cache.data, mod->name); //johnfitz -- safeprint instead of print } Con_Printf ("%i models\n",mod_numknown); //johnfitz -- print the total too }