// Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // // $Id:$ // // Copyright (C) 1993-1996 by id Software, Inc. // // This source is available for distribution and/or modification // only under the terms of the DOOM Source Code License as // published by id Software. All rights reserved. // // The source is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // // $Log:$ // // Revision 1.3 1997/01/29 20:10 // DESCRIPTION: // Preparation of data for rendering, // generation of lookups, caching, retrieval by name. // //----------------------------------------------------------------------------- #include "doom_config.h" #include "i_system.h" #include "z_zone.h" #include "m_swap.h" #include "w_wad.h" #include "doomdef.h" #include "r_local.h" #include "p_local.h" #include "doomstat.h" #include "r_sky.h" #if defined(DOOM_WIN32) // #define strncasecmp strnicmp #elif defined(DOOM_APPLE) #else #ifdef LINUX #include #endif #endif #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; } 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]; doom_boolean masked; short width; short height; //void **columndirectory; // OBSOLETE int columndirectory; // [pd] If it's not used, at least make sure it's the right size! Pointers are 8 bytes in x64 short patchcount; mappatch_t patches[1]; } 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. int originx; int 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 { // Keep name for switch changing, etc. char name[8]; short width; short height; // All the patches[patchcount] // are drawn back to front into the cached texture. short patchcount; texpatch_t patches[1]; } texture_t; int firstflat; int lastflat; int numflats; int firstpatch; int lastpatch; int numpatches; int firstspritelump; int lastspritelump; int numspritelumps; int numtextures; texture_t** textures; 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; int flatmemory; int texturememory; int spritememory; // // 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; byte* dest; dest = (byte*)cache + 3; 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) doom_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; i < texture->patchcount; 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 < x2; x++) { // Column does not have multiple patches? if (collump[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*)doom_malloc(texture->width); doom_memset(patchcount, 0, texture->width); patch = texture->patches; for (i = 0, patch = texture->patches; i < texture->patchcount; 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 < x2; x++) { patchcount[x]++; collump[x] = patch->patch; colofs[x] = LONG(realpatch->columnofs[x - x1]) + 3; } } for (x = 0; x < texture->width; x++) { if (!patchcount[x]) { //doom_print("R_GenerateLookup: column without a patch (%s)\n", // texture->name); doom_print("R_GenerateLookup: column without a patch ("); doom_print(texture->name); doom_print(")\n"); return; } // I_Error ("R_GenerateLookup: column without a patch"); if (patchcount[x] > 1) { // Use the cached block. collump[x] = -1; colofs[x] = texturecompositesize[texnum]; if (texturecompositesize[texnum] > 0x10000 - texture->height) { //I_Error("Error: R_GenerateLookup: texture %i is >64k", // texnum); doom_strcpy(error_buf, "Error: R_GenerateLookup: texture "); doom_concat(error_buf, doom_itoa(texnum, 10)); doom_concat(error_buf, " is >64k"); I_Error(error_buf); } texturecompositesize[texnum] += texture->height; } } doom_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; } // // 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; int 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 = LONG(*((int*)names)); name_p = names + 4; patchlookup = doom_malloc(nummappatches * sizeof(*patchlookup)); for (i = 0; i < nummappatches; i++) { doom_strncpy(name, name_p + i * 8, 8); patchlookup[i] = W_CheckNumForName(name); } Z_Free(names); // 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 = LONG(*maptex); maxoff = W_LumpLength(W_GetNumForName("TEXTURE1")); directory = maptex + 1; if (W_CheckNumForName("TEXTURE2") != -1) { maptex2 = W_CacheLumpName("TEXTURE2", PU_STATIC); numtextures2 = LONG(*maptex2); maxoff2 = W_LumpLength(W_GetNumForName("TEXTURE2")); } else { maptex2 = 0; numtextures2 = 0; maxoff2 = 0; } numtextures = numtextures1 + numtextures2; textures = Z_Malloc(numtextures * sizeof(texture_t*), PU_STATIC, 0); texturecolumnlump = Z_Malloc(numtextures * sizeof(short*), PU_STATIC, 0); texturecolumnofs = Z_Malloc(numtextures * sizeof(unsigned short*), PU_STATIC, 0); texturecomposite = Z_Malloc(numtextures * sizeof(byte*), PU_STATIC, 0); texturecompositesize = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); texturewidthmask = Z_Malloc(numtextures * sizeof(int), PU_STATIC, 0); textureheight = Z_Malloc(numtextures * sizeof(fixed_t), 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); doom_print("["); for (i = 0; i < temp3; i++) doom_print(" "); doom_print(" ]"); for (i = 0; i < temp3; i++) doom_print("\x8"); doom_print("\x8\x8\x8\x8\x8\x8\x8\x8\x8\x8"); for (i = 0; i < numtextures; i++, directory++) { if (!(i & 63)) doom_print("."); if (i == numtextures1) { // Start looking in second texture file. maptex = maptex2; maxoff = maxoff2; directory = maptex + 1; } offset = LONG(*directory); if (offset > maxoff) I_Error("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); doom_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++) { patch->originx = SHORT(mpatch->originx); patch->originy = SHORT(mpatch->originy); patch->patch = patchlookup[SHORT(mpatch->patch)]; if (patch->patch == -1) { //I_Error("Error: R_InitTextures: Missing patch in texture %s", // texture->name); doom_strcpy(error_buf, "Error: R_InitTextures: Missing patch in texture "); doom_concat(error_buf, texture->name); I_Error(error_buf); } } texturecolumnlump[i] = Z_Malloc(texture->width * sizeof(short), PU_STATIC, 0); texturecolumnofs[i] = Z_Malloc(texture->width * sizeof(unsigned short), PU_STATIC, 0); j = 1; while (j * 2 <= texture->width) j <<= 1; texturewidthmask[i] = j - 1; textureheight[i] = texture->height << FRACBITS; totalwidth += texture->width; } Z_Free(maptex1); if (maptex2) Z_Free(maptex2); // Precalculate whatever possible. for (i = 0; i < numtextures; i++) R_GenerateLookup(i); // Create translation table for global animation. texturetranslation = Z_Malloc((numtextures + 1) * sizeof(int), PU_STATIC, 0); for (i = 0; i < numtextures; i++) texturetranslation[i] = i; doom_free(patchlookup); } // // 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. flattranslation = Z_Malloc((numflats + 1) * sizeof(int), PU_STATIC, 0); for (i = 0; i < numflats; i++) flattranslation[i] = i; } // // R_InitSpriteLumps // Finds the width and hoffset of all sprites in the wad, // so the sprite does not need to be cached completely // just for having the header info ready during rendering. // void R_InitSpriteLumps(void) { int i; patch_t* patch; firstspritelump = W_GetNumForName("S_START") + 1; lastspritelump = W_GetNumForName("S_END") - 1; numspritelumps = lastspritelump - firstspritelump + 1; spritewidth = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); spriteoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); spritetopoffset = Z_Malloc(numspritelumps * sizeof(fixed_t), PU_STATIC, 0); for (i = 0; i < numspritelumps; i++) { if (!(i & 63)) doom_print("."); patch = W_CacheLumpNum(firstspritelump + i, PU_CACHE); spritewidth[i] = SHORT(patch->width) << FRACBITS; spriteoffset[i] = SHORT(patch->leftoffset) << FRACBITS; spritetopoffset[i] = SHORT(patch->topoffset) << FRACBITS; } } // // R_InitColormaps // void R_InitColormaps(void) { int lump, length; // Load in the light tables, // 256 byte align tables. lump = W_GetNumForName("COLORMAP"); length = W_LumpLength(lump) + 255; colormaps = Z_Malloc(length, PU_STATIC, 0); colormaps = (byte*)(((unsigned long long)colormaps + 255) & ~0xff); W_ReadLump(lump, colormaps); } // // 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(); doom_print("\nInitTextures"); R_InitFlats(); doom_print("\nInitFlats"); R_InitSpriteLumps(); doom_print("\nInitSprites"); R_InitColormaps(); doom_print("\nInitColormaps"); } // // R_FlatNumForName // Retrieval, get a flat number for a flat name. // int R_FlatNumForName(char* name) { int i; char namet[9]; i = W_CheckNumForName(name); if (i == -1) { namet[8] = 0; doom_memcpy(namet, name, 8); //I_Error("Error: R_FlatNumForName: %s not found", namet); doom_strcpy(error_buf, "Error: R_FlatNumForName: "); doom_concat(error_buf, namet); doom_concat(error_buf, " not found"); I_Error(error_buf); } return i - firstflat; } // // R_CheckTextureNumForName // Check whether texture is available. // Filter out NoTexture indicator. // int R_CheckTextureNumForName(char* name) { int i; // "NoTexture" marker. if (name[0] == '-') return 0; for (i = 0; i < numtextures; i++) if (!doom_strncasecmp(textures[i]->name, name, 8)) return i; 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("Error: R_TextureNumForName: %s not found", // name); doom_strcpy(error_buf, "Error: R_TextureNumForName: "); doom_concat(error_buf, name); doom_concat(error_buf, " not found"); I_Error(error_buf); } return i; } // // R_PrecacheLevel // Preloads all relevant graphics for the level. // 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 = doom_malloc(numflats); doom_memset(flatpresent, 0, numflats); for (i = 0; i < numsectors; i++) { flatpresent[sectors[i].floorpic] = 1; flatpresent[sectors[i].ceilingpic] = 1; } flatmemory = 0; for (i = 0; i < numflats; i++) { if (flatpresent[i]) { lump = firstflat + i; flatmemory += lumpinfo[lump].size; W_CacheLumpNum(lump, PU_CACHE); } } // Precache textures. texturepresent = doom_malloc(numtextures); doom_memset(texturepresent, 0, numtextures); for (i = 0; i < numsides; i++) { texturepresent[sides[i].toptexture] = 1; texturepresent[sides[i].midtexture] = 1; texturepresent[sides[i].bottomtexture] = 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. texturepresent[skytexture] = 1; texturememory = 0; for (i = 0; i < numtextures; i++) { if (!texturepresent[i]) continue; texture = textures[i]; for (j = 0; j < texture->patchcount; j++) { lump = texture->patches[j].patch; texturememory += lumpinfo[lump].size; W_CacheLumpNum(lump, PU_CACHE); } } // Precache sprites. spritepresent = doom_malloc(numsprites); doom_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; i < numsprites; i++) { if (!spritepresent[i]) continue; for (j = 0; j < sprites[i].numframes; j++) { sf = &sprites[i].spriteframes[j]; for (k = 0; k < 8; k++) { lump = firstspritelump + sf->lump[k]; spritememory += lumpinfo[lump].size; W_CacheLumpNum(lump, PU_CACHE); } } } doom_free(texturepresent); doom_free(flatpresent); doom_free(spritepresent); }