/* 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 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ // gl_model.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 byte mod_novis[MAX_MAP_LEAFS/8]; #define MAX_MOD_KNOWN 512 model_t mod_known[MAX_MOD_KNOWN]; int mod_numknown; /* =============== Mod_Init =============== */ void Mod_Init (void) { memset (mod_novis, 0xff, sizeof(mod_novis)); } /* =============== Mod_Extradata Caches the data if needed =============== */ void *Mod_Extradata (model_t *mod) { void *r; if ((r = Cache_Check (&mod->cache))) { 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 = PlaneDiff(p, plane); node = (d > 0) ? node->children[0] : 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 (!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_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; } } } /* ================== Mod_FindName ================== */ model_t *Mod_FindName (char *name) { int i; model_t *mod; if (!name[0]) Sys_Error ("Mod_ForName: NULL name"); // 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_strcpy (mod->name, name); mod->needload = true; mod_numknown++; } return mod; } /* ================== Mod_TouchModel ================== */ void Mod_TouchModel (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) { void *d; unsigned *buf; byte stackbuf[1024]; // avoid dirtying the cache heap if (!mod->needload) { if (mod->type == mod_alias) { if ((d = 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 if (!(buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)))) { if (crash) { Sys_Error ("Mod_NumForName: %s not found", mod->name); } return NULL; } // allocate a new model COM_FileBase (mod->name, loadname); // fill it in loadmodel = mod; // call the apropriate loader mod->needload = false; switch (LittleLong(*(unsigned *)buf)) { case IDPOLYHEADER: Mod_LoadAliasModel (mod, buf); break; case MD3IDHEADER: Mod_LoadQ3Model (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 (char *name, qboolean crash) { model_t *mod; mod = Mod_FindName (name); return Mod_LoadModel (mod, crash); } /* ================== Mod_HasFullbrights checks for fullbrights in models ================== */ qboolean Mod_HasFullbrights (byte *pixels, int size) { int i; for (i=0 ; i= 224) { return true; } } return false; } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ byte *mod_base; /* ================= Mod_LoadBrushModelTexture ================= */ int Mod_LoadBrushModelTexture (texture_t *tx, int flags) { char *name, *mapname; name = tx->name; mapname = CL_MapName (); if (loadmodel->isworldmodel) { if ((tx->gl_texturenum = GL_LoadTextureImage(va("textures/%s/%s", mapname, name), name, 0, 0, flags | TEX_BLEND))) { if (!ISTURBTEX(name)) { tx->fb_texturenum = GL_LoadTextureImage (va("textures/%s/%s_luma", mapname, name), va("@fb_%s", name), 0, 0, flags | TEX_LUMA); } } } else { if ((tx->gl_texturenum = GL_LoadTextureImage(va("textures/bmodels/%s", name), name, 0, 0, flags | TEX_BLEND))) { if (!ISTURBTEX(name)) { tx->fb_texturenum = GL_LoadTextureImage (va("textures/bmodels/%s_luma", name), va("@fb_%s", name), 0, 0, flags | TEX_LUMA); } } } if (!tx->gl_texturenum) { if ((tx->gl_texturenum = GL_LoadTextureImage(va("textures/%s", name), name, 0, 0, flags | TEX_BLEND))) { if (!ISTURBTEX(name)) { tx->fb_texturenum = GL_LoadTextureImage (va("textures/%s_luma", name), va("@fb_%s", name), 0, 0, flags | TEX_LUMA); } } } if (tx->fb_texturenum) { tx->is_lumatexture = true; } return tx->gl_texturenum; } /* ================= Mod_LoadTextures ================= */ void Mod_LoadTextures (lump_t *l) { int i, j, num, max, altmax, texture_flag, brighten_flag; texture_t *tx, *tx2, *txblock, *anims[10], *altanims[10]; miptex_t *mt; dmiptexlump_t *m; byte *data; if (!l->filelen) { loadmodel->textures = NULL; return; } m = (dmiptexlump_t *)(mod_base + l->fileofs); m->nummiptex = LittleLong (m->nummiptex); loadmodel->numtextures = m->nummiptex; loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures), loadname); txblock = Hunk_AllocName (m->nummiptex * sizeof(**loadmodel->textures), loadname); brighten_flag = (lightmode == 2) ? TEX_BRIGHTEN : 0; texture_flag = (gl_picmip_all.value || loadmodel->isworldmodel) ? TEX_MIPMAP : 0; for (i=0 ; inummiptex ; i++) { m->dataofs[i] = LittleLong (m->dataofs[i]); if (m->dataofs[i] == -1) continue; mt = (miptex_t *)((byte *)m + m->dataofs[i]); loadmodel->textures[i] = tx = txblock + i; memcpy (tx->name, mt->name, sizeof(tx->name)); if (!tx->name[0]) { Q_snprintfz (tx->name, sizeof(tx->name), "unnamed%d", i); Con_DPrintf ("Warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name); } tx->width = mt->width = LittleLong (mt->width); tx->height = mt->height = LittleLong (mt->height); if ((mt->width & 15) || (mt->height & 15)) { Sys_Error ("Mod_LoadTextures: Texture %s is not 16 aligned", mt->name); } for (j=0 ; joffsets[j] = LittleLong (mt->offsets[j]); } // HACK HACK HACK if (!strcmp(mt->name, "shot1sid") && mt->width == 32 && mt->height == 32 && CRC_Block((byte*)(mt+1), mt->width*mt->height) == 65393) { // This texture in b_shell1.bsp has some of the first 32 pixels painted white. // They are invisible in software, but look really ugly in GL. So we just copy // 32 pixels from the bottom to make it look nice. memcpy (mt+1, (byte *)(mt+1) + 32*31, 32); } if (loadmodel->isworldmodel && ISSKYTEX(tx->name)) { R_InitSky (mt); continue; } if (Mod_LoadBrushModelTexture(tx, texture_flag)) continue; if (mt->offsets[0]) { data = (byte *)mt + mt->offsets[0]; tx2 = tx; } else { data = (byte *)tx2 + tx2->offsets[0]; tx2 = r_notexture_mip; } tx->gl_texturenum = GL_LoadTexture (tx2->name, tx2->width, tx2->height, data, texture_flag | brighten_flag, 1); if (!ISTURBTEX(tx->name) && Mod_HasFullbrights (data, tx2->width * tx2->height)) { tx->fb_texturenum = GL_LoadTexture (va("@fb_%s", tx2->name), tx2->width, tx2->height, data, texture_flag | TEX_FULLBRIGHT, 1); } } // sequence the animations for (i=0 ; inummiptex ; i++) { tx = loadmodel->textures[i]; if (!tx || tx->name[0] != '+') continue; if (tx->anim_next) continue; // already sequenced // find the number of frames in the animation memset (anims, 0, sizeof(anims)); memset (altanims, 0, sizeof(altanims)); max = tx->name[1]; altmax = 0; if (max >= 'a' && max <= 'z') { max -= 'a' - 'A'; } if (max >= '0' && max <= '9') { max -= '0'; altmax = 0; anims[max] = tx; max++; } else if (max >= 'A' && max <= 'J') { altmax = max - 'A'; max = 0; altanims[altmax] = tx; altmax++; } else { Sys_Error ("Bad animating texture %s", tx->name); } for (j=i+1 ; jnummiptex ; j++) { tx2 = loadmodel->textures[j]; if (!tx2 || tx2->name[0] != '+') continue; if (strcmp(tx2->name+2, tx->name+2)) continue; num = tx2->name[1]; if (num >= 'a' && num <= 'z') { num -= 'a' - 'A'; } if (num >= '0' && num <= '9') { num -= '0'; anims[num] = tx2; if (num+1 > max) { max = num + 1; } } else if (num >= 'A' && num <= 'J') { num = num - 'A'; altanims[num] = tx2; if (num+1 > altmax) altmax = num+1; } else { Sys_Error ("Bad animating texture %s", tx->name); } } #define ANIM_CYCLE 2 // link them all together for (j=0 ; jname); } tx2->anim_total = max * ANIM_CYCLE; tx2->anim_min = j * ANIM_CYCLE; tx2->anim_max = (j+1) * ANIM_CYCLE; tx2->anim_next = anims[(j+1)%max]; if (altmax) { tx2->alternate_anims = altanims[0]; } } for (j=0 ; 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 (max) { tx2->alternate_anims = anims[0]; } } } } // joe: from FuhQuake static byte *Mod_LoadColoredLighting (char *name, char **litfilename) { byte *data; char *tmpname; if (!gl_loadlitfiles.value) { return NULL; } tmpname = CL_MapName (); if (strcmp(name, va("maps/%s.bsp", tmpname))) { return NULL; } *litfilename = va("maps/%s.lit", tmpname); data = COM_LoadHunkFile (*litfilename); if (!data) { *litfilename = va("lits/%s.lit", tmpname); data = COM_LoadHunkFile (*litfilename); } return data; } /* ================= Mod_LoadLighting ================= */ void Mod_LoadLighting (lump_t *l) { int i, lit_ver, mark; byte *in, *out, *data, d; char *litfilename; loadmodel->lightdata = NULL; if (!l->filelen) return; // check for a .lit file mark = Hunk_LowMark (); data = Mod_LoadColoredLighting (loadmodel->name, &litfilename); if (data) { if (com_filesize < 8 || strncmp((char *)data, "QLIT", 4)) { Con_Printf ("Corrupt .lit file (%s)...ignoring\n", COM_SkipPath(litfilename)); } else if (l->filelen * 3 + 8 != com_filesize) { Con_Printf ("Warning: .lit file (%s) has incorrect size\n", COM_SkipPath(litfilename)); } else if ((lit_ver = LittleLong(((int *)data)[1])) != 1) { Con_Printf ("Unknown .lit file version (v%d)\n", lit_ver); } else { Con_DPrintf ("Static coloured lighting loaded\n"); loadmodel->lightdata = data + 8; in = mod_base + l->fileofs; out = loadmodel->lightdata; for (i=0 ; ifilelen ; i++) { int b = max(out[3*i], max(out[3*i+1], out[3*i+2])); if (!b) { out[3*i] = out[3*i+1] = out[3*i+2] = in[i]; } else { // too bright float r = in[i] / (float)b; out[3*i+0] = (int)(r * out[3*i+0]); out[3*i+1] = (int)(r * out[3*i+1]); out[3*i+2] = (int)(r * out[3*i+2]); } } return; } Hunk_FreeToLowMark (mark); } // no .lit found, expand the white lighting data to color loadmodel->lightdata = Hunk_AllocName (l->filelen * 3, va("%s_@lightdata", loadmodel->name)); // place the file at the end, so it will not be overwritten until the very last write in = loadmodel->lightdata + l->filelen * 2; out = loadmodel->lightdata; memcpy (in, mod_base + l->fileofs, l->filelen); for (i = 0 ; i < l->filelen ; i++, out += 3) { d = *in++; out[0] = d; out[1] = d; out[2] = d; } } /* ================= Mod_LoadVisibility ================= */ void Mod_LoadVisibility (lump_t *l) { if (!l->filelen) { loadmodel->visdata = NULL; return; } loadmodel->visdata = Hunk_AllocName (l->filelen, loadname); memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen); } /* ================= Mod_LoadEntities ================= */ void Mod_LoadEntities (lump_t *l) { if (!l->filelen) { loadmodel->entities = NULL; return; } loadmodel->entities = 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 = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = 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_LoadSubmodels ================= */ void Mod_LoadSubmodels (lump_t *l) { dmodel_t *in, *out; int i, j, count; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); if (count > MAX_MODELS) { Sys_Error ("Mod_LoadSubmodels: count > MAX_MODELS"); } out = 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); } } /* ================= Mod_LoadEdges ================= */ 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)) { Sys_Error ("Mod_LoadEdges: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = 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; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Sys_Error ("Mod_LoadTexinfo: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_AllocName (count * sizeof(*out), loadname); loadmodel->texinfo = out; loadmodel->numtexinfo = count; for (i=0 ; ivecs[0][j] = LittleFloat (in->vecs[0][j]); } miptex = LittleLong (in->miptex); out->flags = LittleLong (in->flags); if (!loadmodel->textures) { out->texture = r_notexture_mip; // checkerboard texture out->flags = 0; } else { if (miptex >= loadmodel->numtextures) { Sys_Error ("Mod_LoadTexinfo: miptex >= loadmodel->numtextures"); } out->texture = loadmodel->textures[miptex]; if (!out->texture) { out->texture = r_notexture_mip; // texture not found out->flags = 0; } } } } /* ================ CalcSurfaceExtents Fills in s->texturemins[] and s->extents[] ================ */ void CalcSurfaceExtents (msurface_t *s) { float mins[2], maxs[2], val; int i, j, e, bmins[2], bmaxs[2]; mvertex_t *v; mtexinfo_t *tex; 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] > 512) { Host_Error ("CalcSurfaceExtents: Bad surface extents"); } } } //===============================================================================// typedef struct { char *name; int len; int flags; } sflags_t; static sflags_t surfaceflags[] = { // mirrors { "window", 6, SURF_MIRROR }, { "wizwin", 6, SURF_MIRROR }, { "window01_", 9, SURF_MIRROR }, { "window02_", 9, SURF_MIRROR }, { "window03_", 9, SURF_MIRROR }, // Runes { "rune", 4, SURF_METAL }, // Metal misc { "metal6_1", 8, SURF_METAL }, { "metal6_2", 8, SURF_METAL }, { "metal6_3", 8, SURF_METAL }, { "metal6_4", 8, SURF_METAL }, { "switch_1", 8, SURF_METAL }, { "wkey02_1", 8, SURF_METAL }, { "wkey02_2", 8, SURF_METAL }, { "wkey02_3", 8, SURF_METAL }, { "+0mtlsw", 7, SURF_METAL }, { "+1mtlsw", 7, SURF_METAL }, { "+2mtlsw", 7, SURF_METAL }, { "+3mtlsw", 7, SURF_METAL }, { "+amtlsw", 7, SURF_METAL }, { "arrow_m", 7, SURF_METAL }, { "azswitch3", 9, SURF_METAL }, { "key", 3, SURF_METAL }, // end of list { NULL, 0, 0 } }; static int numsflags = sizeof(surfaceflags) / sizeof(surfaceflags[0]) - 1; static int Mod_FindSurfaceFlags(char *name) { int i; for (i = 0; i < numsflags; i++) { if (surfaceflags[i].len > 0) { if (!strncmp(name, surfaceflags[i].name, surfaceflags[i].len)) { return surfaceflags[i].flags; } } else { if (!strcmp(name, surfaceflags[i].name)) { return surfaceflags[i].flags; } } } return 0; } /* ================= Mod_LoadFaces ================= */ void Mod_LoadFaces (lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum, planenum, side; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_AllocName (count * sizeof(*out), loadname); 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); if ((side = LittleShort(in->side))) { out->flags |= SURF_PLANEBACK; } out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); CalcSurfaceExtents (out); // lighting info for (i=0 ; istyles[i] = in->styles[i]; } i = LittleLong(in->lightofs); out->samples = (i == -1) ? NULL : loadmodel->lightdata + i * 3; // set the drawing flags flag if (ISSKYTEX(out->texinfo->texture->name)) { // sky out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); GL_SubdivideSurface (out); // cut up polygon for warps continue; } if (ISTURBTEX(out->texinfo->texture->name)) { // turbulent out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED); for (i=0 ; i<2 ; i++) { out->extents[i] = 16384; out->texturemins[i] = -8192; } GL_SubdivideSurface (out); // cut up polygon for warps continue; } // keep these flags seperate or else !! if (!(out->sflags = Mod_FindSurfaceFlags (out->texinfo->texture->name))) { out->sflags |= SURF_DETAIL; } } } /* ================= 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 = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_AllocName (count * sizeof(*out), loadname); 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 = LittleShort (in->firstface); out->numsurfaces = LittleShort (in->numfaces); for (j=0 ; j<2 ; j++) { p = LittleShort (in->children[j]); if (p >= 0) out->children[j] = loadmodel->nodes + p; else out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p)); } } Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs } /* ================= Mod_LoadLeafs ================= */ 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)) { Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_AllocName (count * sizeof(*out), loadname); 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 + LittleShort (in->firstmarksurface); out->nummarksurfaces = LittleShort(in->nummarksurfaces); p = LittleLong(in->visofs); out->compressed_vis = (p == -1) ? NULL : loadmodel->visdata + p; out->efrags = NULL; for (j=0 ; j<4 ; j++) { out->ambient_sound_level[j] = in->ambient_level[j]; } if (out->contents != CONTENTS_EMPTY) { for (j=0 ; jnummarksurfaces ; j++) { out->firstmarksurface[j]->flags |= SURF_UNDERWATER; } } } } /* ================= Mod_LoadClipnodes ================= */ void Mod_LoadClipnodes (lump_t *l) { dclipnode_t *in, *out; int i, count; hull_t *hull; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); count = l->filelen / sizeof(*in); out = Hunk_AllocName (count * sizeof(*out), loadname); 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); out->children[0] = LittleShort(in->children[0]); out->children[1] = LittleShort(in->children[1]); } } /* ================= Mod_MakeHull0 Deplicate the drawing hull structure as a clipping hull ================= */ void Mod_MakeHull0 (void) { mnode_t *in, *child; dclipnode_t *out; int i, j, count; hull_t *hull; hull = &loadmodel->hulls[0]; in = loadmodel->nodes; count = loadmodel->numnodes; out = Hunk_AllocName (count * sizeof(*out), loadname); hull->clipnodes = out; hull->firstclipnode = 0; hull->lastclipnode = count-1; hull->planes = loadmodel->planes; for (i=0 ; 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 = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_AllocName (count * sizeof(*out), loadname); loadmodel->marksurfaces = out; loadmodel->nummarksurfaces = count; 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, *in, *out; in = (void *)(mod_base + l->fileofs); if (l->filelen % sizeof(*in)) { Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = Hunk_AllocName (count * sizeof(*out), loadname); loadmodel->surfedges = out; loadmodel->numsurfedges = count; for (i=0 ; ifileofs); if (l->filelen % sizeof(*in)) { Sys_Error ("MOD_LoadBrushModel: funny lump size in %s", loadmodel->name); } count = l->filelen / sizeof(*in); out = 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_LoadBrushModel ================= */ void Mod_LoadBrushModel (model_t *mod, void *buffer) { int i, j; dheader_t *header; dmodel_t *bm; 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); } loadmodel->isworldmodel = !strcmp (loadmodel->name, va("maps/%s.bsp", cl_mapname.string)); // 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) 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); mod->radius = RadiusFromBounds (mod->mins, mod->maxs); 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 aliasbboxmins[3], aliasbboxmaxs[3]; /* ================= Mod_LoadAliasFrame ================= */ void *Mod_LoadAliasFrame (void *pin, maliasframedesc_t *frame) { int i; trivertx_t *pinframe; 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]; aliasbboxmins[i] = min(aliasbboxmins[i], frame->bboxmin.v[i]); aliasbboxmaxs[i] = max(aliasbboxmaxs[i], frame->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]; aliasbboxmins[i] = min(aliasbboxmins[i], frame->bboxmin.v[i]); aliasbboxmaxs[i] = max(aliasbboxmaxs[i], frame->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) \ { \ 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]; \ } 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 i, inpt = 0, outpt = 0, filledcolor = -1; 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)) 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_LoadAliasModelTexture ================= */ void Mod_LoadAliasModelTexture (char *identifier, int picmip_flag, int *gl_texnum, int *fb_texnum) { char loadpath[64]; Q_snprintfz (loadpath, sizeof(loadpath), "textures/models/%s", identifier); *gl_texnum = GL_LoadTextureImage (loadpath, identifier, 0, 0, picmip_flag | TEX_BLEND); if (*gl_texnum) { *fb_texnum = GL_LoadTextureImage (va("%s_luma", loadpath), va("@fb_%s", identifier), 0, 0, picmip_flag | TEX_LUMA); } if (!*gl_texnum) { Q_snprintfz (loadpath, sizeof(loadpath), "textures/%s", identifier); *gl_texnum = GL_LoadTextureImage (loadpath, identifier, 0, 0, picmip_flag); if (*gl_texnum) { *fb_texnum = GL_LoadTextureImage (va("%s_luma", loadpath), va("@fb_%s", identifier), 0, 0, picmip_flag | TEX_LUMA); } } } /* =============== Mod_LoadAllSkins =============== */ void *Mod_LoadAllSkins (int numskins, daliasskintype_t *pskintype) { int i, j, k, size, groupskins, gl_texnum, fb_texnum, picmip_flag; char basename[MAX_QPATH], identifier[MAX_QPATH]; byte *skin, *texels; daliasskingroup_t *pinskingroup; daliasskininterval_t *pinskinintervals; skin = (byte *)(pskintype + 1); if (numskins < 1 || numskins > MAX_SKINS) { Sys_Error ("Mod_LoadAllSkins: Invalid # of skins: %d\n", numskins); } size = pheader->skinwidth * pheader->skinheight; COM_StripExtension (COM_SkipPath(loadmodel->name), basename); picmip_flag = gl_picmip_all.value ? TEX_MIPMAP : 0; for (i=0 ; itype == ALIAS_SKIN_SINGLE) { Mod_FloodFillSkin (skin, pheader->skinwidth, pheader->skinheight); // save 8 bit texels for the player model to remap if (loadmodel->mflags & MOD_PLAYER) { texels = Hunk_AllocName (size, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype + 1), size); } Q_snprintfz (identifier, sizeof(identifier), "%s_%i", basename, i); gl_texnum = fb_texnum = 0; Mod_LoadAliasModelTexture (identifier, picmip_flag, &gl_texnum, &fb_texnum); if (!gl_texnum) { gl_texnum = GL_LoadTexture (identifier, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1), picmip_flag, 1); if (Mod_HasFullbrights((byte *)(pskintype + 1), pheader->skinwidth * pheader->skinheight)) { fb_texnum = GL_LoadTexture (va("@fb_%s", identifier), pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1), picmip_flag | TEX_FULLBRIGHT, 1); } } pheader->gl_texturenum[i][0] = pheader->gl_texturenum[i][1] = pheader->gl_texturenum[i][2] = pheader->gl_texturenum[i][3] = gl_texnum; pheader->fb_texturenum[i][0] = pheader->fb_texturenum[i][1] = pheader->fb_texturenum[i][2] = pheader->fb_texturenum[i][3] = fb_texnum; 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 = (void *)(pinskinintervals + groupskins); texels = Hunk_AllocName (size, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype), size); for (j=0 ; jskinwidth, pheader->skinheight); Q_snprintfz (identifier, sizeof(identifier), "%s_%i_%i", basename, i, j); gl_texnum = fb_texnum = 0; Mod_LoadAliasModelTexture (identifier, picmip_flag, &gl_texnum, &fb_texnum); if (!gl_texnum) { gl_texnum = GL_LoadTexture (identifier, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype), picmip_flag, 1); if (Mod_HasFullbrights((byte *)(pskintype), pheader->skinwidth * pheader->skinheight)) { fb_texnum = GL_LoadTexture (va("@fb_%s", identifier), pheader->skinwidth, pheader->skinheight, (byte *)(pskintype), picmip_flag | TEX_FULLBRIGHT, 1); } } pheader->gl_texturenum[i][j&3] = gl_texnum; pheader->fb_texturenum[i][j&3] = fb_texnum; pskintype = (daliasskintype_t *)((byte *)(pskintype) + size); } for (k=j ; j<4 ; j++) { pheader->gl_texturenum[i][j&3] = pheader->gl_texturenum[i][j-k]; } } } return (void *)pskintype; } //=========================================================================// // From Twilight. This nice feature makes all that fuss with strncmp // // feel lightyears away :) // // Many Thx. Reckless:Ohh by the way add as many flags here as you see fit// //=========================================================================// typedef struct { char *name; int len; int flags; } mflags_t; static mflags_t modelflags[] = { // Regular Quake { "progs/flame.mdl", 0, MOD_FLAME }, { "progs/flame0.mdl", 0, MOD_FLAME }, { "progs/flame2.mdl", 0, MOD_FLAME }, { "progs/fire.mdl", 0, MOD_FLAME }, { "progs/torch.mdl", 0, MOD_FLAME }, { "progs/bolt.mdl", 10, MOD_BOLT }, { "progs/spike.mdl", 0, MOD_SPIKE }, { "progs/s_spike.mdl", 0, MOD_SPIKE }, { "progs/player.mdl", 0, MOD_PLAYER }, { "progs/v_spike.mdl", 0, MOD_SPIKE }, { "progs/eyes.mdl", 0, MOD_EYES }, // Dissolution of Eternity { "progs/lavalball.mdl", 0, MOD_LAVABALL }, { "progs/fireball.mdl", 0, MOD_LAVABALL }, { "progs/lspike.mdl", 0, MOD_SPIKE }, // Common { "progs/v_shot.mdl", 0, MOD_WEAPON }, { "progs/v_shot2.mdl", 0, MOD_WEAPON }, { "progs/v_nail.mdl", 0, MOD_WEAPON }, { "progs/v_nail2.mdl", 0, MOD_WEAPON }, { "progs/v_rock.mdl", 0, MOD_WEAPON }, { "progs/v_rock2.mdl", 0, MOD_WEAPON }, { "progs/v_light.mdl", 0, MOD_WEAPON }, // hipnotic weapons { "progs/v_laserg.mdl", 0, MOD_WEAPON }, { "progs/v_prox.mdl", 0, MOD_WEAPON }, // rogue weapons { "progs/v_grpple.mdl", 0, MOD_WEAPON }, { "progs/v_lava.mdl", 0, MOD_WEAPON }, { "progs/v_lava2.mdl", 0, MOD_WEAPON }, { "progs/v_multi.mdl", 0, MOD_WEAPON }, { "progs/v_multi2.mdl", 0, MOD_WEAPON }, { "progs/v_plasma.mdl", 0, MOD_WEAPON }, { "progs/v_star.mdl", 0, MOD_WEAPON }, { "progs/v_", 8, MOD_WEAPON }, // end of list { NULL, 0, 0 } }; static int nummflags = sizeof(modelflags) / sizeof(modelflags[0]) - 1; static int Mod_FindModelFlags(char *name) { int i; for (i = 0; i < nummflags; i++) { if (modelflags[i].len > 0) { if (!strncmp(name, modelflags[i].name, modelflags[i].len)) { return modelflags[i].flags; } } else { if (!strcmp(name, modelflags[i].name)) { return modelflags[i].flags; } } } return 0; } /* ================= Mod_LoadAliasModel ================= */ void Mod_LoadAliasModel (model_t *mod, void *buffer) { int i, j, version, numframes, size, start, end, total; mdl_t *pinmodel; stvert_t *pinstverts; dtriangle_t *pintriangles; daliasframetype_t *pframetype; daliasskintype_t *pskintype; start = Hunk_LowMark (); pinmodel = (mdl_t *)buffer; version = LittleLong (pinmodel->version); if (version != ALIAS_VERSION) { Hunk_FreeToLowMark (start); Sys_Error ("Mod_LoadAliasModel: %s has wrong version number (%i should be %i)", mod->name, version, ALIAS_VERSION); return; } mod->mflags = Mod_FindModelFlags(mod->name); // 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 = 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 ("Mod_LoadAliasModel: model %s has a skin taller than %d", mod->name, MAX_LBM_HEIGHT); pheader->numverts = LittleLong (pinmodel->numverts); if (pheader->numverts <= 0) Sys_Error ("Mod_LoadAliasModel: model %s has no vertices", mod->name); if (pheader->numverts > MAXALIASVERTS) Sys_Error ("Mod_LoadAliasModel: model %s has too many vertices", mod->name); pheader->numtris = LittleLong (pinmodel->numtris); if (pheader->numtris <= 0) Sys_Error ("Mod_LoadAliasModel: 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 = 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 = 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]; aliasbboxmins[0] = aliasbboxmins[1] = aliasbboxmins[2] = 255; aliasbboxmaxs[0] = aliasbboxmaxs[1] = aliasbboxmaxs[2] = 0; 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; for (i=0 ; i<3 ; i++) { mod->mins[i] = aliasbboxmins[i] * pheader->scale[i] + pheader->scale_origin[i]; mod->maxs[i] = aliasbboxmaxs[i] * pheader->scale[i] + pheader->scale_origin[i]; } mod->radius = RadiusFromBounds (mod->mins, mod->maxs); // glow effects - set the defaults // has to be here anywhere else // breaks model skins or make the engine crash // i learned the hard way "sigh" debugging took // me ages enjoy you lucky bastards :/ mod->glowRadius = 30; mod->glowColours[0] = 0.7; mod->glowColours[1] = 0.2; mod->glowColours[2] = 0.05; if (!strcmp(mod->name, "progs/flame.mdl")) { mod->glowRadius = 19.0; mod->glowtype = FX_TORCH; } else if (!strcmp(mod->name, "progs/flame0.mdl")) { mod->glowRadius = 19.0; mod->glowtype = FX_TORCH; } else if (!strcmp(mod->name, "progs/flame2.mdl")) { mod->glowRadius = 19.0; mod->glowtype = FX_TORCH; } else if (!strncmp(mod->name, "progs/bolt",10)) { mod->glowRadius = 27.0; mod->glowColours[0] = 0.05; mod->glowColours[1] = 0.05; mod->glowColours[2] = 0.2; mod->glowtype = FX_BOLT; } else if (!strcmp(mod->name, "progs/missile.mdl")) { mod->glowtype = FX_MISSILE; } else if (!strcmp(mod->name, "progs/laser.mdl")) { mod->glowRadius = 10.0; mod->glowColours[0] = 0.25; mod->glowColours[1] = 0.025; mod->glowColours[2] = 0.025; mod->glowtype = FX_ENTITY; } else if (!strcmp(mod->name, "progs/w_spike.mdl")) { mod->glowRadius = 10.0; mod->glowColours[0] = 0.025; mod->glowColours[1] = 0.25; mod->glowColours[2] = 0.025; mod->glowtype = FX_ENTITY; } else if (!strcmp(mod->name, "progs/v_spike.mdl")) { mod->glowRadius = 10.0; mod->glowColours[0] = 0.25; mod->glowColours[1] = 0.025; mod->glowColours[2] = 0.25; mod->glowtype = FX_ENTITY; } else if (!strcmp(mod->name, "progs/lavaball.mdl")) { mod->glowRadius = 10.0; mod->glowColours[0] = 0.75; mod->glowColours[1] = 0.25; mod->glowColours[2] = 0.05; mod->glowtype = FX_ENTITY; } else if (!strcmp(mod->name, "progs/beam.mdl")) { mod->glowRadius = 10.0; mod->glowColours[0] = 0.05; mod->glowColours[1] = 0.05; mod->glowColours[2] = 0.2; mod->glowtype = FX_ENTITY; } else if (!strcmp(mod->name, "progs/candle.mdl")) { mod->glowRadius = 19.0; mod->glowtype = FX_TORCH; } else if (!strcmp(mod->name, "progs/lantern.mdl")) { mod->glowRadius = 19.0; mod->glowtype = FX_TORCH; } else if (!strcmp(mod->name, "progs/lspike.mdl")) { mod->glowRadius = 10.0; mod->glowColours[0] = 0.25; mod->glowColours[1] = 0.025; mod->glowColours[2] = 0.025; mod->glowtype = FX_ENTITY; } else if (!strcmp(mod->name, "progs/lasrspik.mdl")) { mod->glowRadius = 10.0; mod->glowColours[0] = 0.25; mod->glowColours[1] = 0.025; mod->glowColours[2] = 0.025; mod->glowtype = FX_ENTITY; } else if (!strcmp(mod->name, "progs/plasma.mdl")) { mod->glowRadius = 15.0; mod->glowColours[0] = 0.05; mod->glowColours[1] = 0.05; mod->glowColours[2] = 0.2; mod->glowtype = FX_ENTITY; } else if (!strcmp(mod->name, "progs/s_light.mdl")) { mod->glowRadius = 60.0; mod->glowColours[0] = 0.05; mod->glowColours[1] = 0.05; mod->glowColours[2] = 0.2; mod->glowtype = FX_SHAMBLER; } else if (!strcmp(mod->name, "progs/quaddama.mdl")) { mod->glowRadius = 20.0; mod->glowColours[0] = 0.05; mod->glowColours[1] = 0.05; mod->glowColours[2] = 0.2; mod->glowtype = FX_POWERUP; } else if (!strcmp(mod->name, "progs/invulner.mdl")) { mod->glowRadius = 20.0; mod->glowColours[0] = 0.2; mod->glowColours[1] = 0.05; mod->glowColours[2] = 0.05; mod->glowtype = FX_POWERUP; } else { mod->glowtype = FX_NORMAL; } // 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); } //============================================================================= static int spr_version; /* ================= Mod_LoadSpriteModelTexture ================= */ void Mod_LoadSpriteModelTexture (char *sprite_name, dspriteframe_t *pinframe, mspriteframe_t *pspriteframe, int framenum, int width, int height, int texture_flag) { char name[64], sprite[64], sprite2[64]; COM_StripExtension (sprite_name, sprite); Q_snprintfz (name, sizeof(name), "%s_%i", sprite_name, framenum); Q_snprintfz (sprite2, sizeof(sprite2), "textures/sprites/%s_%i", COM_SkipPath(sprite), framenum); pspriteframe->gl_texturenum = GL_LoadTextureImage (sprite2, name, 0, 0, texture_flag); if (pspriteframe->gl_texturenum == 0) { sprintf (sprite2, "textures/%s_%i", COM_SkipPath(sprite), framenum); pspriteframe->gl_texturenum = GL_LoadTextureImage (sprite2, name, 0, 0, texture_flag); } if (pspriteframe->gl_texturenum == 0) { if (spr_version == SPRITE32_VERSION) pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), texture_flag, 4); else pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), texture_flag, 1); } } /* ================= Mod_LoadSpriteFrame ================= */ void *Mod_LoadSpriteFrame (void *pin, mspriteframe_t **ppframe, int framenum) { dspriteframe_t *pinframe; mspriteframe_t *pspriteframe; int width, height, size, origin[2], texture_flag; texture_flag = (gl_picmip_all.value ? TEX_MIPMAP : 0) | TEX_ALPHA; pinframe = (dspriteframe_t *)pin; width = LittleLong (pinframe->width); height = LittleLong (pinframe->height); size = (spr_version == SPRITE32_VERSION) ? width * height * 4 : width * height; pspriteframe = Hunk_AllocName (sizeof(mspriteframe_t), loadname); memset (pspriteframe, 0, sizeof(mspriteframe_t)); *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]; Mod_LoadSpriteModelTexture (loadmodel->name, pinframe, pspriteframe, framenum, width, height, texture_flag); 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 = 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 = 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, numframes, size; dsprite_t *pin; msprite_t *psprite; dspriteframetype_t *pframetype; pin = (dsprite_t *)buffer; spr_version = LittleLong (pin->version); if (spr_version != SPRITE_VERSION && spr_version != SPRITE32_VERSION) Sys_Error ("%s has wrong version number (%i should be %i or %i)", mod->name, spr_version, SPRITE_VERSION, SPRITE32_VERSION); if (nospr32.value && spr_version == SPRITE32_VERSION) return; // joe: indicating whether it's a 32bit sprite or not mod->sprtype = (spr_version == SPRITE32_VERSION) ? MOD_SPR32 : MOD_SPR; numframes = LittleLong (pin->numframes); size = sizeof(msprite_t) + (numframes - 1) * sizeof(psprite->frames); psprite = 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 = 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); } // certain sprites are now drawn differently if (!strcmp (mod->name, "progs/s_explod.spr")) { psprite->drawType = SPR_EXPLODE; } else if (!strcmp (mod->name, "progs/s_light.spr")) { psprite->drawType = SPR_LIGHT; } else if (!strcmp (mod->name, "progs/s_bubble.spr")) { psprite->drawType = SPR_BUBBLE; } else if (!strcmp (mod->name, "progs/s_rain.spr")) { psprite->drawType = SPR_RAIN; } else { psprite->drawType = SPR_DRAW; } mod->type = mod_sprite; } //============================================================================= /* ================ Mod_Print ================ */ void Mod_Print (void) { int i; model_t *mod; Con_Printf ("Cached models:\n"); for (i = 0, mod = mod_known ; i < mod_numknown ; i++, mod++) Con_Printf ("%8p : %s\n", mod->cache.data, mod->name); }