/* =========================================================================== Doom 3 BFG Edition GPL Source Code Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code"). Doom 3 BFG Edition Source Code 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 3 of the License, or (at your option) any later version. Doom 3 BFG Edition Source Code 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 Doom 3 BFG Edition Source Code. If not, see . In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "Precompiled.h" #include "globaldata.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" #ifdef LINUX #include #endif #include "r_data.h" #include // // Graphics. // DOOM graphics for walls and ::g->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. // // // Texture definition. // A DOOM wall texture is a list of patches // which are to be combined in a predefined order. // // A single patch from a texture definition, // basically a rectangular area within // the texture rectangle. // A maptexturedef_t describes a rectangular texture, // which is composed of one or more mappatch_t structures // that arrange graphic patches. // for global animation // needed for pre rendering // // 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 postColumn_ts generated. // // // R_DrawColumnInCache // Clip and draw a column // from a patch into a cached post. // void R_DrawColumnInCache ( postColumn_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) memcpy (cache + position, source, count); patch = (postColumn_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; postColumn_t* patchcol; short* collump; unsigned short* colofs; texture = ::g->s_textures[texnum]; block = (byte*)DoomLib::Z_Malloc (::g->s_texturecompositesize[texnum], PU_CACHE_SHARED, &::g->s_texturecomposite[texnum]); collump = ::g->s_texturecolumnlump[texnum]; colofs = ::g->s_texturecolumnofs[texnum]; // Composite the columns together. patch = texture->patches; for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = (patch_t*)W_CacheLumpNum (patch->patch, PU_CACHE_SHARED); 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 = (postColumn_t *)((byte *)realpatch + LONG(realpatch->columnofs[x-x1])); R_DrawColumnInCache (patchcol, block + colofs[x], patch->originy, texture->height); } } } // // R_GenerateLookup // void R_GenerateLookup (int texnum) { texture_t* texture; texpatch_t* patch; patch_t* realpatch; int x; int x1; int x2; int i; short* collump; unsigned short* colofs; texture = ::g->s_textures[texnum]; // Composited texture not created yet. ::g->s_texturecomposite[texnum] = 0; ::g->s_texturecompositesize[texnum] = 0; collump = ::g->s_texturecolumnlump[texnum]; colofs = ::g->s_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. std::vector patchcount(texture->width, 0); patch = texture->patches; for (i=0 , patch = texture->patches; ipatchcount; i++, patch++) { realpatch = (patch_t*)W_CacheLumpNum (patch->patch, PU_CACHE_SHARED); 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 ; xwidth ; x++) { if (!patchcount[x]) { I_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] = ::g->s_texturecompositesize[texnum]; if (::g->s_texturecompositesize[texnum] > 0x10000-texture->height) { I_Error ("R_GenerateLookup: texture %i is >64k", texnum); } ::g->s_texturecompositesize[texnum] += texture->height; } } } // // R_GetColumn // byte* R_GetColumn ( int tex, int col ) { int lump; int ofs; col &= ::g->s_texturewidthmask[tex]; lump = ::g->s_texturecolumnlump[tex][col]; ofs = ::g->s_texturecolumnofs[tex][col]; if (lump > 0) return (byte *)W_CacheLumpNum(lump,PU_CACHE_SHARED)+ofs; if (!::g->s_texturecomposite[tex]) R_GenerateComposite (tex); return ::g->s_texturecomposite[tex] + ofs; } // // R_InitTextures // Initializes the texture list // with the s_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 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 = (char*)W_CacheLumpName ("PNAMES", PU_CACHE_SHARED); nummappatches = LONG ( *((int *)names) ); name_p = names+4; std::vector patchlookup(nummappatches); for (i=0 ; is_numtextures == 0) { // 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 = (int*)W_CacheLumpName ("TEXTURE1", PU_CACHE_SHARED); // ALAN: LOADTIME numtextures1 = LONG(*maptex); maxoff = W_LumpLength (W_GetNumForName ("TEXTURE1")); directory = maptex+1; if (W_CheckNumForName ("TEXTURE2") != -1) { maptex2 = (int*)W_CacheLumpName ("TEXTURE2", PU_CACHE_SHARED); // ALAN: LOADTIME numtextures2 = LONG(*maptex2); maxoff2 = W_LumpLength (W_GetNumForName ("TEXTURE2")); } else { maptex2 = NULL; numtextures2 = 0; maxoff2 = 0; } ::g->s_numtextures = numtextures1 + numtextures2; ::g->s_textures = (texture_t**)DoomLib::Z_Malloc (::g->s_numtextures*sizeof(texture_t*), PU_STATIC_SHARED, 0); ::g->s_texturecolumnlump = (short**)DoomLib::Z_Malloc (::g->s_numtextures*sizeof(short*), PU_STATIC_SHARED, 0); ::g->s_texturecolumnofs = (unsigned short**)DoomLib::Z_Malloc (::g->s_numtextures*sizeof(unsigned short*), PU_STATIC_SHARED, 0); ::g->s_texturewidthmask = (int*)DoomLib::Z_Malloc (::g->s_numtextures*4, PU_STATIC_SHARED, 0); ::g->s_textureheight = (fixed_t*)DoomLib::Z_Malloc (::g->s_numtextures*4, PU_STATIC_SHARED, 0); ::g->s_texturecomposite = (byte**)DoomLib::Z_Malloc (::g->s_numtextures*sizeof(byte*), PU_STATIC_SHARED, 0); ::g->s_texturecompositesize = (int*)DoomLib::Z_Malloc (::g->s_numtextures*4, PU_STATIC_SHARED, 0); totalwidth = 0; // Really complex printing shit... temp1 = W_GetNumForName ("S_START"); // P_??????? temp2 = W_GetNumForName ("S_END") - 1; temp3 = ((temp2-temp1+63)/64) + ((::g->s_numtextures+63)/64); I_Printf("["); for (i = 0; i < temp3; i++) I_Printf(" "); I_Printf(" ]"); for (i = 0; i < temp3; i++) I_Printf("\x8"); I_Printf("\x8\x8\x8\x8\x8\x8\x8\x8\x8\x8"); for (i=0 ; i < ::g->s_numtextures ; i++, directory++) { if (!(i&63)) I_Printf ("."); if (i == numtextures1) { // Start looking in second texture file. maptex = maptex2; maxoff = maxoff2; directory = maptex+1; } offset = LONG(*directory); if (offset > maxoff) I_Error ("R_InitTextures: bad texture directory"); mtexture = (maptexture_t *) ( (byte *)maptex + offset); texture = ::g->s_textures[i] = (texture_t*)DoomLib::Z_Malloc (sizeof(texture_t) + sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1), PU_STATIC_SHARED, 0); texture->width = SHORT(mtexture->width); texture->height = SHORT(mtexture->height); texture->patchcount = SHORT(mtexture->patchcount); memcpy (texture->name, mtexture->name, sizeof(texture->name)); mpatch = &mtexture->patches[0]; patch = &texture->patches[0]; 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) { I_Error ("R_InitTextures: Missing patch in texture %s", texture->name); } } ::g->s_texturecolumnlump[i] = (short*)DoomLib::Z_Malloc (texture->width*2, PU_STATIC_SHARED,0); ::g->s_texturecolumnofs[i] = (unsigned short*)DoomLib::Z_Malloc (texture->width*2, PU_STATIC_SHARED,0); j = 1; while (j*2 <= texture->width) j<<=1; ::g->s_texturewidthmask[i] = j-1; ::g->s_textureheight[i] = texture->height<width; } Z_Free(maptex1); if (maptex2) Z_Free(maptex2); // Precalculate whatever possible. for (i=0 ; i < ::g->s_numtextures ; i++) R_GenerateLookup (i); } // ALAN: These animations are done globally -- can it be shared? // Create translation table for global animation. ::g->texturetranslation = (int*)DoomLib::Z_Malloc ((::g->s_numtextures+1)*4, PU_STATIC, 0); for (i=0 ; i < ::g->s_numtextures ; i++) ::g->texturetranslation[i] = i; } // // R_InitFlats // void R_InitFlats (void) { int i; ::g->firstflat = W_GetNumForName ("F_START") + 1; ::g->lastflat = W_GetNumForName ("F_END") - 1; ::g->numflats = ::g->lastflat - ::g->firstflat + 1; // Create translation table for global animation. ::g->flattranslation = (int*)DoomLib::Z_Malloc ((::g->numflats+1)*4, PU_STATIC, 0); for (i=0 ; i < ::g->numflats ; i++) ::g->flattranslation[i] = i; } // // R_InitSpriteLumps // Finds the width and hoffset of all ::g->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; ::g->firstspritelump = W_GetNumForName ("S_START") + 1; ::g->lastspritelump = W_GetNumForName ("S_END") - 1; ::g->numspritelumps = ::g->lastspritelump - ::g->firstspritelump + 1; ::g->spritewidth = (fixed_t*)DoomLib::Z_Malloc (::g->numspritelumps*4, PU_STATIC, 0); ::g->spriteoffset = (fixed_t*)DoomLib::Z_Malloc (::g->numspritelumps*4, PU_STATIC, 0); ::g->spritetopoffset = (fixed_t*)DoomLib::Z_Malloc (::g->numspritelumps*4, PU_STATIC, 0); for (i=0 ; i< ::g->numspritelumps ; i++) { if (!(i&63)) I_Printf ("."); patch = (patch_t*)W_CacheLumpNum (::g->firstspritelump+i, PU_CACHE_SHARED); ::g->spritewidth[i] = SHORT(patch->width)<spriteoffset[i] = SHORT(patch->leftoffset)<spritetopoffset[i] = SHORT(patch->topoffset)<colormaps = (lighttable_t*)DoomLib::Z_Malloc (length, PU_STATIC, 0); ::g->colormaps = (byte *)( ((intptr_t)::g->colormaps + 255)&~0xff); W_ReadLump (lump,::g->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 (); I_Printf ("\nInitTextures"); R_InitFlats (); I_Printf ("\nInitFlats"); R_InitSpriteLumps (); I_Printf ("\nInitSprites"); R_InitColormaps (); I_Printf ("\nInitColormaps"); } // // R_FlatNumForName // Retrieval, get a flat number for a flat name. // int R_FlatNumForName (const 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 - ::g->firstflat; } // // R_CheckTextureNumForName // Check whether texture is available. // Filter out NoTexture indicator. // int R_CheckTextureNumForName (const char *name) { int i; // "NoTexture" marker. if (name[0] == '-') return 0; for (i=0 ; i < ::g->s_numtextures ; i++) if ( !idStr::Icmpn( ::g->s_textures[i]->name, name, 8 ) ) return i; return -1; } // // R_TextureNumForName // Calls R_CheckTextureNumForName, // aborts with error message. // int R_TextureNumForName (const 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. // void R_PrecacheLevel (void) { int i; int j; int k; int lump; texture_t* texture; thinker_t* th; spriteframe_t* sf; if (::g->demoplayback) return; // Precache flats. std::vector flatpresent(::g->numflats, 0); for (i=0 ; i < ::g->numsectors ; i++) { flatpresent[::g->sectors[i].floorpic] = 1; flatpresent[::g->sectors[i].ceilingpic] = 1; } ::g->flatmemory = 0; for (i=0 ; i < ::g->numflats ; i++) { if (flatpresent[i]) { lump = ::g->firstflat + i; ::g->flatmemory += lumpinfo[lump].size; W_CacheLumpNum(lump, PU_CACHE_SHARED); } } // Precache textures. std::vector texturepresent(::g->s_numtextures, 0); for (i=0 ; i < ::g->numsides ; i++) { texturepresent[::g->sides[i].toptexture] = 1; texturepresent[::g->sides[i].midtexture] = 1; texturepresent[::g->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[::g->skytexture] = 1; ::g->texturememory = 0; for (i=0 ; i < ::g->s_numtextures ; i++) { if (!texturepresent[i]) continue; texture = ::g->s_textures[i]; for (j=0 ; jpatchcount ; j++) { lump = texture->patches[j].patch; ::g->texturememory += lumpinfo[lump].size; W_CacheLumpNum(lump , PU_CACHE_SHARED); } } // Precache ::g->sprites. std::vector spritepresent(::g->numsprites, 0); for (th = ::g->thinkercap.next ; th != &::g->thinkercap ; th=th->next) { if (th->function.acp1 == (actionf_p1)P_MobjThinker) spritepresent[((mobj_t *)th)->sprite] = 1; } ::g->spritememory = 0; for (i=0 ; i < ::g->numsprites ; i++) { if (!spritepresent[i]) continue; for (j=0 ; j < ::g->sprites[i].numframes ; j++) { sf = &::g->sprites[i].spriteframes[j]; for (k=0 ; k<8 ; k++) { lump = ::g->firstspritelump + sf->lump[k]; ::g->spritememory += lumpinfo[lump].size; W_CacheLumpNum(lump , PU_CACHE_SHARED); } } } }