// // Copyright(C) 1993-1996 Id Software, Inc. // Copyright(C) 2005-2014 Simon Howard // // 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. // // DESCRIPTION: // Preparation of data for rendering, // generation of lookups, caching, retrieval by name. // #include "essence/include.h" #include "i_swap.h" #include "i_system.h" #include "z_zone.h" #include "w_wad.h" #include "doomdef.h" #include "m_misc.h" #include "r_local.h" #include "p_local.h" #include "doomstat.h" #include "r_sky.h" #include "r_data.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; short colormap; } PACKEDATTR mappatch_t; // // Texture definition. // A DOOM wall texture is a list of patches // which are to be combined in a predefined order. // typedef struct { char name[8]; int masked; short width; short height; int obsolete; short patchcount; mappatch_t patches[1]; } PACKEDATTR maptexture_t; // A single patch from a texture definition, // basically a rectangular area within // the texture rectangle. typedef struct { // Block origin (allways UL), // which has allready accounted // for the internal origin of the patch. short originx; short originy; int patch; } texpatch_t; // A maptexturedef_t describes a rectangular texture, // which is composed of one or more mappatch_t structures // that arrange graphic patches. typedef struct texture_s texture_t; struct texture_s { // Keep name for switch changing, etc. char name[8]; short width; short height; // Index in textures list int index; // Next in hash table chain texture_t *next; // All the patches[patchcount] // are drawn back to front into the cached texture. short patchcount; texpatch_t patches[1]; }; int firstflat; int lastflat; int numflats; int firstpatch; int lastpatch; int numpatches; int firstspritelump; int lastspritelump; int numspritelumps; size_t numtextures = 0; texture_t** textures; texture_t** textures_hashtable; int* texturewidthmask; // needed for texture pegging fixed_t* textureheight; int* texturecompositesize; short** texturecolumnlump; unsigned short** texturecolumnofs; byte** texturecomposite; // for global animation int* flattranslation; int* texturetranslation; // needed for pre rendering fixed_t* spritewidth; fixed_t* spriteoffset; fixed_t* spritetopoffset; lighttable_t *colormaps; // // 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. // void R_DrawColumnInCache ( column_t* patch, byte* cache, int originy, int cacheheight ) { int count; int position; byte* source; while (patch->topdelta != 0xff) { source = (byte *)patch + 3; count = patch->length; position = originy + patch->topdelta; if (position < 0) { count += position; position = 0; } if (position + count > cacheheight) count = cacheheight - position; if (count > 0) ES_memcpy (cache + position, source, 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. // void R_GenerateComposite (int texnum) { byte* block; texture_t* texture; texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; column_t* patchcol; short* collump; unsigned short* colofs; texture = textures[texnum]; block = Z_Malloc (texturecompositesize[texnum], PU_STATIC, &texturecomposite[texnum]); collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // Composite the columns together. patch = texture->patches; for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = W_CacheLumpNum (patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1<0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for ( ; x= 0) continue; patchcol = (column_t *)((byte *)realpatch + LONG(realpatch->columnofs[x-x1])); R_DrawColumnInCache (patchcol, block + colofs[x], patch->originy, texture->height); } } // Now that the texture has been built in column cache, // it is purgable from zone memory. Z_ChangeTag (block, PU_CACHE); } // // R_GenerateLookup // void R_GenerateLookup (int texnum) { texture_t* texture; byte* patchcount; // patchcount[texture->width] texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; short* collump; unsigned short* colofs; texture = textures[texnum]; // Composited texture not created yet. texturecomposite[texnum] = 0; texturecompositesize[texnum] = 0; collump = texturecolumnlump[texnum]; colofs = texturecolumnofs[texnum]; // 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. patchcount = (byte *) Z_Malloc(texture->width, PU_STATIC, &patchcount); ES_memset (patchcount, 0, texture->width); patch = texture->patches; for (i=0, patch = texture->patches; ipatchcount; i++, patch++) { realpatch = W_CacheLumpNum(patch->patch, PU_CACHE); x1 = patch->originx; x2 = x1 + SHORT(realpatch->width); if (x1 < 0) x = 0; else x = x1; if (x2 > texture->width) x2 = texture->width; for ( ; xpatch; colofs[x] = LONG(realpatch->columnofs[x-x1])+3; } } for (x=0; x < texture->width; x++) { if (!patchcount[x]) { ES_debugf("R_GenerateLookup: column without a patch (%s)\n", texture->name); return; } if (patchcount[x] > 1) { // Use the cached block. collump[x] = -1; colofs[x] = texturecompositesize[texnum]; if (texturecompositesize[texnum] > 0x10000-texture->height) { I_Error ("R_GenerateLookup: texture %i is >64k", texnum); } texturecompositesize[texnum] += texture->height; } } Z_Free(patchcount); } // // R_GetColumn // byte* R_GetColumn(int tex, int col) { int lump; int ofs; col &= texturewidthmask[tex]; lump = texturecolumnlump[tex][col]; ofs = texturecolumnofs[tex][col]; if (lump > 0) return (byte *)W_CacheLumpNum(lump,PU_CACHE)+ofs; if (!texturecomposite[tex]) R_GenerateComposite (tex); return texturecomposite[tex] + ofs; } static void GenerateTextureHashTable(void) { texture_t **rover; int i; int key; textures_hashtable = Z_Malloc(sizeof(texture_t *) * numtextures, PU_STATIC, 0); ES_memset(textures_hashtable, 0, sizeof(texture_t *) * numtextures); // Add all textures to hash table for (i=0; iindex = i; // Vanilla Doom does a linear search of the texures array // and stops at the first entry it finds. If there are two // entries with the same name, the first one in the array // wins. The new entry must therefore be added at the end // of the hash chain, so that earlier entries win. key = W_LumpNameHash(textures[i]->name) % numtextures; rover = &textures_hashtable[key]; while (*rover != NULL) { rover = &(*rover)->next; } // Hook into hash table textures[i]->next = NULL; *rover = textures[i]; } } // // 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; int j; int* maptex; int* maptex2; int* maptex1; char name[9]; char* names; char* name_p; int* patchlookup; int totalwidth; size_t nummappatches; int offset; int maxoff; int maxoff2; int numtextures1; int numtextures2; int* directory; int temp1; int temp2; int temp3; // Load the patch names from pnames.lmp. name[8] = 0; names = W_CacheLumpName("PNAMES", PU_STATIC); nummappatches = *(uint32_t*)names; name_p = names + 4; patchlookup = Z_Malloc(nummappatches * sizeof(*patchlookup), PU_STATIC, NULL); for (i = 0; i < nummappatches; i++) { M_StringCopy(name, name_p + i * 8, sizeof(name)); patchlookup[i] = W_CheckNumForName(name); if (patchlookup[i] == -1) { ES_warnf("R_InitTextures: Name not found %.9s\n", name); } } W_ReleaseLumpName("PNAMES"); // Load the map texture definitions from textures.lmp. // The data is contained in one or two lumps, // TEXTURE1 for shareware, plus TEXTURE2 for commercial. maptex = maptex1 = W_CacheLumpName ("TEXTURE1", PU_STATIC); numtextures1 = *maptex; maxoff = W_LumpLength (W_GetNumForName ("TEXTURE1")); directory = maptex+1; if (W_CheckNumForName ("TEXTURE2") != -1) { maptex2 = W_CacheLumpName ("TEXTURE2", PU_STATIC); numtextures2 = *maptex2; maxoff2 = W_LumpLength (W_GetNumForName ("TEXTURE2")); } else { maptex2 = NULL; numtextures2 = 0; maxoff2 = 0; } numtextures = numtextures1 + numtextures2; textures = Z_Malloc (numtextures * sizeof(*textures), PU_STATIC, 0); texturecolumnlump = Z_Malloc (numtextures * sizeof(*texturecolumnlump), PU_STATIC, 0); texturecolumnofs = Z_Malloc (numtextures * sizeof(*texturecolumnofs), PU_STATIC, 0); texturecomposite = Z_Malloc (numtextures * sizeof(*texturecomposite), PU_STATIC, 0); texturecompositesize = Z_Malloc (numtextures * sizeof(*texturecompositesize), PU_STATIC, 0); texturewidthmask = Z_Malloc (numtextures * sizeof(*texturewidthmask), PU_STATIC, 0); textureheight = Z_Malloc (numtextures * sizeof(*textureheight), PU_STATIC, 0); totalwidth = 0; // Really complex printing shit... temp1 = W_GetNumForName ("S_START"); // P_??????? temp2 = W_GetNumForName ("S_END") - 1; temp3 = ((temp2-temp1+63)/64) + ((numtextures+63)/64); for (i=0; i < numtextures; i++, directory++) { if (i == numtextures1) { // Start looking in second texture file. maptex = maptex2; maxoff = maxoff2; directory = maptex+1; } offset = *directory; if (offset > 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)*(mtexture->patchcount-1), PU_STATIC, 0); texture->width = mtexture->width; texture->height = mtexture->height; texture->patchcount = mtexture->patchcount; ES_memcpy (texture->name, mtexture->name, sizeof(texture->name)); mpatch = &mtexture->patches[0]; patch = &texture->patches[0]; for (j=0; j < texture->patchcount; j++, mpatch++, patch++) { if (mpatch->patch >= nummappatches) { I_Error ( "R_InitTexture: Out of bounds patch in texture %.8s (max_patches=%d)", texture->name, texture->patchcount ); } patch->originx = mpatch->originx; patch->originy = mpatch->originy; patch->patch = patchlookup[mpatch->patch]; if (patch->patch == -1 || patch->patch > numlumps) { I_Error ("R_InitTextures: Invalid patch in texture %.8s", texture->name); } } texturecolumnlump[i] = Z_Malloc (texture->width*sizeof(**texturecolumnlump), PU_STATIC,0); texturecolumnofs[i] = Z_Malloc (texture->width*sizeof(**texturecolumnofs), PU_STATIC,0); j = 1; while (j*2 <= texture->width) j<<=1; texturewidthmask[i] = j-1; textureheight[i] = texture->height<width; } Z_Free(patchlookup); W_ReleaseLumpName("TEXTURE1"); if (maptex2) W_ReleaseLumpName("TEXTURE2"); // Precalculate whatever possible. for (i=0 ; iwidth)<leftoffset)<topoffset)<name, name, 8) ) return texture->index; texture = texture->next; } return -1; } // // R_TextureNumForName // Calls R_CheckTextureNumForName, // aborts with error message. // int R_TextureNumForName (char* name) { int i; 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. // int flatmemory; int texturememory; int spritememory; void R_PrecacheLevel (void) { char* flatpresent; char* texturepresent; char* spritepresent; int i; int j; int k; int lump; texture_t* texture; thinker_t* th; spriteframe_t* sf; if (demoplayback) return; // Precache flats. flatpresent = Z_Malloc(numflats, PU_STATIC, NULL); ES_memset (flatpresent,0,numflats); for (i=0 ; ipatchcount; j++) { lump = texture->patches[j].patch; texturememory += lumpinfo[lump].size; W_CacheLumpNum(lump , PU_CACHE); } } Z_Free(texturepresent); // Precache sprites. spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL); ES_memset (spritepresent,0, numsprites); for (th = thinkercap.next ; th != &thinkercap ; th=th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) spritepresent[((mobj_t *)th)->sprite] = 1; } spritememory = 0; for (i=0 ; ilump[k]; spritememory += lumpinfo[lump].size; W_CacheLumpNum(lump , PU_CACHE); } } } Z_Free(spritepresent); }