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();