From 4cedbf6cc28108c1bf89abf5c31d057916af74de Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 9 Dec 2018 15:25:56 +0100 Subject: [PATCH] - split between textures and images is complete now. * split up FMultiPatchTexture into a builder class and the actual image source. * since images can now be referenced by multiple textures the old redirection mechanism has been removed. It can be done better and less intrusive now. Simple single patch textures already directly reference the underlying patch image now. * allocate all image source related data from a memory arena. Since this is all static this makes it a lot easier to free this in bulk. --- src/CMakeLists.txt | 1 + src/hwrenderer/textures/hw_material.cpp | 8 - src/swrenderer/textures/r_swtexture.cpp | 1 - src/swrenderer/textures/r_swtexture.h | 1 - src/swrenderer/things/r_playersprite.cpp | 2 + src/textures/formats/ddstexture.cpp | 11 - src/textures/formats/multipatchtexture.cpp | 1119 ++------------------ src/textures/image.cpp | 2 +- src/textures/image.h | 18 +- src/textures/imagetexture.cpp | 34 +- src/textures/multipatchtexturebuilder.cpp | 975 +++++++++++++++++ src/textures/skyboxtexture.cpp | 11 - src/textures/skyboxtexture.h | 9 +- src/textures/texture.cpp | 66 +- src/textures/texturemanager.cpp | 43 +- src/textures/textures.h | 20 +- 16 files changed, 1137 insertions(+), 1184 deletions(-) create mode 100644 src/textures/multipatchtexturebuilder.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7d6b7f4eb..98d220e9d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1101,6 +1101,7 @@ set (PCH_SOURCES textures/image.cpp textures/imagetexture.cpp textures/texturemanager.cpp + textures/multipatchtexturebuilder.cpp textures/skyboxtexture.cpp textures/formats/automaptexture.cpp textures/formats/brightmaptexture.cpp diff --git a/src/hwrenderer/textures/hw_material.cpp b/src/hwrenderer/textures/hw_material.cpp index ab833a715..712a8e7af 100644 --- a/src/hwrenderer/textures/hw_material.cpp +++ b/src/hwrenderer/textures/hw_material.cpp @@ -237,14 +237,6 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) mSpriteU[0] = mSpriteV[0] = 0.f; mSpriteU[1] = mSpriteV[1] = 1.f; - FTexture *basetex = tx->GetRedirect(); - // allow the redirect only if the texture is not expanded or the scale matches. - if (!expanded || (tx->Scale.X == basetex->Scale.X && tx->Scale.Y == basetex->Scale.Y)) - { - sourcetex = basetex; - mBaseLayer = ValidateSysTexture(basetex, expanded); - } - mExpanded = expanded; if (expanded) { diff --git a/src/swrenderer/textures/r_swtexture.cpp b/src/swrenderer/textures/r_swtexture.cpp index ae8c0d1ce..5e3721fe9 100644 --- a/src/swrenderer/textures/r_swtexture.cpp +++ b/src/swrenderer/textures/r_swtexture.cpp @@ -44,7 +44,6 @@ FSoftwareTexture *FTexture::GetSoftwareTexture() if (!SoftwareTexture) { if (bWarped) SoftwareTexture = new FWarpTexture(this, bWarped); - // else if (GetRedirect() != this) ... must be decided later. The current data structures make it hard to do this without creating a mess. else SoftwareTexture = new FSoftwareTexture(this); } return SoftwareTexture; diff --git a/src/swrenderer/textures/r_swtexture.h b/src/swrenderer/textures/r_swtexture.h index af65003bf..3dd534624 100644 --- a/src/swrenderer/textures/r_swtexture.h +++ b/src/swrenderer/textures/r_swtexture.h @@ -55,7 +55,6 @@ public: return mTexture->bMasked; } - bool UseBasePalette() const { return mTexture->UseBasePalette(); } int GetSkyOffset() const { return mTexture->GetSkyOffset(); } PalEntry GetSkyCapColor(bool bottom) const { return mTexture->GetSkyCapColor(bottom); } diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp index 548bb5c8a..ac99bee93 100644 --- a/src/swrenderer/things/r_playersprite.cpp +++ b/src/swrenderer/things/r_playersprite.cpp @@ -385,6 +385,7 @@ namespace swrenderer { noaccel = true; } +#if 0 // If the main colormap has fixed lights, and this sprite is being drawn with that // colormap, disable acceleration so that the lights can remain fixed. CameraLight *cameraLight = CameraLight::Instance(); @@ -394,6 +395,7 @@ namespace swrenderer { noaccel = true; } +#endif } else { diff --git a/src/textures/formats/ddstexture.cpp b/src/textures/formats/ddstexture.cpp index 2672d7b2d..31ec4f01f 100644 --- a/src/textures/formats/ddstexture.cpp +++ b/src/textures/formats/ddstexture.cpp @@ -184,7 +184,6 @@ protected: void DecompressDXT5 (FileReader &lump, bool premultiplied, uint8_t *buffer, int pixelmode); int CopyPixels(FBitmap *bmp, int conversion) override; - bool UseBasePalette(); friend class FTexture; }; @@ -809,13 +808,3 @@ int FDDSTexture::CopyPixels(FBitmap *bmp, int conversion) return -1; } - -//=========================================================================== -// -// -//=========================================================================== - -bool FDDSTexture::UseBasePalette() -{ - return false; -} diff --git a/src/textures/formats/multipatchtexture.cpp b/src/textures/formats/multipatchtexture.cpp index 42a9d7802..cd50d555c 100644 --- a/src/textures/formats/multipatchtexture.cpp +++ b/src/textures/formats/multipatchtexture.cpp @@ -50,161 +50,8 @@ #include "v_text.h" #include "cmdlib.h" #include "imagehelpers.h" - -// On the Alpha, accessing the shorts directly if they aren't aligned on a -// 4-byte boundary causes unaligned access warnings. Why it does this at -// all and only while initing the textures is beyond me. - -#ifdef ALPHA -#define SAFESHORT(s) ((short)(((uint8_t *)&(s))[0] + ((uint8_t *)&(s))[1] * 256)) -#else -#define SAFESHORT(s) LittleShort(s) -#endif - - - -//-------------------------------------------------------------------------- -// -// Data structures for the TEXTUREx lumps -// -//-------------------------------------------------------------------------- - -// -// 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 -{ - int16_t originx; - int16_t originy; - int16_t patch; - int16_t stepdir; - int16_t colormap; -}; - -// -// A wall texture is a list of patches which are to be combined in a -// predefined order. -// -struct maptexture_t -{ - uint8_t name[8]; - uint16_t Flags; // [RH] Was unused - uint8_t ScaleX; // [RH] Scaling (8 is normal) - uint8_t ScaleY; // [RH] Same as above - int16_t width; - int16_t height; - uint8_t columndirectory[4]; // OBSOLETE - int16_t patchcount; - mappatch_t patches[1]; -}; - -#define MAPTEXF_WORLDPANNING 0x8000 - -// Strife uses versions of the above structures that remove all unused fields - -struct strifemappatch_t -{ - int16_t originx; - int16_t originy; - int16_t patch; -}; - -// -// A wall texture is a list of patches which are to be combined in a -// predefined order. -// -struct strifemaptexture_t -{ - uint8_t name[8]; - uint16_t Flags; // [RH] Was unused - uint8_t ScaleX; // [RH] Scaling (8 is normal) - uint8_t ScaleY; // [RH] Same as above - int16_t width; - int16_t height; - int16_t patchcount; - strifemappatch_t patches[1]; -}; - - -//========================================================================== -// -// In-memory representation of a single PNAMES lump entry -// -//========================================================================== - -struct FPatchLookup -{ - FString Name; -}; - - -//========================================================================== -// -// A texture defined in a TEXTURE1 or TEXTURE2 lump -// -//========================================================================== - -class FMultiPatchTexture : public FTexture -{ -public: - FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflump); - FMultiPatchTexture (FScanner &sc, ETextureType usetype); - ~FMultiPatchTexture (); - - bool UseBasePalette() override; - virtual void SetFrontSkyLayer () override; - - int CopyPixels(FBitmap *bmp) override; - int GetSourceLump() override { return DefinitionLump; } - FTexture *GetRedirect() override; - FTexture *GetRawTexture() override; - void ResolvePatches(); - -protected: - uint8_t *Pixels; - //FSoftwareTextureSpan **Spans; - int DefinitionLump; - - struct TexPart - { - FRemapTable *Translation = nullptr; - FTexture *Texture = nullptr; - PalEntry Blend = 0; - blend_t Alpha = FRACUNIT; - int16_t OriginX = 0; - int16_t OriginY = 0; - uint8_t Rotate = 0; - uint8_t op = OP_COPY; - }; - - struct TexInit - { - FString TexName; - ETextureType UseType = ETextureType::Null; - bool Silent = false; - bool HasLine = false; - bool UseOffsets = false; - FScriptPosition sc; - }; - - int NumParts; - TexPart *Parts; - TexInit *Inits; - bool bRedirect; - bool bTranslucentPatches; - - TArray MakeTexture (bool alphatex); - - // The getters must optionally redirect if it's a simple one-patch texture. - TArray Get8BitPixels(bool alphatex) override { return bRedirect ? Parts->Texture->Get8BitPixels(alphatex) : MakeTexture(alphatex); } - - -private: - void CheckForHacks (); - void ParsePatch(FScanner &sc, TexPart & part, TexInit &init); -}; +#include "image.h" +#include "multipatchtexture.h" //========================================================================== // @@ -212,128 +59,25 @@ private: // //========================================================================== -FMultiPatchTexture::FMultiPatchTexture (const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum) -: Pixels (0), Parts(nullptr), Inits(nullptr), bRedirect(false), bTranslucentPatches(false) +FMultiPatchTexture::FMultiPatchTexture(int w, int h, const TArray &parts, bool complex, bool textual) { - union + Width = w; + Height = h; + bComplex = complex; + bTextual = textual; + Parts = (TexPart*)ImageArena.Alloc(sizeof(TexPart) * parts.Size()); + NumParts = parts.Size(); + memcpy(Parts, parts.Data(), sizeof(TexPart) * parts.Size()); + + bUseGamePalette = false; + if (!bComplex) { - 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 = 1; - - 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; - - 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)) + for (int i = 0; i < NumParts; i++) { - 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); + if (!Parts[i].Image->UseGamePalette()) return; } - 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++; + bUseGamePalette = true; // only if all patches use the game palette. } - if (NumParts == 0) - { - Printf ("Texture %s is left without any patches\n", Name.GetChars()); - } - - DefinitionLump = deflumpnum; -} - -//========================================================================== -// -// FMultiPatchTexture :: ~FMultiPatchTexture -// -//========================================================================== - -FMultiPatchTexture::~FMultiPatchTexture () -{ - if (Parts != NULL) - { - for(int i=0; iSetFrontSkyLayer (); - } - bNoRemap0 = true; } //========================================================================== @@ -342,7 +86,7 @@ void FMultiPatchTexture::SetFrontSkyLayer () // //========================================================================== -uint8_t *GetBlendMap(PalEntry blend, uint8_t *blendwork) +static uint8_t *GetBlendMap(PalEntry blend, uint8_t *blendwork) { switch (blend.a==0 ? int(blend) : -1) { @@ -387,7 +131,55 @@ uint8_t *GetBlendMap(PalEntry blend, uint8_t *blendwork) } } } - return NULL; + return nullptr; +} + +//========================================================================== +// +// +// +//========================================================================== + +void FMultiPatchTexture::CopyToBlock(uint8_t *dest, int dwidth, int dheight, FImageSource *source, int xpos, int ypos, int rotate, const uint8_t *translation, int style) +{ + auto image = source->GetPalettedPixels(style); // should use composition cache + const uint8_t *pixels = image.Data(); + int srcwidth = source->GetWidth(); + int srcheight = source->GetHeight(); + int step_x = source->GetHeight(); + int step_y = 1; + FClipRect cr = { 0, 0, dwidth, dheight }; + if (style) translation = nullptr; // do not apply translations to alpha textures. + + if (ClipCopyPixelRect(&cr, xpos, ypos, pixels, srcwidth, srcheight, step_x, step_y, rotate)) + { + dest += ypos + dheight * xpos; + if (translation == NULL) + { + for (int x = 0; x < srcwidth; x++) + { + int pos = x * dheight; + for (int y = 0; y < srcheight; y++, pos++) + { + // the optimizer is doing a good enough job here so there's no need to optimize this by hand + uint8_t v = pixels[y * step_y + x * step_x]; + if (v != 0) dest[pos] = v; + } + } + } + else + { + for (int x = 0; x < srcwidth; x++) + { + int pos = x * dheight; + for (int y = 0; y < srcheight; y++, pos++) + { + uint8_t v = pixels[y * step_y + x * step_x]; + if (v != 0) dest[pos] = translation[v]; + } + } + } + } } //========================================================================== @@ -396,7 +188,7 @@ uint8_t *GetBlendMap(PalEntry blend, uint8_t *blendwork) // //========================================================================== -TArray FMultiPatchTexture::MakeTexture (bool alphatex) +TArray FMultiPatchTexture::GetPalettedPixels(int conversion) { int numpix = Width * Height; uint8_t blendwork[256]; @@ -405,9 +197,10 @@ TArray FMultiPatchTexture::MakeTexture (bool alphatex) TArray Pixels(numpix, true); memset (Pixels.Data(), 0, numpix); - if (alphatex) + if (conversion == luminance) { - buildrgb = !UseBasePalette(); + // For alpha textures, downconversion to the palette would lose too much precision if not all patches use the palette. + buildrgb = !UseGamePalette(); } else { @@ -420,20 +213,27 @@ TArray FMultiPatchTexture::MakeTexture (bool alphatex) } } } + if (conversion == noremap0) + { + // sky remapping will only happen if + // - the texture was defined through a TEXTUREx lump (this implies only trivial copies) + // - all patches use the base palette. + // All other cases would not be able to properly deal with this special case. + // For textual definitions this hack isn't necessary. + if (bTextual || !UseGamePalette()) conversion = normal; + } if (!buildrgb) { for (int i = 0; i < NumParts; ++i) { - if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. - uint8_t *trans = Parts[i].Translation? Parts[i].Translation->Remap : nullptr; { if (Parts[i].Blend != 0) { trans = GetBlendMap(Parts[i].Blend, blendwork); } - Parts[i].Texture->CopyToBlock (Pixels.Data(), Width, Height, Parts[i].OriginX, Parts[i].OriginY, Parts[i].Rotate, trans, alphatex); + CopyToBlock (Pixels.Data(), Width, Height, Parts[i].Image, Parts[i].OriginX, Parts[i].OriginY, Parts[i].Rotate, trans, conversion); } } } @@ -441,16 +241,18 @@ TArray FMultiPatchTexture::MakeTexture (bool alphatex) { // In case there are translucent patches let's do the composition in // True color to keep as much precision as possible before downconverting to the palette. - auto buffer = GetBgraBitmap(nullptr); + FBitmap PixelsIn; + PixelsIn.Create(Width, Height); + CopyPixels(&PixelsIn, normal); for(int y = 0; y < Height; y++) { - uint8_t *in = buffer.GetPixels() + Width * y * 4; + uint8_t *in = PixelsIn.GetPixels() + Width * y * 4; uint8_t *out = Pixels.Data() + y; for (int x = 0; x < Width; x++) { if (*out == 0 && in[3] != 0) { - *out = ImageHelpers::RGBToPalette(alphatex, in[2], in[1], in[0]); + *out = ImageHelpers::RGBToPalette(conversion == luminance, in[2], in[1], in[0]); } out += Height; in += 4; @@ -468,13 +270,13 @@ TArray FMultiPatchTexture::MakeTexture (bool alphatex) // //=========================================================================== -int FMultiPatchTexture::CopyPixels(FBitmap *bmp) +int FMultiPatchTexture::CopyPixels(FBitmap *bmp, int conversion) { int retv = -1; - if (bRedirect) - { // Redirect straight to the real texture's routine. - return Parts[0].Texture->CopyPixels(bmp); + if (conversion == noremap0) + { + if (bTextual || !UseGamePalette()) conversion = normal; } for(int i = 0; i < NumParts; i++) @@ -482,8 +284,6 @@ int FMultiPatchTexture::CopyPixels(FBitmap *bmp) int ret = -1; FCopyInfo info; - if (Parts[i].Texture->bHasCanvas) continue; // cannot use camera textures as patch. - memset (&info, 0, sizeof(info)); info.alpha = Parts[i].Alpha; info.invalpha = BLENDUNIT - info.alpha; @@ -513,7 +313,9 @@ int FMultiPatchTexture::CopyPixels(FBitmap *bmp) } } - auto Pixels = Parts[i].Texture->GetBgraBitmap(Parts[i].Translation ? Parts[i].Translation->Palette : nullptr, &ret); + FBitmap Pixels; + Pixels.Create(Width, Height); + ret = Parts[i].Image->CopyPixels(&Pixels, conversion); bmp->Blit(Parts[i].OriginX, Parts[i].OriginY, Pixels, &info); // treat -1 (i.e. unknown) as absolute. We have no idea if this may have overwritten previous info so a real check needs to be done. if (ret == -1) retv = ret; @@ -522,740 +324,3 @@ int FMultiPatchTexture::CopyPixels(FBitmap *bmp) return retv; } -//=========================================================================== -// -// FMultipatchTexture::UseBasePalette -// -// returns true if all patches in the texture use the unmodified base -// palette. -// -//=========================================================================== - -bool FMultiPatchTexture::UseBasePalette() -{ - if (bComplex) return false; - for(int i=0;iUseBasePalette()) return false; - } - return true; -} - -//========================================================================== -// -// FMultiPatchTexture :: CheckForHacks -// -//========================================================================== - -void FMultiPatchTexture::CheckForHacks () -{ - if (NumParts <= 0) - { - return; - } - - // Heretic sky textures are marked as only 128 pixels tall, - // even though they are really 200 pixels tall. - if (gameinfo.gametype == GAME_Heretic && - Name[0] == 'S' && - Name[1] == 'K' && - Name[2] == 'Y' && - Name[4] == 0 && - Name[3] >= '1' && - Name[3] <= '3' && - Height == 128) - { - Height = 200; - return; - } - - // The Doom E1 sky has its patch's y offset at -8 instead of 0. - if (gameinfo.gametype == GAME_Doom && - !(gameinfo.flags & GI_MAPxx) && - NumParts == 1 && - Height == 128 && - Parts->OriginY == -8 && - Name[0] == 'S' && - Name[1] == 'K' && - Name[2] == 'Y' && - Name[3] == '1' && - Name[4] == 0) - { - Parts->OriginY = 0; - return; - } - - // BIGDOOR7 in Doom also has patches at y offset -4 instead of 0. - if (gameinfo.gametype == GAME_Doom && - !(gameinfo.flags & GI_MAPxx) && - NumParts == 2 && - Height == 128 && - Parts[0].OriginY == -4 && - Parts[1].OriginY == -4 && - Name[0] == 'B' && - Name[1] == 'I' && - Name[2] == 'G' && - Name[3] == 'D' && - Name[4] == 'O' && - Name[5] == 'O' && - Name[6] == 'R' && - Name[7] == '7') - { - Parts[0].OriginY = 0; - Parts[1].OriginY = 0; - return; - } - - // [RH] Some wads (I forget which!) have single-patch textures 256 - // pixels tall that have patch lengths recorded as 0. I can't think of - // any good reason for them to do this, and since I didn't make note - // of which wad made me hack in support for them, the hack is gone - // because I've added support for DeePsea's true tall patches. - // - // Okay, I found a wad with crap patches: Pleiades.wad's sky patches almost - // fit this description and are a big mess, but they're not single patch! - if (Height == 256) - { - int i; - - // All patches must be at the top of the texture for this fix - for (i = 0; i < NumParts; ++i) - { - if (Parts[i].OriginY != 0) - { - break; - } - } - } -} - -//========================================================================== -// -// FMultiPatchTexture :: GetRedirect -// -//========================================================================== - -FTexture *FMultiPatchTexture::GetRedirect() -{ - return bRedirect ? Parts->Texture : this; -} - -//========================================================================== -// -// FMultiPatchTexture :: GetRawTexture -// -// Doom ignored all compositing of mid-sided textures on two-sided lines. -// Since these textures had to be single-patch in Doom, that essentially -// means it ignores their Y offsets. -// -// If this texture is composed of only one patch, return that patch. -// Otherwise, return this texture, since Doom wouldn't have been able to -// draw it anyway. -// -//========================================================================== - -FTexture *FMultiPatchTexture::GetRawTexture() -{ - return NumParts == 1 && UseType == ETextureType::Wall && bMultiPatch == 1 && Scale == Parts->Texture->Scale ? Parts->Texture : this; -} - -//========================================================================== -// -// FTextureManager :: AddTexturesLump -// -//========================================================================== - -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(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; -} - - -//========================================================================== -// -// FTextureManager :: AddTexturesLumps -// -//========================================================================== - -void FTextureManager::AddTexturesLumps (int lump1, int lump2, int patcheslump) -{ - int firstdup = (int)Textures.Size(); - - if (lump1 >= 0) - { - FMemLump texdir = Wads.ReadLump (lump1); - AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump1), lump1, patcheslump, firstdup, true); - } - if (lump2 >= 0) - { - FMemLump texdir = Wads.ReadLump (lump2); - AddTexturesLump (texdir.GetMem(), Wads.LumpLength (lump2), lump2, patcheslump, firstdup, false); - } -} - - -//========================================================================== -// -// -// -//========================================================================== - -void FMultiPatchTexture::ParsePatch(FScanner &sc, TexPart & part, TexInit &init) -{ - FString patchname; - int Mirror = 0; - sc.MustGetString(); - - init.TexName = sc.String; - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.OriginX = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.OriginY = sc.Number; - - if (sc.CheckString("{")) - { - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("flipx")) - { - Mirror |= 1; - } - else if (sc.Compare("flipy")) - { - Mirror |= 2; - } - else if (sc.Compare("rotate")) - { - sc.MustGetNumber(); - sc.Number = (((sc.Number + 90)%360)-90); - if (sc.Number != 0 && sc.Number !=90 && sc.Number != 180 && sc.Number != -90) - { - sc.ScriptError("Rotation must be a multiple of 90 degrees."); - } - part.Rotate = (sc.Number / 90) & 3; - } - else if (sc.Compare("Translation")) - { - int match; - - bComplex = true; - if (part.Translation != NULL) delete part.Translation; - part.Translation = NULL; - part.Blend = 0; - static const char *maps[] = { "inverse", "gold", "red", "green", "blue", NULL }; - sc.MustGetString(); - - match = sc.MatchString(maps); - if (match >= 0) - { - part.Blend = BLEND_SPECIALCOLORMAP1 + match; - } - else if (sc.Compare("ICE")) - { - part.Blend = BLEND_ICEMAP; - } - else if (sc.Compare("DESATURATE")) - { - sc.MustGetStringName(","); - sc.MustGetNumber(); - part.Blend = BLEND_DESATURATE1 + clamp(sc.Number-1, 0, 30); - } - else - { - sc.UnGet(); - part.Translation = new FRemapTable; - part.Translation->MakeIdentity(); - do - { - sc.MustGetString(); - part.Translation->AddToTranslation(sc.String); - } - while (sc.CheckString(",")); - } - - } - else if (sc.Compare("Colormap")) - { - float r1,g1,b1; - float r2,g2,b2; - - sc.MustGetFloat(); - r1 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - g1 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - b1 = (float)sc.Float; - if (!sc.CheckString(",")) - { - part.Blend = AddSpecialColormap(0,0,0, r1, g1, b1); - } - else - { - sc.MustGetFloat(); - r2 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - g2 = (float)sc.Float; - sc.MustGetStringName(","); - sc.MustGetFloat(); - b2 = (float)sc.Float; - part.Blend = AddSpecialColormap(r1, g1, b1, r2, g2, b2); - } - } - else if (sc.Compare("Blend")) - { - bComplex = true; - if (part.Translation != NULL) delete part.Translation; - part.Translation = NULL; - part.Blend = 0; - - if (!sc.CheckNumber()) - { - sc.MustGetString(); - part.Blend = V_GetColor(NULL, sc); - } - else - { - int r,g,b; - - r = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - g = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - b = sc.Number; - //sc.MustGetStringName(","); This was never supposed to be here. - part.Blend = MAKERGB(r, g, b); - } - // Blend.a may never be 0 here. - if (sc.CheckString(",")) - { - sc.MustGetFloat(); - if (sc.Float > 0.f) - part.Blend.a = clamp(int(sc.Float*255), 1, 254); - else - part.Blend = 0; - } - else part.Blend.a = 255; - } - else if (sc.Compare("alpha")) - { - sc.MustGetFloat(); - part.Alpha = clamp(int(sc.Float * BLENDUNIT), 0, BLENDUNIT); - // bComplex is not set because it is only needed when the style is not OP_COPY. - } - else if (sc.Compare("style")) - { - static const char *styles[] = {"copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay", NULL }; - sc.MustGetString(); - part.op = sc.MustMatchString(styles); - bComplex |= (part.op != OP_COPY); - bTranslucentPatches = bComplex; - } - else if (sc.Compare("useoffsets")) - { - init.UseOffsets = true; - } - } - } - if (Mirror & 2) - { - part.Rotate = (part.Rotate + 2) & 3; - Mirror ^= 1; - } - if (Mirror & 1) - { - part.Rotate |= 4; - } -} - -//========================================================================== -// -// Constructor for text based multipatch definitions -// -//========================================================================== - -FMultiPatchTexture::FMultiPatchTexture (FScanner &sc, ETextureType usetype) -: Pixels (0), Parts(0), bRedirect(false), bTranslucentPatches(false) -{ - TArray parts; - TArray inits; - bool bSilent = false; - - bMultiPatch = 2; - sc.SetCMode(true); - sc.MustGetString(); - const char* textureName = NULL; - if (sc.Compare("optional")) - { - bSilent = true; - sc.MustGetString(); - if (sc.Compare(",")) - { - // this is not right. Apparently a texture named 'optional' is being defined right now... - sc.UnGet(); - textureName = "optional"; - bSilent = false; - } - } - Name = !textureName ? sc.String : textureName; - Name.ToUpper(); - sc.MustGetStringName(","); - sc.MustGetNumber(); - Width = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - Height = sc.Number; - UseType = usetype; - - bool offset2set = false; - if (sc.CheckString("{")) - { - while (!sc.CheckString("}")) - { - sc.MustGetString(); - if (sc.Compare("XScale")) - { - sc.MustGetFloat(); - Scale.X = sc.Float; - if (Scale.X == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", Name.GetChars()); - } - else if (sc.Compare("YScale")) - { - sc.MustGetFloat(); - Scale.Y = sc.Float; - if (Scale.Y == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", Name.GetChars()); - } - else if (sc.Compare("WorldPanning")) - { - bWorldPanning = true; - } - else if (sc.Compare("NullTexture")) - { - UseType = ETextureType::Null; - } - else if (sc.Compare("NoDecals")) - { - bNoDecals = true; - } - else if (sc.Compare("Patch")) - { - TexPart part; - TexInit init; - ParsePatch(sc, part, init); - if (init.TexName.IsNotEmpty()) - { - parts.Push(part); - init.UseType = ETextureType::WallPatch; - init.Silent = bSilent; - init.HasLine = true; - init.sc = sc; - inits.Push(init); - } - part.Texture = NULL; - part.Translation = NULL; - } - else if (sc.Compare("Sprite")) - { - TexPart part; - TexInit init; - ParsePatch(sc, part, init); - if (init.TexName.IsNotEmpty()) - { - parts.Push(part); - init.UseType = ETextureType::Sprite; - init.Silent = bSilent; - init.HasLine = true; - init.sc = sc; - inits.Push(init); - } - part.Texture = NULL; - part.Translation = NULL; - } - else if (sc.Compare("Graphic")) - { - TexPart part; - TexInit init; - ParsePatch(sc, part, init); - if (init.TexName.IsNotEmpty()) - { - parts.Push(part); - init.UseType = ETextureType::MiscPatch; - init.Silent = bSilent; - init.HasLine = true; - init.sc = sc; - inits.Push(init); - } - part.Texture = NULL; - part.Translation = NULL; - } - else if (sc.Compare("Offset")) - { - sc.MustGetNumber(); - _LeftOffset[0] = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - _TopOffset[0] = sc.Number; - if (!offset2set) - { - _LeftOffset[1] = _LeftOffset[0]; - _TopOffset[1] = _TopOffset[0]; - } - } - else if (sc.Compare("Offset2")) - { - sc.MustGetNumber(); - _LeftOffset[1] = sc.Number; - sc.MustGetStringName(","); - sc.MustGetNumber(); - _TopOffset[1] = sc.Number; - offset2set = true; - } - else - { - sc.ScriptError("Unknown texture property '%s'", sc.String); - } - } - - NumParts = parts.Size(); - Parts = new TexPart[NumParts]; - memcpy(Parts, &parts[0], NumParts * sizeof(*Parts)); - Inits = new TexInit[NumParts]; - for (int i = 0; i < NumParts; i++) - { - Inits[i] = inits[i]; - } - } - - if (Width <= 0 || Height <= 0) - { - UseType = ETextureType::Null; - Printf("Texture %s has invalid dimensions (%d, %d)\n", Name.GetChars(), Width, Height); - Width = Height = 1; - } - - sc.SetCMode(false); -} - - -void FMultiPatchTexture::ResolvePatches() -{ - if (Inits != nullptr) - { - for (int i = 0; i < NumParts; i++) - { - FTextureID texno = TexMan.CheckForTexture(Inits[i].TexName, Inits[i].UseType); - if (texno == id) // we found ourselves. Try looking for another one with the same name which is not a multipatch texture itself. - { - TArray list; - TexMan.ListTextures(Inits[i].TexName, list, true); - for (int i = list.Size() - 1; i >= 0; i--) - { - if (list[i] != id && !TexMan.GetTexture(list[i])->bMultiPatch) - { - texno = list[i]; - break; - } - } - if (texno == id) - { - if (Inits[i].HasLine) Inits[i].sc.Message(MSG_WARNING, "Texture '%s' references itself as patch\n", Inits[i].TexName.GetChars()); - else Printf(TEXTCOLOR_YELLOW "Texture '%s' references itself as patch\n", Inits[i].TexName.GetChars()); - continue; - } - else - { - // If it could be resolved, just print a developer warning. - DPrintf(DMSG_WARNING, "Resolved self-referencing texture by picking an older entry for %s\n", Inits[i].TexName.GetChars()); - } - } - - if (!texno.isValid()) - { - if (!Inits[i].Silent) - { - if (Inits[i].HasLine) Inits[i].sc.Message(MSG_WARNING, "Unknown patch '%s' in texture '%s'\n", Inits[i].TexName.GetChars(), Name.GetChars()); - else Printf(TEXTCOLOR_YELLOW "Unknown patch '%s' in texture '%s'\n", Inits[i].TexName.GetChars(), Name.GetChars()); - } - } - else - { - Parts[i].Texture = TexMan.GetTexture(texno); - bComplex |= Parts[i].Texture->bComplex; - Parts[i].Texture->bKeepAround = true; - if (Inits[i].UseOffsets) - { - Parts[i].OriginX -= Parts[i].Texture->GetLeftOffset(0); - Parts[i].OriginY -= Parts[i].Texture->GetTopOffset(0); - } - } - } - for (int i = 0; i < NumParts; i++) - { - if (Parts[i].Texture == nullptr) - { - memmove(&Parts[i], &Parts[i + 1], (NumParts - i - 1) * sizeof(TexPart)); - i--; - NumParts--; - } - } - } - delete[] Inits; - Inits = nullptr; - - CheckForHacks(); - - // If this texture is just a wrapper around a single patch, we can simply - // forward getter calls to that patch. - - if (NumParts == 1) - { - if (Parts->OriginX == 0 && Parts->OriginY == 0 && - Parts->Texture->GetWidth() == Width && - Parts->Texture->GetHeight() == Height && - Parts->Rotate == 0 && - !bComplex) - { - bRedirect = true; - } - } -} - - -void FTextureManager::ParseXTexture(FScanner &sc, ETextureType usetype) -{ - FTexture *tex = new FMultiPatchTexture(sc, usetype); - TexMan.AddTexture (tex); -} diff --git a/src/textures/image.cpp b/src/textures/image.cpp index 20c457a96..7f246ba02 100644 --- a/src/textures/image.cpp +++ b/src/textures/image.cpp @@ -38,7 +38,7 @@ #include "bitmap.h" #include "image.h" - +FMemArena FImageSource::ImageArena(32768); //=========================================================================== // // the default just returns an empty texture. diff --git a/src/textures/image.h b/src/textures/image.h index 88a04b0fb..e53505acb 100644 --- a/src/textures/image.h +++ b/src/textures/image.h @@ -3,6 +3,7 @@ #include #include "tarray.h" #include "textures/bitmap.h" +#include "memarena.h" // This represents a naked image. It has no high level logic attached to it. @@ -11,6 +12,8 @@ class FImageSource { friend class FBrightmapImage; protected: + + static FMemArena ImageArena; int SourceLump; int Width = 0, Height = 0; int LeftOffset = 0, TopOffset = 0; // Offsets stored in the image. @@ -18,6 +21,10 @@ protected: public: + // Images are statically allocated and freed in bulk. None of the subclasses may hold any destructible data. + void *operator new(size_t block) { return ImageArena.Alloc(block); } + void operator delete(void *block) {} + bool bMasked = true; // Image (might) have holes (Assume true unless proven otherwise!) int8_t bTranslucent = -1; // Image has pixels with a non-0/1 value. (-1 means the user needs to do a real check) @@ -25,6 +32,7 @@ public: virtual TArray GetPalettedPixels(int conversion); // 'noremap0' will only be looked at by FPatchTexture and forwarded by FMultipatchTexture. virtual int CopyPixels(FBitmap *bmp, int conversion); // This will always ignore 'luminance'. int CopyTranslatedPixels(FBitmap *bmp, PalEntry *remap); + static void ClearImages() { ImageArena.FreeAll(); } // Conversion option enum EType @@ -69,8 +77,6 @@ public: { return bUseGamePalette; } - - bool UseBasePalette() = delete; }; //========================================================================== @@ -85,9 +91,13 @@ class FImageTexture : public FTexture public: FImageTexture (FImageSource *image, const char *name = nullptr); virtual TArray Get8BitPixels(bool alphatex); - FImageSource *GetImage() const { return mImage; } - bool UseBasePalette() override; + void SetImage(FImageSource *img) // This is only for the multipatch texture builder! + { + mImage = img; + } + + FImageSource *GetImage() const override { return mImage; } protected: int CopyPixels(FBitmap *bmp) override; diff --git a/src/textures/imagetexture.cpp b/src/textures/imagetexture.cpp index 6a376551c..9afdec41a 100644 --- a/src/textures/imagetexture.cpp +++ b/src/textures/imagetexture.cpp @@ -49,19 +49,22 @@ //========================================================================== FImageTexture::FImageTexture(FImageSource *img, const char *name) -: FTexture(name, img->LumpNum()) +: FTexture(name, img? img->LumpNum() : 0) { mImage = img; - Wads.GetLumpName (Name, img->LumpNum()); - Width = img->GetWidth(); - Height = img->GetHeight(); - - auto offsets = img->GetOffsets(); - _LeftOffset[1] = _LeftOffset[0] = offsets.first;; - _TopOffset[1] = _TopOffset[0] = offsets.second; - - bMasked = img->bMasked; - bTranslucent = img->bTranslucent; + if (img != nullptr) + { + if (name == nullptr) Wads.GetLumpName(Name, img->LumpNum()); + Width = img->GetWidth(); + Height = img->GetHeight(); + + auto offsets = img->GetOffsets(); + _LeftOffset[1] = _LeftOffset[0] = offsets.first; + _TopOffset[1] = _TopOffset[0] = offsets.second; + + bMasked = img->bMasked; + bTranslucent = img->bTranslucent; + } } //=========================================================================== @@ -86,12 +89,3 @@ TArray FImageTexture::Get8BitPixels(bool alpha) return mImage->GetPalettedPixels(alpha? alpha : bNoRemap0 ? FImageSource::noremap0 : FImageSource::normal); } -//=========================================================================== -// -// -//=========================================================================== - -bool FImageTexture::UseBasePalette() -{ - return mImage->UseGamePalette(); -} diff --git a/src/textures/multipatchtexturebuilder.cpp b/src/textures/multipatchtexturebuilder.cpp new file mode 100644 index 000000000..ac2b06917 --- /dev/null +++ b/src/textures/multipatchtexturebuilder.cpp @@ -0,0 +1,975 @@ +/* +** multipatchtexturebuilder.cpp +** Texture class for standard Doom multipatch textures +** +**--------------------------------------------------------------------------- +** Copyright 2004-2006 Randy Heit +** Copyright 2006-2018 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include +#include "doomtype.h" +#include "files.h" +#include "w_wad.h" +#include "i_system.h" +#include "gi.h" +#include "st_start.h" +#include "sc_man.h" +#include "templates.h" +#include "r_data/r_translate.h" +#include "bitmap.h" +#include "colormatcher.h" +#include "v_palette.h" +#include "v_video.h" +#include "v_text.h" +#include "cmdlib.h" +#include "imagehelpers.h" +#include "image.h" +#include "formats/multipatchtexture.h" + +// On the Alpha, accessing the shorts directly if they aren't aligned on a +// 4-byte boundary causes unaligned access warnings. Why it does this at +// all and only while initing the textures is beyond me. + +#ifdef ALPHA +#define SAFESHORT(s) ((short)(((uint8_t *)&(s))[0] + ((uint8_t *)&(s))[1] * 256)) +#else +#define SAFESHORT(s) LittleShort(s) +#endif + + + +//-------------------------------------------------------------------------- +// +// Data structures for the TEXTUREx lumps +// +//-------------------------------------------------------------------------- + +// +// 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 +{ + int16_t originx; + int16_t originy; + int16_t patch; + int16_t stepdir; + int16_t colormap; +}; + +// +// A wall texture is a list of patches which are to be combined in a +// predefined order. +// +struct maptexture_t +{ + uint8_t name[8]; + uint16_t Flags; // [RH] Was unused + uint8_t ScaleX; // [RH] Scaling (8 is normal) + uint8_t ScaleY; // [RH] Same as above + int16_t width; + int16_t height; + uint8_t columndirectory[4]; // OBSOLETE + int16_t patchcount; + mappatch_t patches[1]; +}; + +#define MAPTEXF_WORLDPANNING 0x8000 + +// Strife uses versions of the above structures that remove all unused fields + +struct strifemappatch_t +{ + int16_t originx; + int16_t originy; + int16_t patch; +}; + +// +// A wall texture is a list of patches which are to be combined in a +// predefined order. +// +struct strifemaptexture_t +{ + uint8_t name[8]; + uint16_t Flags; // [RH] Was unused + uint8_t ScaleX; // [RH] Scaling (8 is normal) + uint8_t ScaleY; // [RH] Same as above + int16_t width; + int16_t height; + int16_t patchcount; + strifemappatch_t patches[1]; +}; + + +//========================================================================== +// +// In-memory representation of a single PNAMES lump entry +// +//========================================================================== + +struct FPatchLookup +{ + FString Name; +}; + + +void FMultipatchTextureBuilder::MakeTexture(BuildInfo &buildinfo, ETextureType usetype) +{ + FImageTexture *tex = new FImageTexture(nullptr, buildinfo.Name); + tex->SetUseType(usetype); + tex->bMultiPatch = true; + tex->Width = buildinfo.Width; + tex->Height = buildinfo.Height; + tex->_LeftOffset[0] = buildinfo.LeftOffset[0]; + tex->_LeftOffset[1] = buildinfo.LeftOffset[1]; + tex->_TopOffset[0] = buildinfo.TopOffset[0]; + tex->_TopOffset[1] = buildinfo.TopOffset[1]; + tex->Scale = buildinfo.Scale; + tex->bMasked = true; // we do not really know yet. + tex->bTranslucent = -1; + tex->bWorldPanning = buildinfo.bWorldPanning; + tex->bNoDecals = buildinfo.bNoDecals; + tex->SourceLump = buildinfo.DefinitionLump; + buildinfo.id = TexMan.AddTexture(tex); + buildinfo.tex = tex; +} + +//========================================================================== +// +// The reader for TEXTUREx +// +//========================================================================== + + +//========================================================================== +// +// FMultiPatchTexture :: FMultiPatchTexture +// +//========================================================================== + +void FMultipatchTextureBuilder::BuildTexture(const void *texdef, FPatchLookup *patchlookup, int maxpatchnum, bool strife, int deflumpnum, ETextureType usetype) +{ + BuildInfo &buildinfo = BuiltTextures[BuiltTextures.Reserve(1)]; + + 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; + int NumParts; + + if (strife) + { + NumParts = SAFESHORT(mtexture.s->patchcount); + } + else + { + NumParts = SAFESHORT(mtexture.d->patchcount); + } + + if (NumParts < 0) + { + I_Error("Bad texture directory"); + } + + buildinfo.Parts.Resize(NumParts); + buildinfo.Inits.Resize(NumParts); + buildinfo.Width = SAFESHORT(mtexture.d->width); + buildinfo.Height = SAFESHORT(mtexture.d->height); + buildinfo.Name = (char *)mtexture.d->name; + + buildinfo.Scale.X = mtexture.d->ScaleX ? mtexture.d->ScaleX / 8. : 1.; + buildinfo.Scale.Y = mtexture.d->ScaleY ? mtexture.d->ScaleY / 8. : 1.; + + if (mtexture.d->Flags & MAPTEXF_WORLDPANNING) + { + buildinfo.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_Error("Bad PNAMES and/or texture directory:\n\nPNAMES has %d entries, but\n%s wants to use entry %d.", + maxpatchnum, buildinfo.Name.GetChars(), LittleShort(mpatch.d->patch) + 1); + } + buildinfo.Parts[i].OriginX = LittleShort(mpatch.d->originx); + buildinfo.Parts[i].OriginY = LittleShort(mpatch.d->originy); + buildinfo.Parts[i].Image = nullptr; + buildinfo.Inits[i].TexName = patchlookup[LittleShort(mpatch.d->patch)].Name; + buildinfo.Inits[i].UseType = ETextureType::WallPatch; + if (strife) + mpatch.s++; + else + mpatch.d++; + } + if (NumParts == 0) + { + Printf("Texture %s is left without any patches\n", buildinfo.Name.GetChars()); + } + + // Insert the incomplete texture right here so that it's in the correct place. + MakeTexture(buildinfo, usetype); + buildinfo.DefinitionLump = deflumpnum; +} + +//========================================================================== +// +// FTextureManager :: AddTexturesLump +// +//========================================================================== + +void FMultipatchTextureBuilder::AddTexturesLump(const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup, bool texture1) +{ + TArray patchlookup; + int i; + uint32_t numpatches; + + if (firstdup == 0) + { + firstdup = (int)TexMan.NumTextures(); + } + + { + 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.Resize(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"); + 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"); + 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(TexMan.ByIndex(0)); + tex0->SetSize(SAFESHORT(tex->width), SAFESHORT(tex->height)); + } + + offset = LittleLong(directory[i]); + if (offset > maxoff) + { + Printf("Bad texture directory\n"); + 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)TexMan.NumTextures() - 1; j >= firstdup; --j) + { + if (strnicmp(TexMan.ByIndex(j)->GetName(), (const char *)maptex + offset, 8) == 0) + break; + } + if (j + 1 == firstdup) + { + BuildTexture((const uint8_t *)maptex + offset, patchlookup.Data(), numpatches, isStrife, deflumpnum, (i == 1 && texture1) ? ETextureType::FirstDefined : ETextureType::Wall); + StartScreen->Progress(); + } + } +} + + +//========================================================================== +// +// FTextureManager :: AddTexturesLumps +// +//========================================================================== + +void FMultipatchTextureBuilder::AddTexturesLumps(int lump1, int lump2, int patcheslump) +{ + int firstdup = (int)TexMan.NumTextures(); + + if (lump1 >= 0) + { + FMemLump texdir = Wads.ReadLump(lump1); + AddTexturesLump(texdir.GetMem(), Wads.LumpLength(lump1), lump1, patcheslump, firstdup, true); + } + if (lump2 >= 0) + { + FMemLump texdir = Wads.ReadLump(lump2); + AddTexturesLump(texdir.GetMem(), Wads.LumpLength(lump2), lump2, patcheslump, firstdup, false); + } +} + +//========================================================================== +// +// THe reader for the textual format +// +//========================================================================== + + +//========================================================================== +// +// +// +//========================================================================== + +void FMultipatchTextureBuilder::ParsePatch(FScanner &sc, BuildInfo &info, TexPart & part, TexInit &init) +{ + FString patchname; + int Mirror = 0; + sc.MustGetString(); + + init.TexName = sc.String; + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.OriginX = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.OriginY = sc.Number; + + if (sc.CheckString("{")) + { + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("flipx")) + { + Mirror |= 1; + } + else if (sc.Compare("flipy")) + { + Mirror |= 2; + } + else if (sc.Compare("rotate")) + { + sc.MustGetNumber(); + sc.Number = (((sc.Number + 90) % 360) - 90); + if (sc.Number != 0 && sc.Number != 90 && sc.Number != 180 && sc.Number != -90) + { + sc.ScriptError("Rotation must be a multiple of 90 degrees."); + } + part.Rotate = (sc.Number / 90) & 3; + } + else if (sc.Compare("Translation")) + { + int match; + + info.bComplex = true; + if (part.Translation != NULL) delete part.Translation; + part.Translation = NULL; + part.Blend = 0; + static const char *maps[] = { "inverse", "gold", "red", "green", "blue", NULL }; + sc.MustGetString(); + + match = sc.MatchString(maps); + if (match >= 0) + { + part.Blend = BLEND_SPECIALCOLORMAP1 + match; + } + else if (sc.Compare("ICE")) + { + part.Blend = BLEND_ICEMAP; + } + else if (sc.Compare("DESATURATE")) + { + sc.MustGetStringName(","); + sc.MustGetNumber(); + part.Blend = BLEND_DESATURATE1 + clamp(sc.Number - 1, 0, 30); + } + else + { + sc.UnGet(); + part.Translation = new FRemapTable; + part.Translation->MakeIdentity(); + do + { + sc.MustGetString(); + part.Translation->AddToTranslation(sc.String); + } while (sc.CheckString(",")); + } + + } + else if (sc.Compare("Colormap")) + { + float r1, g1, b1; + float r2, g2, b2; + + sc.MustGetFloat(); + r1 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + g1 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + b1 = (float)sc.Float; + if (!sc.CheckString(",")) + { + part.Blend = AddSpecialColormap(0, 0, 0, r1, g1, b1); + } + else + { + sc.MustGetFloat(); + r2 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + g2 = (float)sc.Float; + sc.MustGetStringName(","); + sc.MustGetFloat(); + b2 = (float)sc.Float; + part.Blend = AddSpecialColormap(r1, g1, b1, r2, g2, b2); + } + } + else if (sc.Compare("Blend")) + { + info.bComplex = true; + if (part.Translation != NULL) delete part.Translation; + part.Translation = NULL; + part.Blend = 0; + + if (!sc.CheckNumber()) + { + sc.MustGetString(); + part.Blend = V_GetColor(NULL, sc); + } + else + { + int r, g, b; + + r = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + g = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + b = sc.Number; + //sc.MustGetStringName(","); This was never supposed to be here. + part.Blend = MAKERGB(r, g, b); + } + // Blend.a may never be 0 here. + if (sc.CheckString(",")) + { + sc.MustGetFloat(); + if (sc.Float > 0.f) + part.Blend.a = clamp(int(sc.Float * 255), 1, 254); + else + part.Blend = 0; + } + else part.Blend.a = 255; + } + else if (sc.Compare("alpha")) + { + sc.MustGetFloat(); + part.Alpha = clamp(int(sc.Float * BLENDUNIT), 0, BLENDUNIT); + // bComplex is not set because it is only needed when the style is not OP_COPY. + } + else if (sc.Compare("style")) + { + static const char *styles[] = { "copy", "translucent", "add", "subtract", "reversesubtract", "modulate", "copyalpha", "copynewalpha", "overlay", NULL }; + sc.MustGetString(); + part.op = sc.MustMatchString(styles); + info.bComplex |= (part.op != OP_COPY); + } + else if (sc.Compare("useoffsets")) + { + init.UseOffsets = true; + } + } + } + if (Mirror & 2) + { + part.Rotate = (part.Rotate + 2) & 3; + Mirror ^= 1; + } + if (Mirror & 1) + { + part.Rotate |= 4; + } +} + + +//========================================================================== +// +// Constructor for text based multipatch definitions +// +//========================================================================== + +void FMultipatchTextureBuilder::ParseTexture(FScanner &sc, ETextureType UseType) +{ + BuildInfo &buildinfo = BuiltTextures[BuiltTextures.Reserve(1)]; + + bool bSilent = false; + + buildinfo.textual = true; + sc.SetCMode(true); + sc.MustGetString(); + + const char *textureName = nullptr; + if (sc.Compare("optional")) + { + bSilent = true; + sc.MustGetString(); + if (sc.Compare(",")) + { + // this is not right. Apparently a texture named 'optional' is being defined right now... + sc.UnGet(); + textureName = "optional"; + bSilent = false; + } + } + buildinfo.Name = !textureName ? sc.String : textureName; + buildinfo.Name.ToUpper(); + sc.MustGetStringName(","); + sc.MustGetNumber(); + buildinfo.Width = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + buildinfo.Height = sc.Number; + + bool offset2set = false; + if (sc.CheckString("{")) + { + while (!sc.CheckString("}")) + { + sc.MustGetString(); + if (sc.Compare("XScale")) + { + sc.MustGetFloat(); + buildinfo.Scale.X = sc.Float; + if (buildinfo.Scale.X == 0) sc.ScriptError("Texture %s is defined with null x-scale\n", buildinfo.Name.GetChars()); + } + else if (sc.Compare("YScale")) + { + sc.MustGetFloat(); + buildinfo.Scale.Y = sc.Float; + if (buildinfo.Scale.Y == 0) sc.ScriptError("Texture %s is defined with null y-scale\n", buildinfo.Name.GetChars()); + } + else if (sc.Compare("WorldPanning")) + { + buildinfo.bWorldPanning = true; + } + else if (sc.Compare("NullTexture")) + { + UseType = ETextureType::Null; + } + else if (sc.Compare("NoDecals")) + { + buildinfo.bNoDecals = true; + } + else if (sc.Compare("Patch")) + { + TexPart part; + TexInit init; + ParsePatch(sc, buildinfo, part, init); + if (init.TexName.IsNotEmpty()) + { + buildinfo.Parts.Push(part); + init.UseType = ETextureType::WallPatch; + init.Silent = bSilent; + init.HasLine = true; + init.sc = sc; + buildinfo.Inits.Push(init); + } + part.Image = nullptr; + part.Translation = nullptr; + } + else if (sc.Compare("Sprite")) + { + TexPart part; + TexInit init; + ParsePatch(sc, buildinfo, part, init); + if (init.TexName.IsNotEmpty()) + { + buildinfo.Parts.Push(part); + init.UseType = ETextureType::Sprite; + init.Silent = bSilent; + init.HasLine = true; + init.sc = sc; + buildinfo.Inits.Push(init); + } + part.Image = nullptr; + part.Translation = nullptr; + } + else if (sc.Compare("Graphic")) + { + TexPart part; + TexInit init; + ParsePatch(sc, buildinfo, part, init); + if (init.TexName.IsNotEmpty()) + { + buildinfo.Parts.Push(part); + init.UseType = ETextureType::MiscPatch; + init.Silent = bSilent; + init.HasLine = true; + init.sc = sc; + buildinfo.Inits.Push(init); + } + part.Image = nullptr; + part.Translation = nullptr; + } + else if (sc.Compare("Offset")) + { + sc.MustGetNumber(); + buildinfo.LeftOffset[0] = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + buildinfo.TopOffset[0] = sc.Number; + if (!offset2set) + { + buildinfo.LeftOffset[1] = buildinfo.LeftOffset[0]; + buildinfo.TopOffset[1] = buildinfo.TopOffset[0]; + } + } + else if (sc.Compare("Offset2")) + { + sc.MustGetNumber(); + buildinfo.LeftOffset[1] = sc.Number; + sc.MustGetStringName(","); + sc.MustGetNumber(); + buildinfo.TopOffset[1] = sc.Number; + offset2set = true; + } + else + { + sc.ScriptError("Unknown texture property '%s'", sc.String); + } + } + } + + if (buildinfo.Width <= 0 || buildinfo.Height <= 0) + { + UseType = ETextureType::Null; + Printf("Texture %s has invalid dimensions (%d, %d)\n", buildinfo.Name.GetChars(), buildinfo.Width, buildinfo.Height); + buildinfo.Width = buildinfo.Height = 1; + } + + MakeTexture(buildinfo, UseType); + sc.SetCMode(false); +} + + + +//========================================================================== +// +// FMultiPatchTexture :: CheckForHacks +// +//========================================================================== + +void FMultipatchTextureBuilder::CheckForHacks(BuildInfo &buildinfo) +{ + if (buildinfo.Parts.Size() == 0) + { + return; + } + + // Heretic sky textures are marked as only 128 pixels tall, + // even though they are really 200 pixels tall. + if (gameinfo.gametype == GAME_Heretic && + buildinfo.Name.Len() == 4 && + buildinfo.Name[0] == 'S' && + buildinfo.Name[1] == 'K' && + buildinfo.Name[2] == 'Y' && + buildinfo.Name[3] >= '1' && + buildinfo.Name[3] <= '3' && + buildinfo.Height == 128) + { + buildinfo.Height = 200; + return; + } + + // The Doom E1 sky has its patch's y offset at -8 instead of 0. + if (gameinfo.gametype == GAME_Doom && + !(gameinfo.flags & GI_MAPxx) && + buildinfo.Name.Len() == 4 && + buildinfo.Parts.Size() == 1 && + buildinfo.Height == 128 && + buildinfo.Parts[0].OriginY == -8 && + buildinfo.Name[0] == 'S' && + buildinfo.Name[1] == 'K' && + buildinfo.Name[2] == 'Y' && + buildinfo.Name[3] == '1') + { + buildinfo.Parts[0].OriginY = 0; + return; + } + + // BIGDOOR7 in Doom also has patches at y offset -4 instead of 0. + if (gameinfo.gametype == GAME_Doom && + !(gameinfo.flags & GI_MAPxx) && + buildinfo.Name.CompareNoCase("BIGDOOR7") == 0 && + buildinfo.Parts.Size() == 2 && + buildinfo.Height == 128 && + buildinfo.Parts[0].OriginY == -4 && + buildinfo.Parts[1].OriginY == -4) + { + buildinfo.Parts[0].OriginY = 0; + buildinfo.Parts[1].OriginY = 0; + return; + } +} + +//========================================================================== +// +// +// +//========================================================================== + +void FMultipatchTextureBuilder::ResolvePatches(BuildInfo &buildinfo) +{ + for (unsigned i = 0; i < buildinfo.Inits.Size(); i++) + { + FTextureID texno = TexMan.CheckForTexture(buildinfo.Inits[i].TexName, buildinfo.Inits[i].UseType); + if (texno == buildinfo.id) // we found ourselves. Try looking for another one with the same name which is not a multipatch texture itself. + { + TArray list; + TexMan.ListTextures(buildinfo.Inits[i].TexName, list, true); + for (int i = list.Size() - 1; i >= 0; i--) + { + if (list[i] != buildinfo.id && !TexMan.GetTexture(list[i])->bMultiPatch) + { + texno = list[i]; + break; + } + } + if (texno == buildinfo.id) + { + if (buildinfo.Inits[i].HasLine) buildinfo.Inits[i].sc.Message(MSG_WARNING, "Texture '%s' references itself as patch\n", buildinfo.Inits[i].TexName.GetChars()); + else Printf(TEXTCOLOR_YELLOW "Texture '%s' references itself as patch\n", buildinfo.Inits[i].TexName.GetChars()); + continue; + } + else + { + // If it could be resolved, just print a developer warning. + DPrintf(DMSG_WARNING, "Resolved self-referencing texture by picking an older entry for %s\n", buildinfo.Inits[i].TexName.GetChars()); + } + } + + if (!texno.isValid()) + { + if (!buildinfo.Inits[i].Silent) + { + if (buildinfo.Inits[i].HasLine) buildinfo.Inits[i].sc.Message(MSG_WARNING, "Unknown patch '%s' in texture '%s'\n", buildinfo.Inits[i].TexName.GetChars(), buildinfo.Name.GetChars()); + else Printf(TEXTCOLOR_YELLOW "Unknown patch '%s' in texture '%s'\n", buildinfo.Inits[i].TexName.GetChars(), buildinfo.Name.GetChars()); + } + } + else + { + FTexture *tex = TexMan.GetTexture(texno); + + if (tex != nullptr && tex->isValid()) + { + //We cannot set the image source yet. First all textures need to be resolved. + buildinfo.Inits[i].Texture = tex; + buildinfo.tex->bComplex |= tex->bComplex; + buildinfo.bComplex |= tex->bComplex; + if (buildinfo.Inits[i].UseOffsets) + { + buildinfo.Parts[i].OriginX -= tex->GetLeftOffset(0); + buildinfo.Parts[i].OriginY -= tex->GetTopOffset(0); + } + } + else + { + // The patch is bogus. Remove it. + if (buildinfo.Inits[i].HasLine) buildinfo.Inits[i].sc.Message(MSG_WARNING, "Invalid patch '%s' in texture '%s'\n", buildinfo.Inits[i].TexName.GetChars(), buildinfo.Name.GetChars()); + else Printf(TEXTCOLOR_YELLOW "Invalid patch '%s' in texture '%s'\n", buildinfo.Inits[i].TexName.GetChars(), buildinfo.Name.GetChars()); + i--; + } + } + } + for (unsigned i = 0; i < buildinfo.Inits.Size(); i++) + { + if (buildinfo.Inits[i].Texture == nullptr) + { + buildinfo.Inits.Delete(i); + buildinfo.Parts.Delete(i); + i--; + } + } + + CheckForHacks(buildinfo); +} + +void FMultipatchTextureBuilder::ResolveAllPatches() +{ + for (auto &bi : BuiltTextures) + { + ResolvePatches(bi); + } + // Now try to resolve the images. We only can do this at the end when all multipatch textures are set up. + int i = 0; + while (BuiltTextures.Size() > 0) + { + bool donesomething = false; + for (unsigned i = 0; i < BuiltTextures.Size(); i++) + { + auto &buildinfo = BuiltTextures[i]; + bool hasEmpty = false; + + for (unsigned j = 0; j < buildinfo.Inits.Size(); j++) + { + if (buildinfo.Parts[j].Image == nullptr) + { + auto image = buildinfo.Inits[j].Texture->GetImage(); + if (image != nullptr) + { + buildinfo.Parts[j].Image = image; + donesomething = true; + } + else hasEmpty = true; + } + } + if (!hasEmpty) + { + // If this texture is just a wrapper around a single patch, we can simply + // use that patch's image directly here. + + bool done = false; + if (buildinfo.Parts.Size() == 1) + { + if (buildinfo.Parts[0].OriginX == 0 && buildinfo.Parts[0].OriginY == 0 && + buildinfo.Parts[0].Image->GetWidth() == buildinfo.Width && + buildinfo.Parts[0].Image->GetHeight() == buildinfo.Height && + buildinfo.Parts[0].Rotate == 0 && + !buildinfo.bComplex) + { + buildinfo.tex->SetImage(buildinfo.Parts[0].Image); + done = true; + } + } + if (!done) + { + auto img = new FMultiPatchTexture(buildinfo.Width, buildinfo.Height, buildinfo.Parts, buildinfo.bComplex, buildinfo.textual); + buildinfo.tex->SetImage(img); + } + + BuiltTextures.Delete(i); + i--; + donesomething = true; + } + } + if (!donesomething) + { + Printf(PRINT_LOG, "%d Unresolved textures remain\n", BuiltTextures.Size()); + for (auto &b : BuiltTextures) + { + Printf(PRINT_LOG, "%s\n", b.Name.GetChars()); + } + break; + } + } +} diff --git a/src/textures/skyboxtexture.cpp b/src/textures/skyboxtexture.cpp index 2d165d46c..6e063be62 100644 --- a/src/textures/skyboxtexture.cpp +++ b/src/textures/skyboxtexture.cpp @@ -66,14 +66,3 @@ int FSkyBox::CopyPixels(FBitmap *bmp) return 0; } -//----------------------------------------------------------------------------- -// -// -// -//----------------------------------------------------------------------------- - -bool FSkyBox::UseBasePalette() -{ - return false; // not really but here it's not important. -} - diff --git a/src/textures/skyboxtexture.h b/src/textures/skyboxtexture.h index cbdbdb0e6..80f9b9c72 100644 --- a/src/textures/skyboxtexture.h +++ b/src/textures/skyboxtexture.h @@ -18,8 +18,6 @@ public: FSkyBox(const char *name = nullptr); TArray Get8BitPixels(bool alphatex); int CopyPixels(FBitmap *bmp); - bool UseBasePalette(); - void Unload (); void SetSize() { @@ -31,11 +29,16 @@ public: bool Is3Face() const { - return faces[5]==NULL; + return faces[5] == nullptr; } bool IsFlipped() const { return fliptop; } + + FImageSource *GetImage() const override + { + return faces[0] ? faces[0]->GetImage() : nullptr; + } }; diff --git a/src/textures/texture.cpp b/src/textures/texture.cpp index 0aec9d106..934f585b3 100644 --- a/src/textures/texture.cpp +++ b/src/textures/texture.cpp @@ -246,54 +246,6 @@ void FTexture::SetFrontSkyLayer () bNoRemap0 = true; } -//========================================================================== -// -// -// -//========================================================================== - -void FTexture::CopyToBlock (uint8_t *dest, int dwidth, int dheight, int xpos, int ypos, int rotate, const uint8_t *translation, bool style) -{ - auto image = Get8BitPixels(style); // should use composition cache - const uint8_t *pixels = image.Data(); - int srcwidth = Width; - int srcheight = Height; - int step_x = Height; - int step_y = 1; - FClipRect cr = {0, 0, dwidth, dheight}; - if (style) translation = nullptr; // do not apply translations to alpha textures. - - if (ClipCopyPixelRect(&cr, xpos, ypos, pixels, srcwidth, srcheight, step_x, step_y, rotate)) - { - dest += ypos + dheight * xpos; - if (translation == NULL) - { - for (int x = 0; x < srcwidth; x++) - { - int pos = x * dheight; - for (int y = 0; y < srcheight; y++, pos++) - { - // the optimizer is doing a good enough job here so there's no need to optimize this by hand - uint8_t v = pixels[y * step_y + x * step_x]; - if (v != 0) dest[pos] = v; - } - } - } - else - { - for (int x = 0; x < srcwidth; x++) - { - int pos = x * dheight; - for (int y = 0; y < srcheight; y++, pos++) - { - uint8_t v = pixels[y * step_y + x * step_x]; - if (v != 0) dest[pos] = translation[v]; - } - } - } - } -} - //=========================================================================== // // FTexture::CopyPixels @@ -341,16 +293,6 @@ FBitmap FTexture::GetBgraBitmap(PalEntry *remap, int *ptrans) // //========================================================================== -bool FTexture::UseBasePalette() -{ - return true; -} - -FTexture *FTexture::GetRedirect() -{ - return this; -} - FTexture *FTexture::GetRawTexture() { return this; @@ -523,7 +465,7 @@ void FTexture::CreateDefaultBrightmap() if (!bBrightmapChecked) { // Check for brightmaps - if (UseBasePalette() && TexMan.HasGlobalBrightmap && + if (GetImage() && GetImage()->UseGamePalette() && TexMan.HasGlobalBrightmap && UseType != ETextureType::Decal && UseType != ETextureType::MiscPatch && UseType != ETextureType::FontChar && Brightmap == NULL && bWarped == 0) { @@ -779,11 +721,6 @@ bool FTexture::ProcessData(unsigned char * buffer, int w, int h, bool ispatch) if (bMasked) { bMasked = SmoothEdges(buffer, w, h); - if (!bMasked) - { - auto stex = GetRedirect(); - stex->bMasked = false; // also clear in the base texture if there is a redirection. - } if (bMasked && !ispatch) FindHoles(buffer, w, h); } return true; @@ -801,7 +738,6 @@ unsigned char * FTexture::CreateTexBuffer(int translation, int & w, int & h, int int W, H; int isTransparent = -1; - if (flags & CTF_CheckHires) { buffer = LoadHiresTexture(&w, &h); diff --git a/src/textures/texturemanager.cpp b/src/textures/texturemanager.cpp index 85221b11e..c21d9c623 100644 --- a/src/textures/texturemanager.cpp +++ b/src/textures/texturemanager.cpp @@ -52,6 +52,8 @@ #include "r_renderer.h" #include "r_sky.h" #include "vm.h" +#include "image.h" +#include "formats/multipatchtexture.h" FTextureManager TexMan; @@ -96,6 +98,7 @@ FTextureManager::~FTextureManager () void FTextureManager::DeleteAll() { + FImageSource::ClearImages(); for (unsigned int i = 0; i < Textures.Size(); ++i) { delete Textures[i].Texture; @@ -595,7 +598,7 @@ void FTextureManager::AddHiresTextures (int wadnum) // //========================================================================== -void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname) +void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname, FMultipatchTextureBuilder &build) { int remapLump, lastLump; @@ -605,12 +608,12 @@ void FTextureManager::LoadTextureDefs(int wadnum, const char *lumpname) { if (Wads.GetLumpFile(remapLump) == wadnum) { - ParseTextureDef(remapLump); + ParseTextureDef(remapLump, build); } } } -void FTextureManager::ParseTextureDef(int lump) +void FTextureManager::ParseTextureDef(int lump, FMultipatchTextureBuilder &build) { TArray tlist; @@ -728,23 +731,23 @@ void FTextureManager::ParseTextureDef(int lump) } else if (sc.Compare("texture")) { - ParseXTexture(sc, ETextureType::Override); + build.ParseTexture(sc, ETextureType::Override); } else if (sc.Compare("sprite")) { - ParseXTexture(sc, ETextureType::Sprite); + build.ParseTexture(sc, ETextureType::Sprite); } else if (sc.Compare("walltexture")) { - ParseXTexture(sc, ETextureType::Wall); + build.ParseTexture(sc, ETextureType::Wall); } else if (sc.Compare("flat")) { - ParseXTexture(sc, ETextureType::Flat); + build.ParseTexture(sc, ETextureType::Flat); } else if (sc.Compare("graphic")) { - ParseXTexture(sc, ETextureType::MiscPatch); + build.ParseTexture(sc, ETextureType::MiscPatch); } else if (sc.Compare("#include")) { @@ -758,7 +761,7 @@ void FTextureManager::ParseTextureDef(int lump) } else { - ParseTextureDef(includelump); + ParseTextureDef(includelump, build); } } else @@ -804,7 +807,7 @@ void FTextureManager::AddPatches (int lumpnum) // //========================================================================== -void FTextureManager::LoadTextureX(int wadnum) +void FTextureManager::LoadTextureX(int wadnum, FMultipatchTextureBuilder &build) { // Use the most recent PNAMES for this WAD. // Multiple PNAMES in a WAD will be ignored. @@ -822,7 +825,7 @@ void FTextureManager::LoadTextureX(int wadnum) int texlump1 = Wads.CheckNumForName ("TEXTURE1", ns_global, wadnum); int texlump2 = Wads.CheckNumForName ("TEXTURE2", ns_global, wadnum); - AddTexturesLumps (texlump1, texlump2, pnames); + build.AddTexturesLumps (texlump1, texlump2, pnames); } //========================================================================== @@ -831,7 +834,7 @@ void FTextureManager::LoadTextureX(int wadnum) // //========================================================================== -void FTextureManager::AddTexturesForWad(int wadnum) +void FTextureManager::AddTexturesForWad(int wadnum, FMultipatchTextureBuilder &build) { int firsttexture = Textures.Size(); int lumpcount = Wads.GetNumLumps(); @@ -846,7 +849,7 @@ void FTextureManager::AddTexturesForWad(int wadnum) AddGroup(wadnum, ns_patches, ETextureType::WallPatch); // Second step: TEXTUREx lumps - LoadTextureX(wadnum); + LoadTextureX(wadnum, build); // Third step: Flats AddGroup(wadnum, ns_flats, ETextureType::Flat); @@ -917,8 +920,8 @@ void FTextureManager::AddTexturesForWad(int wadnum) } // Check for text based texture definitions - LoadTextureDefs(wadnum, "TEXTURES"); - LoadTextureDefs(wadnum, "HIRESTEX"); + LoadTextureDefs(wadnum, "TEXTURES", build); + LoadTextureDefs(wadnum, "HIRESTEX", build); // Seventh step: Check for hires replacements. AddHiresTextures(wadnum); @@ -1007,14 +1010,14 @@ void FTextureManager::Init() AddTexture(CreateShaderTexture(true, true)); int wadcnt = Wads.GetNumWads(); + + FMultipatchTextureBuilder build(*this); + for(int i = 0; i< wadcnt; i++) { - AddTexturesForWad(i); - } - for (unsigned i = 0; i < Textures.Size(); i++) - { - Textures[i].Texture->ResolvePatches(); + AddTexturesForWad(i, build); } + build.ResolveAllPatches(); // Add one marker so that the last WAD is easier to handle and treat // Build tiles as a completely separate block. diff --git a/src/textures/textures.h b/src/textures/textures.h index 199b7389d..c1a08abca 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -48,6 +48,7 @@ #define MAX_CUSTOM_HW_SHADER_TEXTURES 15 typedef TMap SpriteHits; +class FImageSource; enum MaterialShaderIndex { @@ -230,6 +231,7 @@ class FTexture friend void R_InitSpriteDefs (); friend void R_InstallSprite (int num, spriteframewithrotate *sprtemp, int &maxframe); friend class GLDefsParser; + friend class FMultipatchTextureBuilder; // The serializer also needs access to more specific info that shouldn't be accessible through the interface. friend FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTextureID *defval); @@ -252,6 +254,7 @@ public: static FTexture *CreateTexture(const char *name, int lumpnum, ETextureType usetype); static FTexture *CreateTexture(int lumpnum, ETextureType usetype); virtual ~FTexture (); + virtual FImageSource *GetImage() const { return nullptr; } void AddAutoMaterials(); unsigned char *CreateUpsampledTextureBuffer(unsigned char *inputBuffer, const int inWidth, const int inHeight, int &outWidth, int &outHeight, bool hasAlpha); @@ -298,6 +301,7 @@ public: bool isFullbright() const { return bFullbright; } void CreateDefaultBrightmap(); bool FindHoles(const unsigned char * buffer, int w, int h); + void SetUseType(ETextureType type) { UseType = type; } // Returns the whole texture, stored in column-major order virtual TArray Get8BitPixels(bool alphatex); @@ -393,9 +397,6 @@ protected: virtual int CopyPixels(FBitmap *bmp); int CopyTranslatedPixels(FBitmap *bmp, PalEntry *remap); - virtual bool UseBasePalette(); - virtual FTexture *GetRedirect(); - void SetSpeed(float fac) { shaderspeed = fac; } int GetWidth () { return Width; } @@ -438,8 +439,6 @@ protected: virtual void SetFrontSkyLayer(); - void CopyToBlock (uint8_t *dest, int dwidth, int dheight, int x, int y, int rotate, const uint8_t *translation, bool style); - static void InitGrayMap(); void CopySize(FTexture *BaseTexture) @@ -566,14 +565,11 @@ public: FTextureID GetTextureID (const char *name, ETextureType usetype, BITFIELD flags=0); int ListTextures (const char *name, TArray &list, bool listall = false); - void AddTexturesLump (const void *lumpdata, int lumpsize, int deflumpnum, int patcheslump, int firstdup=0, bool texture1=false); - void AddTexturesLumps (int lump1, int lump2, int patcheslump); void AddGroup(int wadnum, int ns, ETextureType usetype); void AddPatches (int lumpnum); void AddHiresTextures (int wadnum); - void LoadTextureDefs(int wadnum, const char *lumpname); - void ParseTextureDef(int remapLump); - void ParseXTexture(FScanner &sc, ETextureType usetype); + void LoadTextureDefs(int wadnum, const char *lumpname, FMultipatchTextureBuilder &build); + void ParseTextureDef(int remapLump, FMultipatchTextureBuilder &build); void SortTexturesByType(int start, int end); bool AreTexturesCompatible (FTextureID picnum1, FTextureID picnum2); @@ -581,8 +577,8 @@ public: FTextureID AddTexture (FTexture *texture); FTextureID GetDefaultTexture() const { return DefaultTexture; } - void LoadTextureX(int wadnum); - void AddTexturesForWad(int wadnum); + void LoadTextureX(int wadnum, FMultipatchTextureBuilder &build); + void AddTexturesForWad(int wadnum, FMultipatchTextureBuilder &build); void Init(); void DeleteAll(); void SpriteAdjustChanged();