// // 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; int* patchlookup; int totalwidth; int nummappatches; int offset; int maxoff; int maxoff2; int numtextures1; int numtextures2; int* directory; int errors = 0; // Load the patch names from pnames.lmp. { char *names = (char *)W_CacheLumpName ("PNAMES", PU_STATIC); char *name_p = names+4; nummappatches = LELONG ( *((int *)names) ); patchlookup = new int[nummappatches]; for (i = 0; i < nummappatches; i++) { patchlookup[i] = W_CheckNumForName (name_p + i*8); if (patchlookup[i] == -1) { // killough 4/17/98: // Some wads use sprites as wall patches, so repeat check and // look for sprites this time, but only if there were no wall // patches found. This is the same as allowing for both, except // that wall patches always win over sprites, even when they // appear first in a wad. This is a kludgy solution to the wad // lump namespace problem. patchlookup[i] = W_CheckNumForName (name_p + i*8, ns_sprites); } } 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 = (int *)W_CacheLumpName ("TEXTURE1", PU_STATIC); numtextures1 = LELONG(*maptex); maxoff = W_LumpLength (W_GetNumForName ("TEXTURE1")); directory = maptex+1; if (W_CheckNumForName ("TEXTURE2") != -1) { maptex2 = (int *)W_CacheLumpName ("TEXTURE2", PU_STATIC); numtextures2 = LELONG(*maptex2); maxoff2 = W_LumpLength (W_GetNumForName ("TEXTURE2")); } else { maptex2 = NULL; numtextures2 = 0; maxoff2 = 0; } // denis - fix memory leaks for (i = 0; i < numtextures; i++) { delete[] texturecolumnlump[i]; delete[] texturecolumnofs[i]; } // denis - fix memory leaks delete[] textures; delete[] texturecolumnlump; delete[] texturecolumnofs; delete[] texturecomposite; delete[] texturecompositesize; delete[] texturewidthmask; delete[] textureheight; delete[] texturescalex; delete[] texturescaley; numtextures = numtextures1 + numtextures2; textures = new texture_t *[numtextures]; texturecolumnlump = new short *[numtextures]; texturecolumnofs = new unsigned int *[numtextures]; texturecomposite = new byte *[numtextures]; texturecompositesize = new int[numtextures]; texturewidthmask = new int[numtextures]; textureheight = new fixed_t[numtextures]; texturescalex = new fixed_t[numtextures]; texturescaley = new fixed_t[numtextures]; totalwidth = 0; for (i = 0; i < numtextures; i++, directory++) { if (i == numtextures1) { // Start looking in second texture file. maptex = maptex2; maxoff = maxoff2; directory = maptex+1; } offset = LELONG(*directory); if (offset > maxoff) I_FatalError ("R_InitTextures: bad texture directory"); mtexture = (maptexture_t *) ( (byte *)maptex + offset); texture = textures[i] = (texture_t *) Z_Malloc (sizeof(texture_t) + sizeof(texpatch_t)*(SAFESHORT(mtexture->patchcount)-1), PU_STATIC, 0); texture->width = SAFESHORT(mtexture->width); texture->height = SAFESHORT(mtexture->height); texture->patchcount = SAFESHORT(mtexture->patchcount); strncpy (texture->name, mtexture->name, 9); // denis - todo string limit? std::transform(texture->name, texture->name + strlen(texture->name), texture->name, toupper); mpatch = &mtexture->patches[0]; patch = &texture->patches[0]; for (j=0 ; j<texture->patchcount ; j++, mpatch++, patch++) { patch->originx = LESHORT(mpatch->originx); patch->originy = LESHORT(mpatch->originy); patch->patch = patchlookup[LESHORT(mpatch->patch)]; if (patch->patch == -1) { Printf (PRINT_HIGH, "R_InitTextures: Missing patch in texture %s\n", texture->name); errors++; } } texturecolumnlump[i] = new short[texture->width]; texturecolumnofs[i] = new unsigned int[texture->width]; for (j = 1; j*2 <= texture->width; j <<= 1) ; texturewidthmask[i] = j-1; textureheight[i] = texture->height << FRACBITS; // [RH] Special for beta 29: Values of 0 will use the tx/ty cvars // to determine scaling instead of defaulting to 8. I will likely // remove this once I finish the betas, because by then, users // should be able to actually create scaled textures. texturescalex[i] = mtexture->scalex ? mtexture->scalex << (FRACBITS - 3) : FRACUNIT; texturescaley[i] = mtexture->scaley ? mtexture->scaley << (FRACBITS - 3) : FRACUNIT; totalwidth += texture->width; } delete[] patchlookup; Z_Free (maptex1); if (maptex2) Z_Free (maptex2); if (errors) I_FatalError ("%d errors in R_InitTextures.", errors); // [RH] Setup hash chains. Go from back to front so that if // duplicates are found, the first one gets used instead // of the last (thus mimicing the original behavior // of R_CheckTextureNumForName(). for (i = 0; i < numtextures; i++) textures[i]->index = -1; for (i = numtextures - 1; i >= 0; i--) { j = 0; //W_LumpNameHash (textures[i]->name) % (unsigned) numtextures; textures[i]->next = textures[j]->index; textures[j]->index = i; } if (clientside) // server doesn't need to load patches ever { // Precalculate whatever possible. for (i = 0; i < numtextures; i++) R_GenerateLookup (i, &errors); } // if (errors) // I_FatalError ("%d errors encountered during texture generation.", errors); // Create translation table for global animation. delete[] texturetranslation; texturetranslation = new int[numtextures+1]; for (i = 0; i < numtextures; i++) texturetranslation[i] = i; }
void FTextureManager::AddTexturesLump (const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1) { FPatchLookup *patchlookup = NULL; int i; uint32_t numpatches; if (firstdup == 0) { firstdup = (int)Textures.Size(); } { auto pnames = Wads.OpenLumpReader(patcheslump); numpatches = pnames.ReadUInt32(); // Check whether the amount of names reported is correct. if ((signed)numpatches < 0) { Printf("Corrupt PNAMES lump found (negative amount of entries reported)\n"); return; } // Check whether the amount of names reported is correct. int lumplength = Wads.LumpLength(patcheslump); if (numpatches > uint32_t((lumplength-4)/8)) { Printf("PNAMES lump is shorter than required (%u entries reported but only %d bytes (%d entries) long\n", numpatches, lumplength, (lumplength-4)/8); // Truncate but continue reading. Who knows how many such lumps exist? numpatches = (lumplength-4)/8; } // Catalog the patches these textures use so we know which // textures they represent. patchlookup = new FPatchLookup[numpatches]; for (uint32_t i = 0; i < numpatches; ++i) { char pname[9]; pnames.Read(pname, 8); pname[8] = '\0'; patchlookup[i].Name = pname; } } bool isStrife = false; const uint32_t *maptex, *directory; uint32_t maxoff; int numtextures; uint32_t offset = 0; // Shut up, GCC! maptex = (const uint32_t *)lumpdata; numtextures = LittleLong(*maptex); maxoff = lumpsize; if (maxoff < uint32_t(numtextures+1)*4) { Printf ("Texture directory is too short\n"); delete[] patchlookup; return; } // Scan the texture lump to decide if it contains Doom or Strife textures for (i = 0, directory = maptex+1; i < numtextures; ++i) { offset = LittleLong(directory[i]); if (offset > maxoff) { Printf ("Bad texture directory\n"); delete[] patchlookup; return; } maptexture_t *tex = (maptexture_t *)((uint8_t *)maptex + offset); // There is bizzarely a Doom editing tool that writes to the // first two elements of columndirectory, so I can't check those. if (SAFESHORT(tex->patchcount) < 0 || tex->columndirectory[2] != 0 || tex->columndirectory[3] != 0) { isStrife = true; break; } } // Textures defined earlier in the lump take precedence over those defined later, // but later TEXTUREx lumps take precedence over earlier ones. for (i = 1, directory = maptex; i <= numtextures; ++i) { if (i == 1 && texture1) { // The very first texture is just a dummy. Copy its dimensions to texture 0. // It still needs to be created in case someone uses it by name. offset = LittleLong(directory[1]); const maptexture_t *tex = (const maptexture_t *)((const uint8_t *)maptex + offset); FDummyTexture *tex0 = static_cast<FDummyTexture *>(Textures[0].Texture); tex0->SetSize (SAFESHORT(tex->width), SAFESHORT(tex->height)); } offset = LittleLong(directory[i]); if (offset > maxoff) { Printf ("Bad texture directory\n"); delete[] patchlookup; return; } // If this texture was defined already in this lump, skip it // This could cause problems with animations that use the same name for intermediate // textures. Should I be worried? int j; for (j = (int)Textures.Size() - 1; j >= firstdup; --j) { if (strnicmp (Textures[j].Texture->Name, (const char *)maptex + offset, 8) == 0) break; } if (j + 1 == firstdup) { FMultiPatchTexture *tex = new FMultiPatchTexture ((const uint8_t *)maptex + offset, patchlookup, numpatches, isStrife, deflumpnum); if (i == 1 && texture1) { tex->UseType = ETextureType::FirstDefined; } TexMan.AddTexture (tex); StartScreen->Progress(); } } delete[] patchlookup; }
FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum) : Pixels (0), Spans(0), Parts(nullptr), Inits(nullptr), bRedirect(false), bTranslucentPatches(false) { union { const maptexture_t *d; const strifemaptexture_t *s; } mtexture; union { const mappatch_t *d; const strifemappatch_t *s; } mpatch; int i; mtexture.d = (const maptexture_t *)texdef; bMultiPatch = true; if (strife) { NumParts = SAFESHORT(mtexture.s->patchcount); } else { NumParts = SAFESHORT(mtexture.d->patchcount); } if (NumParts < 0) { I_FatalError ("Bad texture directory"); } UseType = ETextureType::Wall; Parts = NumParts > 0 ? new TexPart[NumParts] : nullptr; Inits = NumParts > 0 ? new TexInit[NumParts] : nullptr; Width = SAFESHORT(mtexture.d->width); Height = SAFESHORT(mtexture.d->height); Name = (char *)mtexture.d->name; CalcBitSize (); Scale.X = mtexture.d->ScaleX ? mtexture.d->ScaleX / 8. : 1.; Scale.Y = mtexture.d->ScaleY ? mtexture.d->ScaleY / 8. : 1.; if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) { bWorldPanning = true; } if (strife) { mpatch.s = &mtexture.s->patches[0]; } else { mpatch.d = &mtexture.d->patches[0]; } for (i = 0; i < NumParts; ++i) { if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) { I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", maxpatchnum, Name.GetChars(), LittleShort(mpatch.d->patch)+1); } Parts[i].OriginX = LittleShort(mpatch.d->originx); Parts[i].OriginY = LittleShort(mpatch.d->originy); Parts[i].Texture = nullptr; Inits[i].TexName = patchlookup[LittleShort(mpatch.d->patch)].Name; Inits[i].UseType = ETextureType::WallPatch; if (strife) mpatch.s++; else mpatch.d++; } if (NumParts == 0) { Printf ("Texture %s is left without any patches\n", Name.GetChars()); } DefinitionLump = deflumpnum; }
FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum) : Pixels (0), Spans(0), Parts(0), bRedirect(false), bTranslucentPatches(false) { union { const maptexture_t *d; const strifemaptexture_t *s; } mtexture; union { const mappatch_t *d; const strifemappatch_t *s; } mpatch; int i; mtexture.d = (const maptexture_t *)texdef; bMultiPatch = true; if (strife) { NumParts = SAFESHORT(mtexture.s->patchcount); } else { NumParts = SAFESHORT(mtexture.d->patchcount); } if (NumParts <= 0) { I_FatalError ("Bad texture directory"); } UseType = FTexture::TEX_Wall; Parts = new TexPart[NumParts]; Width = SAFESHORT(mtexture.d->width); Height = SAFESHORT(mtexture.d->height); strncpy (Name, (const char *)mtexture.d->name, 8); Name[8] = 0; CalcBitSize (); xScale = mtexture.d->ScaleX ? mtexture.d->ScaleX*(FRACUNIT/8) : FRACUNIT; yScale = mtexture.d->ScaleY ? mtexture.d->ScaleY*(FRACUNIT/8) : FRACUNIT; if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) { bWorldPanning = true; } if (strife) { mpatch.s = &mtexture.s->patches[0]; } else { mpatch.d = &mtexture.d->patches[0]; } for (i = 0; i < NumParts; ++i) { if (unsigned(LittleShort(mpatch.d->patch)) >= unsigned(maxpatchnum)) { I_FatalError ("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", maxpatchnum, Name, LittleShort(mpatch.d->patch)+1); } Parts[i].OriginX = LittleShort(mpatch.d->originx); Parts[i].OriginY = LittleShort(mpatch.d->originy); Parts[i].Texture = patchlookup[LittleShort(mpatch.d->patch)].Texture; if (Parts[i].Texture == NULL) { Printf ("Unknown patch %s in texture %s\n", patchlookup[LittleShort(mpatch.d->patch)].Name, Name); NumParts--; i--; } if (strife) mpatch.s++; else mpatch.d++; } if (NumParts == 0) { Printf ("Texture %s is left without any patches\n", Name); } CheckForHacks (); // If this texture is just a wrapper around a single patch, we can simply // forward GetPixels() and GetColumn() calls to that patch. if (NumParts == 1) { if (Parts->OriginX == 0 && Parts->OriginY == 0 && Parts->Texture->GetWidth() == Width && Parts->Texture->GetHeight() == Height) { bRedirect = true; } } DefinitionLump = deflumpnum; }
// // TextureManager::addTextureDirectory // // Requires that the PNAMES lump has been read and processed. // void TextureManager::addTextureDirectory(const char* lumpname) { // // 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. // struct mappatch_t { short originx; short originy; short patch; short stepdir; short colormap; }; // // Texture definition. // A DOOM wall texture is a list of patches // which are to be combined in a predefined order. // struct maptexture_t { char name[8]; WORD masked; // [RH] Unused BYTE scalex; // [RH] Scaling (8 is normal) BYTE scaley; // [RH] Same as above short width; short height; byte columndirectory[4]; // OBSOLETE short patchcount; mappatch_t patches[1]; }; int lumpnum = W_CheckNumForName(lumpname); if (lumpnum == -1) { if (iequals("TEXTURE1", lumpname)) I_Error("R_InitTextures: TEXTURE1 lump not found"); return; } size_t lumplen = W_LumpLength(lumpnum); if (lumplen == 0) return; byte* lumpdata = new byte[lumplen]; W_ReadLump(lumpnum, lumpdata); int* texoffs = (int*)(lumpdata + 4); // keep track of the number of texture errors int errors = 0; int count = LELONG(*((int*)(lumpdata + 0))); for (int i = 0; i < count; i++) { maptexture_t* mtexdef = (maptexture_t*)((byte*)lumpdata + LELONG(texoffs[i])); size_t texdefsize = sizeof(texdef_t) + sizeof(texdefpatch_t) * (SAFESHORT(mtexdef->patchcount) - 1); texdef_t* texdef = (texdef_t*)(new byte[texdefsize]); texdef->width = SAFESHORT(mtexdef->width); texdef->height = SAFESHORT(mtexdef->height); texdef->patchcount = SAFESHORT(mtexdef->patchcount); texdef->scalex = mtexdef->scalex; texdef->scaley = mtexdef->scaley; char uname[9]; for (int c = 0; c < 8; c++) uname[c] = toupper(mtexdef->name[c]); uname[8] = 0; mappatch_t* mpatch = &mtexdef->patches[0]; texdefpatch_t* patch = &texdef->patches[0]; for (int j = 0; j < texdef->patchcount; j++, mpatch++, patch++) { patch->originx = LESHORT(mpatch->originx); patch->originy = LESHORT(mpatch->originy); patch->patch = mPNameLookup[LESHORT(mpatch->patch)]; if (patch->patch == -1) { Printf(PRINT_HIGH, "R_InitTextures: Missing patch in texture %s\n", uname); errors++; } } mTextureDefinitions.push_back(texdef); mTextureNameTranslationMap[uname] = mTextureDefinitions.size() - 1; } delete [] lumpdata; }
void R_InitSkyMap () { texpatch_t *texpatch; patch_t *wpatch; int p_height, t_height,i,count; if (textureheight == NULL) return; // [SL] 2011-11-30 - Don't run if we don't know what sky texture to use if (gamestate != GS_LEVEL) return; if (sky2texture && textureheight[sky1texture] != textureheight[sky2texture]) { Printf (PRINT_HIGH,"\x1f+Both sky textures must be the same height.\x1f-\n"); sky2texture = sky1texture; } t_height = textures[sky1texture]->height; p_height = 0; count = textures[sky1texture]->patchcount; texpatch = &(textures[sky1texture]->patches[0]); // Find the tallest patch in the texture for(i = 0; i < count; i++, texpatch++) { wpatch = W_CachePatch(texpatch->patch); if(wpatch->height() > p_height) p_height = SAFESHORT(wpatch->height()); } textures[sky1texture]->height = MAX(t_height,p_height); textureheight[sky1texture] = textures[sky1texture]->height << FRACBITS; skystretch = 0; if (textureheight[sky1texture] <= (128 << FRACBITS)) { skytexturemid = 200/2*FRACUNIT; skystretch = (r_stretchsky == 1) || (r_stretchsky == 2 && sv_freelook && cl_mouselook); } else { skytexturemid = 199 * FRACUNIT; } if (viewwidth && viewheight) { skyiscale = (200*FRACUNIT) / (((freelookviewheight<<detailxshift) * viewwidth) / (viewwidth<<detailxshift)); skyscale = ((((freelookviewheight<<detailxshift) * viewwidth) / (viewwidth<<detailxshift)) << FRACBITS) /(200); skyiscale = FixedMul (skyiscale, FixedDiv (FieldOfView, 2048)); skyscale = FixedMul (skyscale, FixedDiv (2048, FieldOfView)); } // The DOOM sky map is 256*128*4 maps. // The Heretic sky map is 256*200*4 maps. sky1shift = 22+skystretch-16; sky2shift = 22+skystretch-16; if (texturewidthmask[sky1texture] >= 127) sky1shift -= skystretch; if (texturewidthmask[sky2texture] >= 127) sky2shift -= skystretch; }