// // 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 "doomstat.h" #include "i_swap.h" #include "m_fixed.h" #include "r_data.h" #include "r_defs.h" #include "r_state.h" #include "w_wad.h" /* #include #include #include #include "d_think.h" #include "i_system.h" #include "m_misc.h" #include "p_local.h" #include "p_mobj.h" #include "r_sky.h" #include "v_patch.h" #include "z_zone.h" */ #include #include "ipu_utils.h" #include "ipu_transfer.h" #include "ipu_malloc.h" struct texture_s; // // 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 PACKED_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 PACKED_STRUCT({ char name[8]; int masked; short width; short height; int obsolete; 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. 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]; }; typedef struct IPUpatchlesstexture_s IPUpatchlesstexture_t; struct IPUpatchlesstexture_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 IPUpatchlesstexture_t *next; }; int firstflat; int lastflat; int numflats; int firstpatch; int lastpatch; int numpatches; int firstspritelump; int lastspritelump; int numspritelumps; int numtextures; IPUpatchlesstexture_t *patchlesstextures; // JOSEF IPUpatchlesstexture_t **textures_hashtable; // JOSEF 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) 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 *)Z_Malloc(texture->width, PU_STATIC, &patchcount); 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]) { printf("R_GenerateLookup: column without a patch (%s)\n", texture->name); 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("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; } */ __SUPER__ static void GenerateTextureHashTable(void) { IPUpatchlesstexture_t **rover; int i; int key; textures_hashtable = IPU_malloc( sizeof(IPUpatchlesstexture_t *) * numtextures, IPUMALLOC_STATIC, "textures_hashtable" ); memset(textures_hashtable, 0, sizeof(IPUpatchlesstexture_t *) * numtextures); // Add all textures to hash table for (i = 0; i < numtextures; ++i) { // Store index patchlesstextures[i].index = 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(patchlesstextures[i].name) % numtextures; rover = &textures_hashtable[key]; while (*rover != NULL) { rover = &(*rover)->next; } // Hook into hash table patchlesstextures[i].next = NULL; *rover = &patchlesstextures[i]; } } // // R_InitTextures // Initializes the texture list // with the textures from the world map. // JOSEF: This is the version that runs on the render tiles // __SUPER__ void R_InitTextures(int* maptex) { maptexture_t *mtexture; IPUpatchlesstexture_t *texture; mappatch_t *mpatch; texpatch_t *patch; int i; int j; // int *maptex; // JOSEF 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; // JOSEF : Don't need patch info on the render tiles? // names = W_CacheLumpName(("PNAMES"), PU_STATIC); // nummappatches = LONG(*((int *)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); // } // W_ReleaseLumpName(("PNAMES")); // END JOSEF // 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); // JOSEF maxoff = *maptex++; // W_LumpLength(W_GetNumForName(("TEXTURE1"))); // JOSEF numtextures1 = LONG(*maptex); directory = maptex + 1; // JOSEF: Only support shareware version // if (W_CheckNumForName(("TEXTURE2")) != -1) { // maptex2 = W_CacheLumpName(("TEXTURE2"), PU_STATIC); // numtextures2 = LONG(*maptex2); // maxoff2 = W_LumpLength(W_GetNumForName(("TEXTURE2"))); // } else { maptex2 = NULL; numtextures2 = 0; maxoff2 = 0; // } numtextures = numtextures1 + numtextures2; patchlesstextures = IPU_malloc(numtextures * sizeof(IPUpatchlesstexture_t), IPUMALLOC_STATIC, "patchlesstextures"); // LATER: texturecolumnlump = IPU_static_malloc(numtextures * sizeof(*texturecolumnlump), "texturecolumnlump"); // LATER: texturecolumnofs = IPU_static_malloc(numtextures * sizeof(*texturecolumnofs), "texturecolumnofs"); // LATER: texturecomposite = IPU_static_malloc(numtextures * sizeof(*texturecomposite), "texturecomposite"); // LATER: texturecompositesize = IPU_static_malloc(numtextures * sizeof(*texturecompositesize), "texturecompositesize"); texturewidthmask = IPU_malloc(numtextures * sizeof(*texturewidthmask), IPUMALLOC_STATIC, "texturewidthmask"); textureheight = IPU_malloc(numtextures * sizeof(*textureheight), IPUMALLOC_STATIC, "textureheight"); // totalwidth = 0; // JOSEF: Unused? // Really complex printing shit... // JOSEF: Printing disabled // temp1 = W_GetNumForName(("S_START")); // P_??????? // temp2 = W_GetNumForName(("S_END")) - 1; // temp3 = ((temp2 - temp1 + 63) / 64) + ((numtextures + 63) / 64); // If stdout is a real console, use the classic vanilla "filling // up the box" effect, which uses backspace to "step back" inside // the box. If stdout is a file, don't draw the box. // if (I_ConsoleStdout()) { // JOSEF: Printing Disabled // printf("["); // for (i = 0; i < temp3 + 9; i++) // printf(" "); // printf("]"); // for (i = 0; i < temp3 + 10; i++) // printf("\b"); // } for (i = 0; i < numtextures; i++, directory++) { // if (!(i & 63)) // JOSEF // printf("."); if (i == numtextures1) { // Start looking in second texture file. maptex = maptex2; maxoff = maxoff2; directory = maptex + 1; } offset = LONG(*directory); if (offset > maxoff) printf("R_InitTextures: bad texture directory\n"); mtexture = (maptexture_t *)((byte *)maptex + offset); texture = &patchlesstextures[i]; // texture = patchlesstextures[i] = IPU_malloc( // sizeof(texture_t) + sizeof(texpatch_t) * (SHORT(mtexture->patchcount) - 1), // IPUMALLOC_STATIC, // "textures[i]" // ); texture->width = (int)SHORT(mtexture->width); texture->height = (int)SHORT(mtexture->height); // texture->patchcount = SHORT(mtexture->patchcount); // JOSEF // memcpy(texture->name, mtexture->name, sizeof(texture->name)); // JOSEF ((unsigned*)texture->name)[0] = ((unsigned*)mtexture->name)[0]; // JOSEF ((unsigned*)texture->name)[1] = ((unsigned*)mtexture->name)[1]; // JOSEF: patches unneeded for render tiles (?) // 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("R_InitTextures: Missing patch in texture %s", texture->name); // } // } // JOSEF: Later, if needed at all // 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 << FRACBITS; // totalwidth += texture->width; // JOSEF: Unused? } // JOSEF // Z_Free(patchlookup); // W_ReleaseLumpName(("TEXTURE1")); // if (maptex2) // W_ReleaseLumpName(("TEXTURE2")); // Precalculate whatever possible. // for (i = 0; i < numtextures; i++) // JOSEF // R_GenerateLookup(i); // Create translation table for global animation. texturetranslation = IPU_malloc( (numtextures + 1) * sizeof(*texturetranslation), IPUMALLOC_STATIC, "texturetranslation" ); for (i = 0; i < numtextures; i++) texturetranslation[i] = i; GenerateTextureHashTable(); } /* // // 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(*flattranslation), 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(*spritewidth), PU_STATIC, 0); spriteoffset = Z_Malloc(numspritelumps * sizeof(*spriteoffset), PU_STATIC, 0); spritetopoffset = Z_Malloc(numspritelumps * sizeof(*spritetopoffset), PU_STATIC, 0); for (i = 0; i < numspritelumps; i++) { if (!(i & 63)) printf("."); 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; // Load in the light tables, // 256 byte align tables. lump = W_GetNumForName(("COLORMAP")); colormaps = W_CacheLumpNum(lump, PU_STATIC); } // // 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("."); R_InitFlats(); printf("."); R_InitSpriteLumps(); printf("."); R_InitColormaps(); } // // 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; memcpy(namet, name, 8); I_Error("R_FlatNumForName: %s not found", namet); } return i - firstflat; } */ __SUPER__ int JOSEFstrsame(const char* restrict a, const char* restrict b) { for (int i = 0; i < 8; ++i) { char c_a = JOSEFtoupper(a[i]), c_b = JOSEFtoupper(b[i]); if (c_a != c_b) return 0; if (c_a == '\0' || c_b == '\0') return 1; } return 1; } // // R_CheckTextureNumForName // Check whether texture is available. // Filter out NoTexture indicator. // __SUPER__ int R_CheckTextureNumForName(char *name) { IPUpatchlesstexture_t *texture; int key; // "NoTexture" marker. if (name[0] == '-') return 0; key = W_LumpNameHash(name) % numtextures; texture = textures_hashtable[key]; while (texture != NULL) { // if (!strncasecmp(texture->name, name, 8)) // JOSEF if (JOSEFstrsame(texture->name, name)) return texture->index; texture = texture->next; } return -1; } // // R_TextureNumForName // Calls R_CheckTextureNumForName, // aborts with error message. // __SUPER__ int R_TextureNumForName(char *name) { int i; i = R_CheckTextureNumForName(name); if (i == -1) { printf("# # ERROR # # : R_TextureNumForName: %s not found\n", 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); 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); } } Z_Free(flatpresent); // Precache textures. texturepresent = Z_Malloc(numtextures, PU_STATIC, NULL); 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); } } Z_Free(texturepresent); // Precache sprites. spritepresent = Z_Malloc(numsprites, PU_STATIC, NULL); 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); } } } Z_Free(spritepresent); } */