/* 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. */ // 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" extern cvar_t *gl_glowmap; // 1999-12-28 OpenGL fullbright fix by Neal White III #ifdef QSB #define MAX_MOD_KNOWN 4096 #else #define MAX_MOD_KNOWN 512 #endif model_t mod_known[MAX_MOD_KNOWN]; int mod_numknown; /* =================== 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"); 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 loadedfile_t *fileinfo; // 2001-09-12 Returning information about loaded file by Maddes if (!mod->needload) { if (mod->type == mod_alias) { d = Cache_Check (&mod->cache); if (d) 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 // // 2001-09-12 Returning information about loaded file by Maddes start /* buf = (unsigned *)COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); if (!buf) */ fileinfo = COM_LoadStackFile (mod->name, stackbuf, sizeof(stackbuf)); if (!fileinfo) // 2001-09-12 Returning information about loaded file by Maddes end { if (crash) Sys_Error ("Mod_NumForName: %s not found", mod->name); return NULL; } buf = (unsigned *)fileinfo->data; // 2001-09-12 Returning information about loaded file by Maddes // // allocate a new model // COM_FileBase (mod->name, loadname); loadmodel = mod; // // fill it in // // call the apropriate loader mod->needload = false; switch (LittleLong(*(unsigned *)buf)) { case IDPOLYHEADER: Mod_LoadAliasModel (mod, buf); break; case IDSPRITEHEADER: Mod_LoadSpriteModel (mod, buf); break; default: Mod_LoadBrushModel (mod, buf, fileinfo); // 2001-09-12 .ENT support by Maddes break; } return mod; } /* =============================================================================== BRUSHMODEL LOADING =============================================================================== */ /* ================= Mod_LoadTextures ================= */ void Mod_LoadTextures (lump_t *l) { // 1999-12-28 OpenGL fullbright fix by Neal White III start // Neal White III - 12-28-1999 - OpenGL fullbright bugfix // nwhite@softblox.com // http://home.telefragged.com/wally/ // // Problem: // // There was a problem in the original glquake with fullbright texels. // In the software renderer, fullbrights glow brightly in the dark. // Essentially, the fullbrights were ignored. I've fixed it by // adding another rendering pass and creating a new glowmap texture. // // Fix: // // When a texture with fullbright (FB) texels is loaded, a copy is made, // then the FB pixels are cleared to black in the original texture. In // the copy, all normal colors are cleared and only the FBs remain. When // it comes time to render the polygons, I do an additional pass and ADD // the glowmap on top of the current polygon. byte *ptexel; qboolean hasfullbrights; // qboolean noglow = COM_CheckParm("-noglow"); // int i, j, pixels, num, max, altmax; int i, num, max, altmax; unsigned long j, pixels; // 1999-12-28 OpenGL fullbright fix by Neal White III end miptex_t *mt; texture_t *tx, *tx2; texture_t *anims[10]; texture_t *altanims[10]; dmiptexlump_t *m; 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); 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]); 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; // 1999-12-28 OpenGL fullbright fix by Neal White III start hasfullbrights = false; if ((!strncmp(mt->name,"sky",3)) || (!strncmp(mt->name,"*",1)) || // turbulent (liquid) (!gl_glowmap->value) || (! gl_mtexable) ) { // sky has no lightmap, nor do liquids (so never needs a glowmap), // -noglow command line parameter, or no multi-texture support // // hasfullbrights is already false } else // check this texture for fullbright texels { ptexel = (byte *)(mt+1); for (j=0 ; j= 256-32) // 32 fullbright colors { hasfullbrights = true; break; } } } if (hasfullbrights) { tx = Hunk_AllocName (sizeof(texture_t) +pixels*2, loadname ); } else { // 1999-12-28 OpenGL fullbright fix by Neal White III end tx = Hunk_AllocName (sizeof(texture_t) +pixels, loadname ); } // 1999-12-28 OpenGL fullbright fix by Neal White III 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); // 1999-12-28 OpenGL fullbright fix by Neal White III start if (hasfullbrights) { tx->glowoffsets[j] = mt->offsets[j] + sizeof(texture_t) - sizeof(miptex_t) + pixels; } else { tx->glowoffsets[j] = 0; } } // 1999-12-28 OpenGL fullbright fix by Neal White III end // the pixels immediately follow the structures memcpy ( tx+1, mt+1, pixels); // 1999-12-28 OpenGL fullbright fix by Neal White III start if (hasfullbrights) { ptexel = (byte *)(tx+1); memcpy ( ptexel+pixels, mt+1, pixels); } tx->flags = 0; // 1999-12-28 OpenGL fullbright fix by Neal White III end if (!strncmp(mt->name,"sky",3)) // 2000-07-09 Dedicated server bug in GLQuake fix by Nathan Cline start { if (cls.state != ca_dedicated) { // 2000-07-09 Dedicated server bug in GLQuake fix by Nathan Cline end R_InitSky (tx); // 2000-07-09 Dedicated server bug in GLQuake fix by Nathan Cline start } } // 2000-07-09 Dedicated server bug in GLQuake fix by Nathan Cline end else { texture_mode = GL_LINEAR_MIPMAP_NEAREST; //_LINEAR; // 1999-12-28 OpenGL fullbright fix by Neal White III start // remove glowing fullbright colors from base texture (make black) if (hasfullbrights) { #ifdef _DEBUG qboolean bColorUsed[256]; Con_DPrintf ("*** Fullbright Texture: \"%s\", %dx%d, %d pixels\n", mt->name, mt->width, mt->height, pixels); for (j=0 ; j<256 ; j++) bColorUsed[j] = false; #endif ptexel = (byte *)(tx+1); for (j=0 ; j= 256-32) // 32 fullbright colors { ptexel[j] = 0; // make fullbrights black } } #ifdef _DEBUG Con_DPrintf ("*** Normal colors: "); for (j=0 ; j<256-32 ; j++) { if (bColorUsed[j]) Con_DPrintf ("%d ", j); } Con_DPrintf ("\n"); Con_DPrintf ("*** Fullbrights: "); for (j=256-32 ; j<256 ; j++) { if (bColorUsed[j]) Con_DPrintf ("%d ", j); } Con_DPrintf ("\n"); #endif } #ifdef _DEBUG else { Con_DPrintf ("*** Normal Texture: \"%s\", %dx%d\n", mt->name, mt->width, mt->height); } #endif // 1999-12-28 OpenGL fullbright fix by Neal White III end tx->gl_texturenum = GL_LoadTexture (mt->name, tx->width, tx->height, (byte *)(tx+1), true, false); // 1999-12-28 OpenGL fullbright fix by Neal White III start // create glowmap texture (all black except for glowing fullbright colors) if (hasfullbrights) { #ifdef _DEBUG qboolean bGlowDoubleCheck = false; #endif char glowname[32]; memcpy (glowname, mt->name, sizeof(mt->name)); glowname[16] = '\0'; for (j=0 ; glowname[j] != '\0'; j++) ; glowname[j++] = '<'; glowname[j++] = 'G'; glowname[j++] = 'L'; glowname[j++] = 'O'; glowname[j++] = 'W'; glowname[j++] = '>'; glowname[j++] = '\0'; ptexel = (byte *)(tx+1) + pixels; for (j=0 ; jgl_glowtexnum = GL_LoadTexture (glowname, tx->width, tx->height, ptexel, true, false); tx->flags |= FLAG_HAS_GLOWMAP; #ifdef _DEBUG if (! bGlowDoubleCheck) Con_DPrintf ("INTERNAL ERROR: Mod_LoadTextures - FullBright texture \"%s\" has no FullBright colors!\n", glowname); #endif } // 1999-12-28 OpenGL fullbright fix by Neal White III end texture_mode = GL_LINEAR; } } // // 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]; } } } /* ================= Mod_LoadLighting ================= */ void Mod_LoadLighting (lump_t *l) { // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes start /* if (!l->filelen) { loadmodel->lightdata = NULL; return; } loadmodel->lightdata = Hunk_AllocName ( l->filelen, loadname); memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen); */ int i; byte *in, *out, *data; byte d; char litfilename[1024]; loadedfile_t *fileinfo; // 2001-09-12 Returning information about loaded file by Maddes loadmodel->lightdata = NULL; if (external_lit->value) { // check for a .LIT file strcpy(litfilename, loadmodel->name); COM_StripExtension(litfilename, litfilename); strcat(litfilename, ".lit"); // 2001-09-12 Returning information about loaded file by Maddes start /* data = (byte*) COM_LoadHunkFile(litfilename); if (data) */ fileinfo = COM_LoadHunkFile(litfilename); if (fileinfo) // 2001-09-12 Returning information about loaded file by Maddes end { Con_DPrintf("%s loaded from %s\n", litfilename, fileinfo->path->pack ? fileinfo->path->pack->filename : fileinfo->path->filename); // 2001-09-12 Displaying where .LIT file is loaded from by Maddes data = fileinfo->data; // 2001-09-12 Returning information about loaded file by Maddes if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T') { i = LittleLong(((int *)data)[1]); if (i == 1) { loadmodel->lightdata = data + 8; return; } else Con_Printf("Unknown .LIT file version (%d)\n", i); } else Con_Printf("Corrupt .LIT file (old version?), ignoring\n"); } // no .LIT found, expand the white lighting data to color } if (!l->filelen) return; loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, loadname); 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; } // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes end } /* ================ 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] > 512 /* 256 */ ) Sys_Error ("Bad surface extents"); } } /* ================= Mod_LoadFaces ================= */ void Mod_LoadFaces (lump_t *l) { dface_t *in; msurface_t *out; int i, count, surfnum; int planenum, side; in = (void *)(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 = 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); side = LittleShort(in->side); if (side) out->flags |= SURF_PLANEBACK; out->plane = loadmodel->planes + planenum; out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo); CalcSurfaceExtents (out); // lighting info for (i=0 ; istyles[i] = in->styles[i]; i = LittleLong(in->lightofs); if (i == -1) out->samples = NULL; else // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes start // out->samples = loadmodel->lightdata + i; out->samples = loadmodel->lightdata + (i * 3); // expand white lighting // 2001-09-11 Colored lightning by LordHavoc/Sarcazm/Maddes end // set the drawing flags flag if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky { out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED); #ifndef QUAKE2 GL_SubdivideSurface (out); // cut up polygon for warps #endif continue; } if (!strncmp(out->texinfo->texture->name,"*",1)) // 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; } } } /* ============================================================================== 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; int aliasbboxmins[3], aliasbboxmaxs[3]; // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz /* ================= Mod_LoadAliasFrame ================= */ void * Mod_LoadAliasFrame (void * pin, maliasframedesc_t *frame) { trivertx_t *pframe, *pinframe; int i, j; 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]; // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz start // frame->bboxmin.v[i] = pdaliasframe->bboxmax.v[i]; frame->bboxmax.v[i] = pdaliasframe->bboxmax.v[i]; aliasbboxmins[i] = min (frame->bboxmin.v[i], aliasbboxmins[i]); aliasbboxmaxs[i] = max (frame->bboxmax.v[i], aliasbboxmaxs[i]); // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz end } 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]; // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz start // frame->bboxmin.v[i] = pingroup->bboxmax.v[i]; frame->bboxmax.v[i] = pingroup->bboxmax.v[i]; aliasbboxmins[i] = min (frame->bboxmin.v[i], aliasbboxmins[i]); aliasbboxmaxs[i] = max (frame->bboxmax.v[i], aliasbboxmaxs[i]); // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz end } 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; extern unsigned d_8to24table[]; // 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 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; char name[32]; int s; byte *copy; byte *skin; byte *texels; daliasskingroup_t *pinskingroup; int groupskins; daliasskininterval_t *pinskinintervals; skin = (byte *)(pskintype + 1); if (numskins < 1 || numskins > MAX_SKINS) Sys_Error ("Mod_LoadAliasModel: Invalid # of skins: %d\n", numskins); s = 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 // if (!strcmp(loadmodel->name,"progs/player.mdl")) { texels = Hunk_AllocName(s, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype + 1), s); // } sprintf (name, "%s_%i", loadmodel->name, i); pheader->gl_texturenum[i][0] = pheader->gl_texturenum[i][1] = pheader->gl_texturenum[i][2] = pheader->gl_texturenum[i][3] = GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype + 1), true, false); pskintype = (daliasskintype_t *)((byte *)(pskintype+1) + s); } else { // animating skin group. yuck. pskintype++; pinskingroup = (daliasskingroup_t *)pskintype; groupskins = LittleLong (pinskingroup->numskins); pinskinintervals = (daliasskininterval_t *)(pinskingroup + 1); pskintype = (void *)(pinskinintervals + groupskins); for (j=0 ; jskinwidth, pheader->skinheight ); if (j == 0) { texels = Hunk_AllocName(s, loadname); pheader->texels[i] = texels - (byte *)pheader; memcpy (texels, (byte *)(pskintype), s); } sprintf (name, "%s_%i_%i", loadmodel->name, i,j); pheader->gl_texturenum[i][j&3] = GL_LoadTexture (name, pheader->skinwidth, pheader->skinheight, (byte *)(pskintype), true, false); pskintype = (daliasskintype_t *)((byte *)(pskintype) + s); } k = j; for (/* */; j < 4; j++) pheader->gl_texturenum[i][j&3] = pheader->gl_texturenum[i][j - k]; } } return (void *)pskintype; } //========================================================================= /* ================= 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, numskins; int size; daliasframetype_t *pframetype; daliasskintype_t *pskintype; int start, end, total; start = Hunk_LowMark (); pinmodel = (mdl_t *)buffer; 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 = 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 = 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]; // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz start aliasbboxmins[0] = aliasbboxmins[1] = aliasbboxmins[2] = 99999; aliasbboxmaxs[0] = aliasbboxmaxs[1] = aliasbboxmaxs[2] = -99999; // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz end 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; // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz start /* // FIXME: do this right mod->mins[0] = mod->mins[1] = mod->mins[2] = -16; mod->maxs[0] = mod->maxs[1] = mod->maxs[2] = 16; */ 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]; // Keep necessary minimum bounding box by Maddes start if (mod->mins[i] > -16) { mod->mins[i] = -16; } if (mod->maxs[i] < 16) { mod->maxs[i] = 16; } // Keep necessary minimum bounding box by Maddes end } // 2001-11-31 Vanishing entity in screen corners fix by LordHavoc/Tomaz end // // 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 i, width, height, size, origin[2]; unsigned short *ppixout; byte *ppixin; char name[64]; pinframe = (dspriteframe_t *)pin; width = LittleLong (pinframe->width); height = LittleLong (pinframe->height); size = width * height; pspriteframe = Hunk_AllocName (sizeof (mspriteframe_t),loadname); Q_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]; sprintf (name, "%s_%i", loadmodel->name, framenum); pspriteframe->gl_texturenum = GL_LoadTexture (name, width, height, (byte *)(pinframe + 1), true, true); 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; int version; dsprite_t *pin; msprite_t *psprite; int numframes; int size; dspriteframetype_t *pframetype; pin = (dsprite_t *)buffer; 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 = 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); } } 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); } }