/* Emacs style mode select -*- C++ -*- *----------------------------------------------------------------------------- * * * PrBoom a Doom port merged with LxDoom and LSDLDoom * based on BOOM, a modified and improved DOOM engine * Copyright (C) 1999 by * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman * Copyright (C) 1999-2000 by * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze * * 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. * * DESCRIPTION: * Preparation of data for rendering, * generation of lookups, caching, retrieval by name. * *-----------------------------------------------------------------------------*/ #include "doomstat.h" #include "w_wad.h" #include "r_main.h" #include "r_sky.h" #include "i_system.h" #include "m_swap.h" #include "p_tick.h" //#include "lprintf.h" // jff 08/03/98 - declaration of lprintf #include "rockmacros.h" // // Graphics. // DOOM graphics for walls and sprites // is stored in vertical runs of opaque pixels (posts). // A column is composed of zero or more posts, // a patch or sprite is composed of zero or more columns. // // // Texture definition. // Each texture is composed of one or more patches, // with patches being lumps stored in the WAD. // The lumps are referenced by number, and patched // into the rectangular texture space using origin // and possibly other attributes. // typedef struct { short originx; short originy; short patch; short stepdir; // unused in Doom but might be used in Phase 2 Boom short colormap; // unused in Doom but might be used in Phase 2 Boom } PACKEDATTR mappatch_t; typedef struct { char name[8]; boolean masked; short width; short height; char pad[4]; // unused in Doom but might be used in Boom Phase 2 short patchcount; mappatch_t patches[1]; } PACKEDATTR maptexture_t; // A maptexturedef_t describes a rectangular texture, which is composed // of one or more mappatch_t structures that arrange graphic patches. // killough 4/17/98: make firstcolormaplump,lastcolormaplump external int firstcolormaplump, lastcolormaplump; // killough 4/17/98 int firstflat, lastflat, numflats; int firstspritelump, lastspritelump, numspritelumps; int numtextures; static texture_t **textures; fixed_t *textureheight; //needed for texture pegging (and TFE fix - killough) int *flattranslation; // for global animation int *texturetranslation; // needed for pre-rendering fixed_t *spritewidth, *spriteoffset, *spritetopoffset; // // MAPTEXTURE_T CACHING // When a texture is first needed, // it counts the number of composite columns // required in the texture and allocates space // for a column directory and any new columns. // The directory will simply point inside other patches // if there is only one patch in a given column, // but any columns with multiple patches // will have new column_ts generated. // // // R_DrawColumnInCache // Clip and draw a column // from a patch into a cached post. // // Rewritten by Lee Killough for performance and to fix Medusa bug // void R_DrawColumnInCache(const column_t *patch, byte *cache, int originy, int cacheheight, byte *marks) { while (patch->topdelta != 0xff) { int count = patch->length; int position = originy + patch->topdelta; if (position < 0) { count += position; position = 0; } if (position + count > cacheheight) count = cacheheight - position; if (count > 0) { memcpy (cache + position, (byte *)patch + 3, count); // killough 4/9/98: remember which cells in column have been drawn, // so that column can later be converted into a series of posts, to // fix the Medusa bug. memset (marks + position, 0xff, count); } patch = (column_t *)((byte *) patch + patch->length + 4); } } // // R_GenerateComposite // Using the texture definition, // the composite texture is created from the patches, // and each column is cached. // // Rewritten by Lee Killough for performance and to fix Medusa bug void R_GenerateComposite(int texnum) { texture_t *texture = textures[texnum]; byte *block = Z_Malloc(texture->compositesize, PU_STATIC, (void **)&texture->composite); // Composite the columns together. texpatch_t *patch = texture->patches; short *collump = texture->columnlump; unsigned *colofs = texture->columnofs; // killough 4/9/98: make 32-bit int i = texture->patchcount; // killough 4/9/98: marks to identify transparent regions in merged textures byte *marks = calloc(texture->width, texture->height), *source; for (; --i >=0; patch++) { const patch_t *realpatch = W_CacheLumpNum(patch->patch); // cph int x1 = patch->originx, x2 = x1 + SHORT(realpatch->width); const int *cofs = realpatch->columnofs-x1; if (x1<0) x1 = 0; if (x2 > texture->width) x2 = texture->width; for (; x1originy,texture->height, marks + x1 * texture->height); W_UnlockLumpNum(patch->patch); // cph - unlock the patch lump } // killough 4/9/98: Next, convert multipatched columns into true columns, // to fix Medusa bug while still allowing for transparent regions. source = malloc(texture->height); // temporary column for (i=0; i < texture->width; i++) if (collump[i] == -1) // process only multipatched columns { column_t *col = (column_t *)(block + colofs[i] - 3); // cached column const byte *mark = marks + i * texture->height; int j = 0; // save column in temporary so we can shuffle it around memcpy(source, (byte *) col + 3, texture->height); for (;;) // reconstruct the column by scanning transparency marks { while (j < texture->height && !mark[j]) // skip transparent cells j++; if (j >= texture->height) // if at end of column { col->topdelta = -1; // end-of-column marker break; } col->topdelta = j; // starting offset of post for (col->length=0; j < texture->height && mark[j]; j++) col->length++; // count opaque cells // copy opaque cells from the temporary back into the column memcpy((byte *) col + 3, source + col->topdelta, col->length); col = (column_t *)((byte *) col + col->length + 4); // next post } } free(source); // free temporary column free(marks); // free transparency marks // Now that the texture has been built in column cache, // it is purgable from zone memory. Z_ChangeTag(block, PU_CACHE); } // // R_GenerateLookup // // Rewritten by Lee Killough for performance and to fix Medusa bug // static void R_GenerateLookup(int texnum, int *const errors) { texture_t *texture = textures[texnum]; // killough 4/9/98: make column offsets 32-bit; // clean up malloc-ing to use sizeof // CPhipps - moved allocing here short *collump = texture->columnlump = Z_Malloc(texture->width*sizeof(*texture->columnlump), PU_STATIC,0); unsigned *colofs = texture->columnofs = Z_Malloc(texture->width*sizeof(*texture->columnofs), PU_STATIC,0); // killough 4/9/98: keep count of posts in addition to patches. // Part of fix for medusa bug for multipatched 2s normals. struct { unsigned short patches, posts; } *count = calloc(sizeof *count, texture->width); { int i = texture->patchcount; const texpatch_t *patch = texture->patches; while (--i >= 0) { int pat = patch->patch; const patch_t *realpatch = W_CacheLumpNum(pat); int x1 = patch++->originx, x2 = x1 + SHORT(realpatch->width), x = x1; const int *cofs = realpatch->columnofs-x1; if (x2 > texture->width) x2 = texture->width; if (x1 < 0) x = 0; for ( ; xtopdelta != 0xff; count[x].posts++) col = (column_t *)((byte *) col + col->length + 4); count[x].patches++; collump[x] = pat; colofs[x] = LONG(cofs[x])+3; } W_UnlockLumpNum(pat); } } // Composited texture not created yet. texture->composite = NULL; // Now count the number of columns // that are covered by more than one patch. // Fill in the lump / offset, so columns // with only a single patch are all done. { int x = texture->width; int height = texture->height; int csize = 0; while (--x >= 0) { if (!count[x].patches) // killough 4/9/98 { //jff 8/3/98 use logical output routine printf("\nR_GenerateLookup: Column %d is without a patch in texture %s", x, texture->name); if (errors) ++*errors; else I_Error("R_GenerateLookup: Failed"); } if (count[x].patches > 1) // killough 4/9/98 { // killough 1/25/98, 4/9/98: // // Fix Medusa bug, by adding room for column header // and trailer bytes for each post in merged column. // For now, just allocate conservatively 4 bytes // per post per patch per column, since we don't // yet know how many posts the merged column will // require, and it's bounded above by this limit. collump[x] = -1; // mark lump as multipatched colofs[x] = csize + 3; // three header bytes in a column csize += 4*count[x].posts+1; // 1 stop byte plus 4 bytes per post } csize += height; // height bytes of texture data } texture->compositesize = csize; } free(count); // killough 4/9/98 } // // R_GetColumn // const byte *R_GetColumn(int tex, int col) { const texture_t *texture = textures[tex]; if (!texture->columnlump) R_GenerateLookup(tex, NULL); { int lump = texture->columnlump[col &= texture->widthmask]; int ofs = texture->columnofs[col]; // cph - WARNING: must be after the above line // cph - remember the last lump, so we can unlock it if no longer needed, // or reuse it if possible to reduce lump locking/unlocking static int lastlump = -1; static const byte* lastlumpdata; if ((lump<=0) && (lastlump<=0)) lump = lastlump; // cph - force equal if (lump != lastlump) { // cph - must change the cached lump if (lastlump>0) W_UnlockLumpNum(lastlump); if ((lastlump = lump) > 0) lastlumpdata = W_CacheLumpNum(lump); #ifdef RANGECHECK else lastlumpdata = NULL; #endif } if (lump > 0) return lastlumpdata + ofs; if (!texture->composite) R_GenerateComposite(tex); return texture->composite + ofs; } } // // R_InitTextures // Initializes the texture list // with the textures from the world map. // void R_InitTextures (void) { maptexture_t *mtexture; texture_t *texture; mappatch_t *mpatch; texpatch_t *patch; int i, j; int maptex_lump[2] = {-1, -1}; const int *maptex; const int *maptex1, *maptex2; char name[9]; int names_lump; // cph - new wad lump handling const unsigned char *names; // cph - const char *name_p;// const*'s int *patchlookup; int totalwidth; int nummappatches; int offset; int maxoff, maxoff2; int numtextures1, numtextures2; const int *directory; int errors = 0; // Load the patch names from pnames.lmp. name[8] = 0; names = W_CacheLumpNum(names_lump = W_GetNumForName("PNAMES")); nummappatches = (names[3]<<24)|(names[2]<<16)|(names[1]<<8)|names[0]; name_p = names+4; patchlookup = malloc(nummappatches*sizeof(*patchlookup)); // killough for (i=0 ; i maxoff) I_Error("R_InitTextures: Bad texture directory"); mtexture = (maptexture_t *) ( (byte *)maptex + offset); texture = textures[i] = Z_Malloc(sizeof(texture_t) + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1), PU_STATIC, 0); texture->width = SHORT(mtexture->width); texture->height = SHORT(mtexture->height); texture->patchcount = SHORT(mtexture->patchcount); /* Mattias EngdegÄrd emailed me of the following explenation of * why memcpy doesnt work on some systems: * "I suppose it is the mad unaligned allocation * going on (and which gcc in some way manages to cope with * through the __attribute__ ((packed))), and which it forgets * when optimizing memcpy (to a single word move) since it appears * to be aligned. Technically a gcc bug, but I can't blame it when * it's stressed with that amount of * non-standard nonsense." * So in short the unaligned struct confuses gcc's optimizer so * i took the memcpy out alltogether to avoid future problems-Jess */ /* The above was #ifndef SPARC, but i got a mail from * Putera Joseph F NPRI containing: * I had to use the memcpy function on a sparc machine. The * other one would give me a core dump. * cph - I find it hard to believe that sparc memcpy is broken, * but I don't believe the pointers to memcpy have to be aligned * either. Use fast memcpy on other machines anyway. */ /* proff - I took this out, because Oli Kraus (olikraus@yahoo.com) told me the memcpy produced a buserror. Since this function isn't time- critical I'm using the for loop now. */ /* #ifndef GCC memcpy(texture->name, mtexture->name, sizeof(texture->name)); #else */ { unsigned int j; for(j=0;jname);j++) texture->name[j]=mtexture->name[j]; } /* #endif */ mpatch = mtexture->patches; patch = texture->patches; for (j=0 ; jpatchcount ; j++, mpatch++, patch++) { patch->originx = SHORT(mpatch->originx); patch->originy = SHORT(mpatch->originy); patch->patch = patchlookup[SHORT(mpatch->patch)]; if (patch->patch == -1) { //jff 8/3/98 use logical output routine printf("\nR_InitTextures: Missing patch %d in texture %s", SHORT(mpatch->patch), texture->name); // killough 4/17/98 ++errors; } } texture->columnofs = NULL; texture->columnlump = NULL; for (j=1; j*2 <= texture->width; j<<=1) ; texture->widthmask = j-1; textureheight[i] = texture->height<width; } free(patchlookup); // killough for (i=0; i<2; i++) // cph - release the TEXTUREx lumps if (maptex_lump[i] != -1) W_UnlockLumpNum(maptex_lump[i]); if (errors) I_Error("R_InitTextures: %d errors", errors); // Precalculate whatever possible. if (devparm) // cph - If in development mode, generate now so all errors are found at once for (i=0 ; iindex = -1; while (--i >= 0) { int j = W_LumpNameHash(textures[i]->name) % (unsigned) numtextures; textures[i]->next = textures[j]->index; // Prepend to chain textures[j]->index = i; } } // // R_InitFlats // void R_InitFlats(void) { int i; firstflat = W_GetNumForName("F_START") + 1; lastflat = W_GetNumForName("F_END") - 1; numflats = lastflat - firstflat + 1; // Create translation table for global animation. // killough 4/9/98: make column offsets 32-bit; // clean up malloc-ing to use sizeof flattranslation = Z_Malloc((numflats+1)*sizeof(*flattranslation), PU_STATIC, 0); for (i=0 ; iwidth)<leftoffset)<topoffset)<=0); } // Next, compute all entries using minimum arithmetic. { int i,j; byte *tp = my_tranmap; for (i=0;i<256;i++) { long r1 = pal[0][i] * w2; long g1 = pal[1][i] * w2; long b1 = pal[2][i] * w2; if (!(i & 31) && progress) //jff 8/3/98 use logical output routine printf(" Computing: %d", 256/32-i/32); for (j=0;j<256;j++,tp++) { register int color = 255; register long err; long r = pal_w1[0][j] + r1; long g = pal_w1[1][j] + g1; long b = pal_w1[2][j] + b1; long best = LONG_MAX; do if ((err = tot[color] - pal[0][color]*r - pal[1][color]*g - pal[2][color]*b) < best) best = err, *tp = color; while (--color >= 0) ; } } } free(stackdeath); // Free this beast if (cachefd) // write out the cached translucency map { cache.pct = tran_filter_pct; memcpy(cache.playpal, playpal, 256); lseek(cachefd, 0, SEEK_SET); write(cachefd, &cache, sizeof cache); write(cachefd,main_tranmap, 256*256); // CPhipps - leave close for a few lines... } } if (cachefd) // killough 11/98: fix filehandle leak close(cachefd); W_UnlockLumpName("PLAYPAL"); } } // // R_InitData // Locates all the lumps // that will be used by all views // Must be called after W_Init. // void R_InitData (void) { R_InitTextures (); printf ("\nInitTextures"); R_InitFlats (); printf ("\nInitFlats"); R_InitSpriteLumps (); printf ("\nInitSprites"); if (general_translucency) // killough 3/1/98 R_InitTranMap(1); R_InitColormaps (); printf ("\nInitColormaps"); } // // R_FlatNumForName // Retrieval, get a flat number for a flat name. // // killough 4/17/98: changed to use ns_flats namespace // int R_FlatNumForName(const char *name) // killough -- const added { int i = (W_CheckNumForName)(name, ns_flats); if (i == -1) I_Error("R_FlatNumForName: %s not found", name); return i - firstflat; } // // R_CheckTextureNumForName // Check whether texture is available. // Filter out NoTexture indicator. // // Rewritten by Lee Killough to use hash table for fast lookup. Considerably // reduces the time needed to start new levels. See w_wad.c for comments on // the hashing algorithm, which is also used for lump searches. // // killough 1/21/98, 1/31/98 // int R_CheckTextureNumForName(const char *name) { int i = 0; if (*name != '-') // "NoTexture" marker. { i = textures[W_LumpNameHash(name) % (unsigned) numtextures]->index; while (i >= 0 && strncasecmp(textures[i]->name,name,8)) i = textures[i]->next; } return i; } // // R_TextureNumForName // Calls R_CheckTextureNumForName, // aborts with error message. // int R_TextureNumForName(const char *name) // const added -- killough { int i = R_CheckTextureNumForName(name); if (i == -1) I_Error("R_TextureNumForName: %s not found", name); return i; } // // R_PrecacheLevel // Preloads all relevant graphics for the level. // // Totally rewritten by Lee Killough to use less memory, // to avoid using alloca(), and to improve performance. // cph - new wad lump handling, calls cache functions but acquires no locks // Structures from p_spec.c // Used to fully cache animations in the level -> avoids stalls on Hard Drive Systems typedef struct { boolean istexture; int picnum; int basepic; int numpics; int speed; } anim_t; extern anim_t* anims; extern anim_t* lastanim; anim_t * isAnim(int flatnum, boolean texcheck) { anim_t *checkf; for(checkf=anims; checkf=checkf->basepic || flatnum<=checkf->numpics)&&checkf->istexture==texcheck) return checkf; } return 0; } void R_PrecacheLevel(void) { register int i, j; register byte *hitlist; anim_t *cacheanim; if (demoplayback) return; { size_t size = numflats > numsprites ? numflats : numsprites; hitlist = malloc((size_t)numtextures > size ? (unsigned)numtextures : size); } // Precache flats. memset(hitlist, 0, numflats); for (i = numsectors; --i >= 0; ) hitlist[sectors[i].floorpic] = hitlist[sectors[i].ceilingpic] = 1; // If flat is an animation, load those too // Definately not the most efficient, but better then stalls in game for(i=0; inumpics; j++) hitlist[cacheanim->basepic+j]=1; for (i = numflats; --i >= 0; ) if (hitlist[i]) (W_CacheLumpNum)(firstflat + i, 0); // Precache textures. memset(hitlist, 0, numtextures); for (i = numsides; --i >= 0;) hitlist[sides[i].bottomtexture] = hitlist[sides[i].toptexture] = hitlist[sides[i].midtexture] = 1; // If texture is an animation, load those too // Definately not the most efficient, but better then stalls in game for(i=0; inumpics; j++) hitlist[cacheanim->basepic+j]=1; // Sky texture is always present. // Note that F_SKY1 is the name used to // indicate a sky floor/ceiling as a flat, // while the sky texture is stored like // a wall texture, with an episode dependend // name. hitlist[skytexture] = 1; for (i = numtextures; --i >= 0; ) if (hitlist[i]) { texture_t *texture = textures[i]; int j = texture->patchcount; while (--j >= 0) (W_CacheLumpNum)(texture->patches[j].patch, 0); } // Precache sprites. memset(hitlist, 0, numsprites); { thinker_t *th; for (th = thinkercap.next ; th != &thinkercap ; th=th->next) if (th->function == P_MobjThinker) hitlist[((mobj_t *)th)->sprite] = 1; } for (i=numsprites; --i >= 0;) if (hitlist[i]) { int j = sprites[i].numframes; while (--j >= 0) { short *sflump = sprites[i].spriteframes[j].lump; int k = 7; do (W_CacheLumpNum)(firstspritelump + sflump[k], 0); while (--k >= 0); } } free(hitlist); } // Proff - Added for OpenGL void R_SetPatchNum(patchnum_t *patchnum, const char *name) { patch_t *patch; patch = (patch_t *) W_CacheLumpName(name); patchnum->width = patch->width; patchnum->height = patch->height; patchnum->leftoffset = patch->leftoffset; patchnum->topoffset = patch->topoffset; patchnum->lumpnum = W_GetNumForName(name); W_UnlockLumpName(name); }