diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 2ccbbd1ae..0a337398d 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -753,6 +753,7 @@ set (PCH_SOURCES common/textures/m_png.cpp common/textures/image.cpp common/textures/texturemanager.cpp + common/textures/hw_ihwtexture.cpp common/textures/formats/fontchars.cpp common/textures/formats/automaptexture.cpp common/textures/formats/brightmaptexture.cpp diff --git a/source/build/include/mdsprite.h b/source/build/include/mdsprite.h index 45b162936..f2bda8907 100644 --- a/source/build/include/mdsprite.h +++ b/source/build/include/mdsprite.h @@ -3,6 +3,7 @@ #ifdef USE_OPENGL #include "hightile.h" +#include "glbackend/gl_hwtexture.h" #if defined(_M_IX86) || defined(_M_AMD64) || defined(__i386) || defined(__x86_64) #define SHIFTMOD32(a) (a) diff --git a/source/common/textures/animtexture.cpp b/source/common/textures/animtexture.cpp index 93dcdeda0..fecb8028b 100644 --- a/source/common/textures/animtexture.cpp +++ b/source/common/textures/animtexture.cpp @@ -42,15 +42,15 @@ void AnimTexture::SetSize(int width, int height) { - Size.x = width; - Size.y = height; + Width = width; + Height = height; Image.Resize(width*height); } void AnimTexture::SetFrame(const uint8_t *palette, const void *data_) { memcpy(Palette, palette, 768); - memcpy(Image.Data(), data_, Size.x * Size.y); + memcpy(Image.Data(), data_, Width*Height); DeleteHardwareTextures(); } @@ -64,11 +64,11 @@ FBitmap AnimTexture::GetBgraBitmap(const PalEntry* remap, int* trans) { FBitmap bmp; - bmp.Create(Size.x, Size.y); + bmp.Create(Width, Height); auto spix = Image.Data(); auto dpix = bmp.GetPixels(); - for (int i = 0; i < Size.x * Size.y; i++) + for (int i = 0; i < Width * Height; i++) { int p = i * 4; int index = spix[i]; diff --git a/source/common/textures/formats/pngtexture.cpp b/source/common/textures/formats/pngtexture.cpp index 8041f7a00..16b4805da 100644 --- a/source/common/textures/formats/pngtexture.cpp +++ b/source/common/textures/formats/pngtexture.cpp @@ -622,8 +622,8 @@ FTexture *PNGTexture_CreateFromFile(PNGHandle *png, const FString &filename) FPNGFileTexture::FPNGFileTexture (FileReader &lump, int width, int height, uint8_t colortype) : ColorType(colortype) { - Size.x = width; - Size.y = height; + Width = width; + Height = height; fr = std::move(lump); } @@ -639,11 +639,11 @@ FBitmap FPNGFileTexture::GetBgraBitmap(const PalEntry *remap, int *trans) // Parse pre-IDAT chunks. I skip the CRCs. Is that bad? PalEntry pe[256]; uint32_t len, id; - int pixwidth = Size.x * (ColorType == 2? 3:1); + int pixwidth = Width * (ColorType == 2? 3:1); FileReader *lump = &fr; - bmp.Create(Size.x, Size.y); + bmp.Create(Width, Height); lump->Seek(33, FileReader::SeekSet); lump->Read(&len, 4); lump->Read(&id, 4); @@ -670,20 +670,20 @@ FBitmap FPNGFileTexture::GetBgraBitmap(const PalEntry *remap, int *trans) } auto StartOfIDAT = (uint32_t)lump->Tell() - 8; - TArray Pixels(pixwidth * Size.y); + TArray Pixels(pixwidth * Height); lump->Seek (StartOfIDAT, FileReader::SeekSet); lump->Read(&len, 4); lump->Read(&id, 4); - M_ReadIDAT (*lump, Pixels.Data(), Size.x, Size.y, pixwidth, 8, ColorType, 0, BigLong((unsigned int)len)); + M_ReadIDAT (*lump, Pixels.Data(), Width, Height, pixwidth, 8, ColorType, 0, BigLong((unsigned int)len)); if (ColorType == 3) { - bmp.CopyPixelData(0, 0, Pixels.Data(), Size.x, Size.y, 1, Size.x, 0, pe); + bmp.CopyPixelData(0, 0, Pixels.Data(), Width, Height, 1, Width, 0, pe); } else { - bmp.CopyPixelDataRGB(0, 0, Pixels.Data(), Size.x, Size.y, 3, pixwidth, 0, CF_RGB); + bmp.CopyPixelDataRGB(0, 0, Pixels.Data(), Width, Height, 3, pixwidth, 0, CF_RGB); } return bmp; } \ No newline at end of file diff --git a/source/common/textures/hw_ihwtexture.cpp b/source/common/textures/hw_ihwtexture.cpp new file mode 100644 index 000000000..aebf387d4 --- /dev/null +++ b/source/common/textures/hw_ihwtexture.cpp @@ -0,0 +1,137 @@ +/* +** ihwtexture.cpp +** Interface for hardware textures +** +**--------------------------------------------------------------------------- +** Copyright 2006-2020 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 "hw_ihwtexture.h" +#include "templates.h" +#include "tarray.h" +#include "xs_Float.h" + +//=========================================================================== +// +// Quick'n dirty image rescaling. +// +// This will only be used when the source texture is larger than +// what the hardware can manage (extremely rare in Doom) +// +// Code taken from wxWidgets +// +//=========================================================================== + +struct BoxPrecalc +{ + int boxStart; + int boxEnd; +}; + +static void ResampleBoxPrecalc(TArray& boxes, int oldDim) +{ + int newDim = boxes.Size(); + const double scale_factor_1 = double(oldDim) / newDim; + const int scale_factor_2 = (int)(scale_factor_1 / 2); + + for (int dst = 0; dst < newDim; ++dst) + { + // Source pixel in the Y direction + const int src_p = int(dst * scale_factor_1); + + BoxPrecalc& precalc = boxes[dst]; + precalc.boxStart = clamp(int(src_p - scale_factor_1 / 2.0 + 1), 0, oldDim - 1); + precalc.boxEnd = clamp(MAX(precalc.boxStart + 1, int(src_p + scale_factor_2)), 0, oldDim - 1); + } +} + +void IHardwareTexture::Resize(int swidth, int sheight, int width, int height, unsigned char *src_data, unsigned char *dst_data) +{ + + // This function implements a simple pre-blur/box averaging method for + // downsampling that gives reasonably smooth results To scale the image + // down we will need to gather a grid of pixels of the size of the scale + // factor in each direction and then do an averaging of the pixels. + + TArray vPrecalcs(height, true); + TArray hPrecalcs(width, true); + + ResampleBoxPrecalc(vPrecalcs, sheight); + ResampleBoxPrecalc(hPrecalcs, swidth); + + int averaged_pixels, averaged_alpha, src_pixel_index; + double sum_r, sum_g, sum_b, sum_a; + + for (int y = 0; y < height; y++) // Destination image - Y direction + { + // Source pixel in the Y direction + const BoxPrecalc& vPrecalc = vPrecalcs[y]; + + for (int x = 0; x < width; x++) // Destination image - X direction + { + // Source pixel in the X direction + const BoxPrecalc& hPrecalc = hPrecalcs[x]; + + // Box of pixels to average + averaged_pixels = 0; + averaged_alpha = 0; + sum_r = sum_g = sum_b = sum_a = 0.0; + + for (int j = vPrecalc.boxStart; j <= vPrecalc.boxEnd; ++j) + { + for (int i = hPrecalc.boxStart; i <= hPrecalc.boxEnd; ++i) + { + // Calculate the actual index in our source pixels + src_pixel_index = j * swidth + i; + + int a = src_data[src_pixel_index * 4 + 3]; + if (a > 0) // do not use color from fully transparent pixels + { + sum_r += src_data[src_pixel_index * 4 + 0]; + sum_g += src_data[src_pixel_index * 4 + 1]; + sum_b += src_data[src_pixel_index * 4 + 2]; + sum_a += a; + averaged_pixels++; + } + averaged_alpha++; + + } + } + + // Calculate the average from the sum and number of averaged pixels + dst_data[0] = (unsigned char)xs_CRoundToInt(sum_r / averaged_pixels); + dst_data[1] = (unsigned char)xs_CRoundToInt(sum_g / averaged_pixels); + dst_data[2] = (unsigned char)xs_CRoundToInt(sum_b / averaged_pixels); + dst_data[3] = (unsigned char)xs_CRoundToInt(sum_a / averaged_alpha); + dst_data += 4; + } + } +} + diff --git a/source/common/textures/hw_ihwtexture.h b/source/common/textures/hw_ihwtexture.h new file mode 100644 index 000000000..24b92c926 --- /dev/null +++ b/source/common/textures/hw_ihwtexture.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include "tarray.h" + +typedef TMap SpriteHits; +class FTexture; + +class IHardwareTexture +{ +public: + enum + { + MAX_TEXTURES = 16 + }; + + IHardwareTexture() {} + virtual ~IHardwareTexture() {} + + virtual void DeleteDescriptors() { } + + virtual void AllocateBuffer(int w, int h, int texelsize) = 0; + virtual uint8_t *MapBuffer() = 0; + virtual unsigned int CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, const char *name) = 0; + + void Resize(int swidth, int sheight, int width, int height, unsigned char *src_data, unsigned char *dst_data); + + int GetBufferPitch() const { return bufferpitch; } + +protected: + int bufferpitch = -1; +}; diff --git a/source/common/textures/hw_texcontainer.h b/source/common/textures/hw_texcontainer.h new file mode 100644 index 000000000..7b6589407 --- /dev/null +++ b/source/common/textures/hw_texcontainer.h @@ -0,0 +1,139 @@ +#pragma once + +#include "tarray.h" +#include "hw_ihwtexture.h" +#include "palettecontainer.h" + +struct FTextureBuffer; +class IHardwareTexture; + +class FHardwareTextureContainer +{ +public: + enum + { + MAX_TEXTURES = 16 + }; + +private: + struct TranslatedTexture + { + IHardwareTexture *hwTexture = nullptr; + int translation = 0; + + void Delete() + { + if (hwTexture) delete hwTexture; + hwTexture = nullptr; + } + + void DeleteDescriptors() + { + if (hwTexture) hwTexture->DeleteDescriptors(); + } + + ~TranslatedTexture() + { + Delete(); + } + }; + +private: + + TranslatedTexture hwDefTex[2]; + TArray hwTex_Translated; + + TranslatedTexture * GetTexID(int translation, bool expanded) + { +#if 0 + auto remap = GPalette.TranslationToTable(translation); + translation = remap == nullptr ? 0 : remap->Index; +#endif + + if (translation == 0) + { + return &hwDefTex[expanded]; + } + + if (expanded) translation = -translation; + // normally there aren't more than very few different + // translations here so this isn't performance critical. + unsigned index = hwTex_Translated.FindEx([=](auto &element) + { + return element.translation == translation; + }); + if (index < hwTex_Translated.Size()) + { + return &hwTex_Translated[index]; + } + + int add = hwTex_Translated.Reserve(1); + auto item = &hwTex_Translated[add]; + item->translation = translation; + return item; + } + +public: + + void Clean(bool cleannormal, bool cleanexpanded) + { + if (cleannormal) hwDefTex[0].Delete(); + if (cleanexpanded) hwDefTex[1].Delete(); + hwDefTex[0].DeleteDescriptors(); + hwDefTex[1].DeleteDescriptors(); + for (int i = hwTex_Translated.Size() - 1; i >= 0; i--) + { + if (cleannormal && hwTex_Translated[i].translation > 0) hwTex_Translated.Delete(i); + else if (cleanexpanded && hwTex_Translated[i].translation < 0) hwTex_Translated.Delete(i); + + for (unsigned int j = 0; j < hwTex_Translated.Size(); j++) + hwTex_Translated[j].DeleteDescriptors(); + } + } + + IHardwareTexture * GetHardwareTexture(int translation, bool expanded) + { + auto tt = GetTexID(translation, expanded); + return tt->hwTexture; + } + + void AddHardwareTexture(int translation, bool expanded, IHardwareTexture *tex) + { + auto tt = GetTexID(translation, expanded); + tt->Delete(); + tt->hwTexture =tex; + } + + //=========================================================================== + // + // Deletes all allocated resources and considers translations + // This will only be called for sprites + // + //=========================================================================== + + void CleanUnused(SpriteHits &usedtranslations, bool expanded) + { + if (usedtranslations.CheckKey(0) == nullptr) + { + hwDefTex[expanded].Delete(); + } + int fac = expanded ? -1 : 1; + for (int i = hwTex_Translated.Size()-1; i>= 0; i--) + { + if (usedtranslations.CheckKey(hwTex_Translated[i].translation * fac) == nullptr) + { + hwTex_Translated.Delete(i); + } + } + } + + template + void Iterate(T callback) + { + for (auto & t : hwDefTex) if (t.hwTexture) callback(t.hwTexture); + for (auto & t : hwTex_Translated) if (t.hwTexture) callback(t.hwTexture); + } + + +}; + diff --git a/source/common/textures/image.h b/source/common/textures/image.h index c9a9c5be5..c6563dd1d 100644 --- a/source/common/textures/image.h +++ b/source/common/textures/image.h @@ -169,26 +169,4 @@ protected: }; -//========================================================================== -// -// a TGA texture -// -//========================================================================== - -class FImageTexture : public FTexture -{ - FImageSource *mImage; -public: - FImageTexture (FImageSource *image, const char *name = nullptr); - ~FImageTexture() - { - if (mImage) delete mImage; - } - void Create8BitPixels(uint8_t* buffer) override; - - FImageSource *GetImage() const override { return mImage; } - FBitmap GetBgraBitmap(const PalEntry *p, int *trans) override; - -}; - FTexture* CreateImageTexture(FImageSource* img, const char *name = nullptr) noexcept; diff --git a/source/core/rendering/gl/renderer/gl_renderer.cpp b/source/core/rendering/gl/renderer/gl_renderer.cpp index 2d4859369..40778380f 100644 --- a/source/core/rendering/gl/renderer/gl_renderer.cpp +++ b/source/core/rendering/gl/renderer/gl_renderer.cpp @@ -171,15 +171,15 @@ void FGLRenderer::EndOffscreen() void FGLRenderer::BindToFrameBuffer(FTexture *mat) { - auto pBaseLayer = mat->GetHardwareTexture(0); - auto BaseLayer = pBaseLayer ? *pBaseLayer : nullptr; + auto pBaseLayer = mat->SystemTextures.GetHardwareTexture(0, false); + auto BaseLayer = pBaseLayer ? (::FHardwareTexture*)pBaseLayer : nullptr; if (BaseLayer == nullptr) { // must create the hardware texture first BaseLayer = new ::FHardwareTexture(); BaseLayer->CreateTexture(mat->GetTexelWidth()*4, mat->GetTexelHeight()*4, ::FHardwareTexture::TrueColor, false); - mat->SetHardwareTexture(0, BaseLayer); + mat->SystemTextures.AddHardwareTexture(0, false, BaseLayer); } BaseLayer->BindToFrameBuffer(mat->GetTexelWidth()*4, mat->GetTexelHeight()*4); } diff --git a/source/core/rendering/v_framebuffer.cpp b/source/core/rendering/v_framebuffer.cpp index f27ad9c23..bc974f159 100644 --- a/source/core/rendering/v_framebuffer.cpp +++ b/source/core/rendering/v_framebuffer.cpp @@ -396,3 +396,8 @@ void DFrameBuffer::FinishScene() GLInterface.DoDraw(); } } + +IHardwareTexture* CreateHardwareTexture() +{ + return screen->CreateHardwareTexture(); +} diff --git a/source/core/textures/buildtiles.cpp b/source/core/textures/buildtiles.cpp index 6c3076d25..fbdf89b9f 100644 --- a/source/core/textures/buildtiles.cpp +++ b/source/core/textures/buildtiles.cpp @@ -44,6 +44,7 @@ #include "build.h" #include "gamecontrol.h" #include "palettecontainer.h" +#include "texturemanager.h" enum { @@ -84,37 +85,34 @@ FBitmap FTileTexture::GetBgraBitmap(const PalEntry* remap, int* ptrans) { FBitmap bmp; TArray buffer; - bmp.Create(Size.x, Size.y); - const uint8_t* ppix = Get8BitPixels(); - if (!remap) - remap = GPalette.BaseColors; // no remap was passed but this code needs a color table, so use the base. - if (!ppix) + bmp.Create(Width, Height); + auto ppix = GetRawData(); + if (ppix) { - // This is needed for tiles with a palette remap. - buffer.Resize(Size.x * Size.y); - Create8BitPixels(buffer.Data()); - ppix = buffer.Data(); + if (remap == nullptr) remap = GPalette.BaseColors; + bmp.CopyPixelData(0, 0, ppix, Width, Height, Height, 1, 0, remap); } - if (ppix) bmp.CopyPixelData(0, 0, ppix, Size.x, Size.y, Size.y, 1, 0, remap); return bmp; } -void FTileTexture::Create8BitPixels(uint8_t* buffer) +TArray FTileTexture::Get8BitPixels(bool alphatex) { - auto pix = Get8BitPixels(); - if (pix) memcpy(buffer, pix, Size.x * Size.y); + TArray buffer(Width * Height, true); + auto p = GetRawData(); + if (p) memcpy(buffer.Data(), p, buffer.Size()); + else memset(buffer.Data(), 0, buffer.Size()); + return buffer; } - //========================================================================== // // Tile textures are owned by their containing file object. // //========================================================================== -FArtTile* GetTileTexture(const char* name, const TArray& backingstore, uint32_t offset, int width, int height, int picanm) +FArtTile* GetTileTexture(const char* name, const TArray& backingstore, uint32_t offset, int width, int height) { - auto tex = new FArtTile(backingstore, offset, width, height, picanm); + auto tex = new FArtTile(backingstore, offset, width, height); if (tex) { @@ -175,7 +173,7 @@ void BuildTiles::AddTiles (int firsttile, TArray& RawData, bool permap) if (width <= 0 || height <= 0) continue; - auto tex = GetTileTexture("", RawData, uint32_t(tiledata - tiles), width, height, anm); + auto tex = GetTileTexture("", RawData, uint32_t(tiledata - tiles), width, height); AddTile(i, tex); int leftoffset, topoffset; this->tiledata[i].picanm = tileConvertAnimFormat(anm, &leftoffset, &topoffset); @@ -346,8 +344,7 @@ void BuildTiles::InvalidateTile(int num) void BuildTiles::MakeCanvas(int tilenum, int width, int height) { auto canvas = ValidateCustomTile(tilenum, ReplacementType::Canvas); - canvas->Size.x = width; - canvas->Size.y = height; + canvas->SetSize(width, height); } //=========================================================================== @@ -463,7 +460,7 @@ FTexture* BuildTiles::ValidateCustomTile(int tilenum, ReplacementType type) // B) the pin display in Redneck Rampage's bowling lanes. // C) Exhumed's menu plus one special effect tile. // All of these effects should probably be redone without actual texture hacking... - if (tile->GetWidth() == 0 || tile->GetHeight() == 0) return nullptr; // The base must have a size for this to work. + if (tile->GetTexelWidth() == 0 || tile->GetTexelHeight() == 0) return nullptr; // The base must have a size for this to work. // todo: invalidate hardware textures for tile. replacement = new FRestorableTile(tile); } @@ -502,7 +499,7 @@ uint8_t* BuildTiles::tileCreate(int tilenum, int width, int height) if (tex == nullptr) return nullptr; auto wtex = static_cast(tex); if (!wtex->Resize(width, height)) return nullptr; - return tex->GetWritableBuffer(); + return wtex->GetRawData(); } //========================================================================== @@ -512,26 +509,11 @@ uint8_t* BuildTiles::tileCreate(int tilenum, int width, int height) // //========================================================================== -uint8_t * BuildTiles::tileMakeWritable(int num) +uint8_t* BuildTiles::tileMakeWritable(int num) { auto tex = ValidateCustomTile(num, ReplacementType::Restorable); - return tex ? tex->GetWritableBuffer() : nullptr; -} - -//========================================================================== -// -// Sets user content for a tile. -// This must copy the buffer to make sure that the renderer has the data, -// even if processing is deferred. -// -// Only used by the movie players. -// -//========================================================================== - -void BuildTiles::tileSetExternal(int tilenum, int width, int height, uint8_t* data) -{ - uint8_t* buffer = tileCreate(tilenum, width, height); - if (buffer) memcpy(buffer, data, width * height); + auto wtex = static_cast(tex); + return wtex ? wtex->GetRawData() : nullptr; } //========================================================================== @@ -545,11 +527,11 @@ int32_t tileGetCRC32(int tileNum) if ((unsigned)tileNum >= (unsigned)MAXTILES) return 0; auto tile = tileGetTexture(tileNum); if (!tile || TileFiles.tiledata[tileNum].replacement != ReplacementType::Art) return 0; // only consider original ART tiles. - auto pixels = tile->Get8BitPixels(); - if (!pixels) return 0; - int size = tile->GetWidth() * tile->GetHeight(); + auto pixels = tile->Get8BitPixels(false); + if (!pixels.Size()) return 0; + int size = tile->GetTexelWidth() * tile->GetTexelHeight(); if (size == 0) return 0; - return crc32(0, (const Bytef*)pixels, size); + return crc32(0, (const Bytef*)pixels.Data(), size); } @@ -562,11 +544,12 @@ int32_t tileGetCRC32(int tileNum) int tileImportFromTexture(const char* fn, int tilenum, int alphacut, int istexture) { - FTexture* tex = TileFiles.GetTexture(fn); - if (tex == nullptr) return -1; - tex->alphaThreshold = 255 - alphacut; + FTextureID texid = TexMan.CheckForTexture(fn, ETextureType::Any); + if (!texid.isValid()) return -1; + auto tex = TexMan.GetTexture(texid); + //tex->alphaThreshold = 255 - alphacut; - int32_t xsiz = tex->GetWidth(), ysiz = tex->GetHeight(); + int32_t xsiz = tex->GetTexelWidth(), ysiz = tex->GetTexelHeight(); if (xsiz <= 0 || ysiz <= 0) return -2; @@ -604,8 +587,8 @@ void tileCopy(int tile, int source, int pal, int xoffset, int yoffset, int flags if (!tex) return; picanm = &TileFiles.tiledata[tile].picanm; sourceanm = picanm; - srcxo = tex->GetLeftOffset(); - srcyo = tex->GetTopOffset(); + srcxo = tex->GetTexelLeftOffset(0); + srcyo = tex->GetTexelTopOffset(0); } else { @@ -613,11 +596,10 @@ void tileCopy(int tile, int source, int pal, int xoffset, int yoffset, int flags tex = tileGetTexture(source); if (!tex) return; sourceanm = &TileFiles.tiledata[source].picanm; - srcxo = tex->GetLeftOffset(); - srcyo = tex->GetTopOffset(); + srcxo = tex->GetTexelLeftOffset(0); + srcyo = tex->GetTexelTopOffset(0); - TArray buffer(tex->GetWidth() * tex->GetHeight(), true); - tex->Create8BitPixels(buffer.Data()); + TArray buffer = tex->Get8BitPixels(false); if (pal != -1) { @@ -627,7 +609,7 @@ void tileCopy(int tile, int source, int pal, int xoffset, int yoffset, int flags pixel = remap[pixel]; } } - tex = new FLooseTile(buffer, tex->GetWidth(), tex->GetHeight()); + tex = new FLooseTile(buffer, tex->GetTexelWidth(), tex->GetTexelHeight()); picanm = &TileFiles.tiledata[tile].picanm; TileFiles.AddTile(tile, tex); } @@ -729,29 +711,6 @@ void tileSetDummy(int tile, int width, int height) } } -//========================================================================== -// -// -// -//========================================================================== - -bool tileLoad(int tileNum) -{ - if ((unsigned)tileNum >= MAXTILES) return false; - auto tex = tileGetTexture(tileNum); - if (!tex || tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) return false; - if (tex->Get8BitPixels()) return true; - - if (!tex->CachedPixels.Size()) - { - // Allocate storage if necessary. - tex->CachedPixels.Resize(tex->GetWidth() * tex->GetHeight()); - tex->Create8BitPixels(tex->CachedPixels.Data()); - } - return true; -} - - //========================================================================== // // @@ -765,7 +724,7 @@ int BuildTiles::findUnusedTile(void) for (; lastUnusedTile >= 0; --lastUnusedTile) { auto tex = tileGetTexture(lastUnusedTile); - if (!tex || tex->GetWidth() <= 0 || tex->GetHeight() <= 0) return lastUnusedTile; + if (!tex || tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) return lastUnusedTile; } return -1; } @@ -774,25 +733,25 @@ int BuildTiles::tileCreateRotated(int tileNum) { if ((unsigned)tileNum >= MAXTILES) return tileNum; auto tex = tileGetTexture(tileNum); - if (!tex || tex->GetWidth() <= 0 || tex->GetHeight() <= 0) return tileNum; - TArray buffer(tex->GetWidth() * tex->GetHeight(), true); - tex->Create8BitPixels(buffer.Data()); - TArray dbuffer(tex->GetWidth() * tex->GetHeight(), true); + if (!tex || tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) return tileNum; + TArray buffer = tex->Get8BitPixels(false); + TArray dbuffer(tex->GetTexelWidth() * tex->GetTexelHeight(), true); auto src = buffer.Data(); auto dst = dbuffer.Data(); - auto siz = tex->GetSize(); - for (int x = 0; x < siz.x; ++x) + auto width = tex->GetTexelWidth(); + auto height = tex->GetTexelHeight(); + for (int x = 0; x < width; ++x) { - int xofs = siz.x - x - 1; - int yofs = siz.y * x; + int xofs = width - x - 1; + int yofs = height * x; - for (int y = 0; y < siz.y; ++y) - *(dst + y * siz.x + xofs) = *(src + y + yofs); + for (int y = 0; y < height; ++y) + *(dst + y * width + xofs) = *(src + y + yofs); } - auto dtex = new FLooseTile(dbuffer, tex->GetHeight(), tex->GetWidth()); + auto dtex = new FLooseTile(dbuffer, tex->GetTexelHeight(), tex->GetTexelWidth()); int index = findUnusedTile(); bool mapart = TileFiles.tiledata[tileNum].texture != TileFiles.tiledata[tileNum].backup; TileFiles.AddTile(index, dtex, mapart); @@ -845,33 +804,36 @@ void BuildTiles::CloseAll() // //========================================================================== -int tileSetHightileReplacement(int picnum, int palnum, const char *filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, uint8_t flags) +int tileSetHightileReplacement(int picnum, int palnum, const char* filename, float alphacut, float xscale, float yscale, float specpower, float specfactor, uint8_t flags) { - if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1; - if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1; + if ((uint32_t)picnum >= (uint32_t)MAXTILES) return -1; + if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1; auto tex = tileGetTexture(picnum); - if (tex->GetWidth() <= 0 || tex->GetHeight() <= 0) + if (tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) { Printf("Warning: defined hightile replacement for empty tile %d.", picnum); return -1; // cannot add replacements to empty tiles, must create one beforehand } HightileReplacement replace = {}; - replace.faces[0] = TileFiles.GetTexture(filename); - if (replace.faces[0] == nullptr) + FTextureID texid = TexMan.CheckForTexture(filename, ETextureType::Any); + if (!texid.isValid()) { Printf("%s: Replacement for tile %d does not exist or is invalid\n", filename, picnum); return -1; } - replace.alphacut = min(alphacut,1.f); + + replace.faces[0] = TexMan.GetTexture(texid); + if (replace.faces[0] == nullptr) + replace.alphacut = min(alphacut, 1.f); replace.scale = { xscale, yscale }; - replace.specpower = specpower; // currently unused - replace.specfactor = specfactor; // currently unused - replace.flags = flags; + replace.specpower = specpower; // currently unused + replace.specfactor = specfactor; // currently unused + replace.flags = flags; replace.palnum = (uint16_t)palnum; TileFiles.AddReplacement(picnum, replace); - return 0; + return 0; } @@ -887,7 +849,7 @@ int tileSetSkybox(int picnum, int palnum, const char **facenames, int flags ) if ((uint32_t)palnum >= (uint32_t)MAXPALOOKUPS) return -1; auto tex = tileGetTexture(picnum); - if (tex->GetWidth() <= 0 || tex->GetHeight() <= 0) + if (tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) { Printf("Warning: defined skybox replacement for empty tile %d.", picnum); return -1; // cannot add replacements to empty tiles, must create one beforehand diff --git a/source/core/textures/buildtiles.h b/source/core/textures/buildtiles.h index 408aa9c3d..e4834f62a 100644 --- a/source/core/textures/buildtiles.h +++ b/source/core/textures/buildtiles.h @@ -1,7 +1,8 @@ #pragma once #include "textures.h" - +#include "i_time.h" +#include "compat.h" // picanm[].sf: // |bit(1<<7) @@ -69,10 +70,13 @@ struct HightileReplacement class FTileTexture : public FTexture { public: - FTileTexture() = default; + FTileTexture() + {} + virtual uint8_t* GetRawData() = 0; void SetName(const char* name) { Name = name; } - FBitmap GetBgraBitmap(const PalEntry* remap, int* ptrans) override; - void Create8BitPixels(uint8_t* buffer) override; + TArray Get8BitPixels(bool alphatex) override; + FBitmap GetBgraBitmap(const PalEntry* remap, int* trans = nullptr) override; + //bool GetTranslucency() override { return false; } }; //========================================================================== @@ -86,15 +90,14 @@ class FArtTile : public FTileTexture const TArray& RawPixels; const uint32_t Offset; public: - FArtTile(const TArray& backingstore, uint32_t offset, int width, int height, int picanm) + FArtTile(const TArray& backingstore, uint32_t offset, int width, int height) : RawPixels(backingstore), Offset(offset) { SetSize(width, height); } - - const uint8_t* Get8BitPixels() override + uint8_t* GetRawData() override final { - return RawPixels.Data() ? RawPixels.Data() + Offset : nullptr; + return &RawPixels[Offset]; } }; @@ -108,16 +111,17 @@ class FLooseTile : public FTileTexture { TArray RawPixels; public: - FLooseTile(TArray &store, int width, int height) + FLooseTile(TArray& store, int width, int height) { RawPixels = std::move(store); SetSize(width, height); } - const uint8_t* Get8BitPixels() override + uint8_t* GetRawData() override { return RawPixels.Data(); } + }; //========================================================================== @@ -134,9 +138,9 @@ public: SetSize(width, height); } - const uint8_t* Get8BitPixels() override + uint8_t* GetRawData() override { - return NULL; + return nullptr; } }; @@ -152,20 +156,19 @@ protected: TArray buffer; public: - const uint8_t* Get8BitPixels() override + FWritableTile() { - return buffer.Data(); + //useType = Writable; } - uint8_t* GetWritableBuffer() override + uint8_t* GetRawData() override { - // Todo: Invalidate all hardware textures depending on this. return buffer.Data(); } bool Resize(int w, int h) { - if (w <= 0 || h <= 0) + if (w <= 0 || h <= 0) { buffer.Reset(); return false; @@ -195,13 +198,13 @@ public: { Base = base; CopySize(base); - Resize(GetWidth(), GetHeight()); + Resize(GetTexelWidth(), GetTexelHeight()); Reload(); } - void Reload() override + void Reload() { - Base->Create8BitPixels(buffer.Data()); + buffer = std::move(Base->Get8BitPixels(false)); } }; @@ -364,21 +367,31 @@ extern BuildTiles TileFiles; inline bool tileCheck(int num) { auto tex = TileFiles.tiledata[num].texture; - return tex->GetWidth() > 0 && tex->GetHeight() > 0; + return tex && tex->GetTexelWidth() > 0 && tex->GetTexelHeight() > 0; } inline const uint8_t* tilePtr(int num) { - auto tex = TileFiles.tiledata[num].texture; - auto p = tex->Get8BitPixels(); - if (p) return p; - return tex->CachedPixels.Data(); + if (TileFiles.tiledata[num].rawCache.data.Size() == 0) + { + auto tex = TileFiles.tiledata[num].texture; + if (!tex || tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) return nullptr; + TileFiles.tiledata[num].rawCache.data = std::move(tex->Get8BitPixels(false)); + } + TileFiles.tiledata[num].rawCache.lastUseTime = I_nsTime(); + return TileFiles.tiledata[num].rawCache.data.Data(); +} + +inline bool tileLoad(int tileNum) +{ + return !!tilePtr(tileNum); } inline uint8_t* tileData(int num) { auto tex = TileFiles.tiledata[num].texture; - return tex->GetWritableBuffer(); + auto p = dynamic_cast(tex); + return p ? p->GetRawData() : nullptr; } // Some hacks to allow accessing the no longer existing arrays as if they still were arrays to avoid changing hundreds of lines of code. diff --git a/source/core/textures/imagetexture.cpp b/source/core/textures/imagetexture.cpp index 76d406063..9754ff0ea 100644 --- a/source/core/textures/imagetexture.cpp +++ b/source/core/textures/imagetexture.cpp @@ -34,10 +34,11 @@ */ #include "files.h" +#include "filesystem.h" #include "templates.h" #include "bitmap.h" #include "image.h" -#include "imagehelpers.h" +#include "textures.h" //========================================================================== @@ -46,17 +47,19 @@ // //========================================================================== -FImageTexture::FImageTexture(FImageSource *img, const char *name) -: FTexture(name) +FImageTexture::FImageTexture(FImageSource* img, const char* name) noexcept + : FTexture(name, img ? img->LumpNum() : 0) { mImage = img; if (img != nullptr) { - SetSize(img->GetWidth(), img->GetHeight()); + if (name == nullptr) fileSystem.GetFileShortName(Name, img->LumpNum()); + Width = img->GetWidth(); + Height = img->GetHeight(); auto offsets = img->GetOffsets(); - leftoffset = offsets.first; - topoffset = offsets.second; + _LeftOffset[1] = _LeftOffset[0] = offsets.first; + _TopOffset[1] = _TopOffset[0] = offsets.second; bMasked = img->bMasked; bTranslucent = img->bTranslucent; @@ -69,9 +72,9 @@ FImageTexture::FImageTexture(FImageSource *img, const char *name) // //=========================================================================== -FBitmap FImageTexture::GetBgraBitmap(const PalEntry *p, int *trans) +FBitmap FImageTexture::GetBgraBitmap(const PalEntry* p, int* trans) { - return mImage->GetCachedBitmap(p, FImageSource::normal, trans); + return mImage->GetCachedBitmap(p, bNoRemap0 ? FImageSource::noremap0 : FImageSource::normal, trans); } //=========================================================================== @@ -80,15 +83,14 @@ FBitmap FImageTexture::GetBgraBitmap(const PalEntry *p, int *trans) // //=========================================================================== -void FImageTexture::Create8BitPixels(uint8_t* buffer) +TArray FImageTexture::Get8BitPixels(bool alpha) { - //ImageHelpers::alphaThreshold = alphaThreshold; - auto buf = mImage->GetPalettedPixels(FImageSource::normal); - memcpy(buffer, buf.Data(), buf.Size()); + return mImage->GetPalettedPixels(alpha ? alpha : bNoRemap0 ? FImageSource::noremap0 : FImageSource::normal); } -FTexture* CreateImageTexture(FImageSource* img, const char *name) noexcept + +FTexture* CreateImageTexture(FImageSource* img, const char* name) noexcept { return new FImageTexture(img, name); } - \ No newline at end of file + diff --git a/source/core/textures/texture.cpp b/source/core/textures/texture.cpp index 8603f3071..d958ee265 100644 --- a/source/core/textures/texture.cpp +++ b/source/core/textures/texture.cpp @@ -34,16 +34,30 @@ ** */ +#include "printf.h" #include "files.h" +#include "filesystem.h" #include "templates.h" - +#include "textures.h" #include "bitmap.h" +#include "colormatcher.h" +#include "c_dispatch.h" +#include "m_fixed.h" +#include "imagehelpers.h" #include "image.h" -#include "palette.h" -#include "../glbackend/gl_hwtexture.h" -#include "build.h" +#include "formats/multipatchtexture.h" +#include "texturemanager.h" -FTexture *CreateBrightmapTexture(FImageSource*); +// Wrappers to keep the definitions of these classes out of here. +void DeleteMaterial(FMaterial* mat) {} +void DeleteSoftwareTexture(FSoftwareTexture* swtex) {} +IHardwareTexture* CreateHardwareTexture(); + + +FTexture* CreateBrightmapTexture(FImageSource*); + +// Make sprite offset adjustment user-configurable per renderer. +int r_spriteadjustSW, r_spriteadjustHW; //========================================================================== // @@ -54,20 +68,38 @@ FTexture *CreateBrightmapTexture(FImageSource*); // Examines the lump contents to decide what type of texture to create, // and creates the texture. -FTexture * FTexture::CreateTexture(const char *name, int lump, ETextureType useType) +FTexture* FTexture::CreateTexture(const char* name, int lumpnum, ETextureType usetype) { - if (lump < 0) - { - lump = fileSystem.FindFile(name); - if (lump < 0) return nullptr; - } - auto image = FImageSource::GetImage(lump, false); + if (lumpnum == -1) return nullptr; + + auto image = FImageSource::GetImage(lumpnum, usetype == ETextureType::Flat); if (image != nullptr) { - FTexture *tex = new FImageTexture(image); - if (tex != nullptr) + FTexture* tex = new FImageTexture(image); + if (tex != nullptr) { + tex->UseType = usetype; + if (usetype == ETextureType::Flat) + { + int w = tex->GetTexelWidth(); + int h = tex->GetTexelHeight(); + + // Auto-scale flats with dimensions 128x128 and 256x256. + // In hindsight, a bad idea, but RandomLag made it sound better than it really is. + // Now we're stuck with this stupid behaviour. + if (w == 128 && h == 128) + { + tex->Scale.X = tex->Scale.Y = 2; + tex->bWorldPanning = true; + } + else if (w == 256 && h == 256) + { + tex->Scale.X = tex->Scale.Y = 4; + tex->bWorldPanning = true; + } + } tex->Name = name; + tex->Name.ToUpper(); return tex; } } @@ -80,13 +112,58 @@ FTexture * FTexture::CreateTexture(const char *name, int lump, ETextureType useT // //========================================================================== -FTexture::FTexture (const char *name) +FTexture::FTexture(const char* name, int lumpnum) + : + Scale(1, 1), SourceLump(lumpnum), + UseType(ETextureType::Any), bNoDecals(false), bNoRemap0(false), bWorldPanning(false), + bMasked(true), bAlphaTexture(false), bHasCanvas(false), bWarped(0), bComplex(false), bMultiPatch(false), bFullNameTexture(false), + Rotations(0xFFFF), SkyOffset(0), Width(0), Height(0) { - Name = name; + bBrightmapChecked = false; + bGlowing = false; + bAutoGlowing = false; + bFullbright = false; + bDisableFullbright = false; + bSkybox = false; + bNoCompress = false; + bNoExpand = false; + bTranslucent = -1; + + + _LeftOffset[0] = _LeftOffset[1] = _TopOffset[0] = _TopOffset[1] = 0; + id.SetInvalid(); + if (name != NULL) + { + Name = name; + Name.ToUpper(); + } + else if (lumpnum < 0) + { + Name = FString(); + } + else + { + fileSystem.GetFileShortName(Name, lumpnum); + } } -FTexture::~FTexture () +FTexture::~FTexture() { + FTexture* link = fileSystem.GetLinkedTexture(SourceLump); + if (link == this) fileSystem.SetLinkedTexture(SourceLump, nullptr); + if (areas != nullptr) delete[] areas; + areas = nullptr; + + for (int i = 0; i < 2; i++) + { + if (Material[i] != nullptr) DeleteMaterial(Material[i]); + Material[i] = nullptr; + } + if (SoftwareTexture != nullptr) + { + DeleteSoftwareTexture(SoftwareTexture); + SoftwareTexture = nullptr; + } } //=========================================================================== @@ -98,20 +175,62 @@ FTexture::~FTexture () // //=========================================================================== -FBitmap FTexture::GetBgraBitmap(const PalEntry *remap, int *ptrans) +FBitmap FTexture::GetBgraBitmap(const PalEntry* remap, int* ptrans) { FBitmap bmp; - bmp.Create(Size.x, Size.y); + bmp.Create(Width, Height); return bmp; } +//========================================================================== +// +// +// +//========================================================================== + +FTexture* FTexture::GetRawTexture() +{ + if (OffsetLess) return OffsetLess; + // Reject anything that cannot have been a single-patch multipatch texture in vanilla. + auto image = static_cast(GetImage()); + if (bMultiPatch != 1 || UseType != ETextureType::Wall || Scale.X != 1 || Scale.Y != 1 || bWorldPanning || image == nullptr || image->NumParts != 1 || _TopOffset[0] == 0) + { + OffsetLess = this; + return this; + } + // Set up a new texture that directly references the underlying patch. + // From here we cannot retrieve the original texture made for it, so just create a new one. + FImageSource* source = image->Parts[0].Image; + + // Size must match for this to work as intended + if (source->GetWidth() != Width || source->GetHeight() != Height) + { + OffsetLess = this; + return this; + } + + + OffsetLess = new FImageTexture(source, ""); + TexMan.AddTexture(OffsetLess); + return OffsetLess; +} + +void FTexture::SetDisplaySize(int fitwidth, int fitheight) +{ + Scale.X = double(Width) / fitwidth; + Scale.Y = double(Height) / fitheight; + // compensate for roundoff errors + if (int(Scale.X * fitwidth) != Width) Scale.X += (1 / 65536.); + if (int(Scale.Y * fitheight) != Height) Scale.Y += (1 / 65536.); +} + //=========================================================================== // // Gets the average color of a texture for use as a sky cap color // //=========================================================================== -PalEntry FTexture::averageColor(const uint32_t *data, int size, int maxout) +PalEntry FTexture::averageColor(const uint32_t* data, int size, int maxout) { int i; unsigned int r, g, b; @@ -133,34 +252,34 @@ PalEntry FTexture::averageColor(const uint32_t *data, int size, int maxout) g = g / size; b = b / size; - int maxv = std::max(std::max(r, g), b); + int maxv = MAX(MAX(r, g), b); if (maxv && maxout) { - r = uint64_t(r) * maxout / maxv; - g = uint64_t(g) * maxout / maxv; - b = uint64_t(b) * maxout / maxv; + r = ::Scale(r, maxout, maxv); + g = ::Scale(g, maxout, maxv); + b = ::Scale(b, maxout, maxv); } return PalEntry(255, r, g, b); } PalEntry FTexture::GetSkyCapColor(bool bottom) { - if (!skyColorDone) + if (!bSWSkyColorDone) { - skyColorDone = true; + bSWSkyColorDone = true; FBitmap bitmap = GetBgraBitmap(nullptr); int w = bitmap.GetWidth(); int h = bitmap.GetHeight(); - const uint32_t *buffer = (const uint32_t *)bitmap.GetPixels(); + const uint32_t* buffer = (const uint32_t*)bitmap.GetPixels(); if (buffer) { - CeilingSkyColor = averageColor((uint32_t *)buffer, w * std::min(30, h), 0); - if (h>30) + CeilingSkyColor = averageColor((uint32_t*)buffer, w * MIN(30, h), 0); + if (h > 30) { - FloorSkyColor = averageColor(((uint32_t *)buffer) + (h - 30)*w, w * 30, 0); + FloorSkyColor = averageColor(((uint32_t*)buffer) + (h - 30) * w, w * 30, 0); } else FloorSkyColor = CeilingSkyColor; } @@ -168,6 +287,245 @@ PalEntry FTexture::GetSkyCapColor(bool bottom) return bottom ? FloorSkyColor : CeilingSkyColor; } +//==================================================================== +// +// CheckRealHeight +// +// Checks the posts in a texture and returns the lowest row (plus one) +// of the texture that is actually used. +// +//==================================================================== + +int FTexture::CheckRealHeight() +{ + auto pixels = Get8BitPixels(false); + + for (int h = GetTexelHeight() - 1; h >= 0; h--) + { + for (int w = 0; w < GetTexelWidth(); w++) + { + if (pixels[h + w * GetTexelHeight()] != 0) + { + // Scale maxy before returning it + h = int((h * 2) / Scale.Y); + h = (h >> 1) + (h & 1); + return h; + } + } + } + return 0; +} + +//========================================================================== +// +// Search auto paths for extra material textures +// +//========================================================================== + +void FTexture::AddAutoMaterials() +{ + struct AutoTextureSearchPath + { + const char* path; + FTexture* FTexture::* pointer; + }; + + static AutoTextureSearchPath autosearchpaths[] = + { + { "brightmaps/", &FTexture::Brightmap }, // For backwards compatibility, only for short names + { "materials/brightmaps/", &FTexture::Brightmap }, + { "materials/normalmaps/", &FTexture::Normal }, + { "materials/specular/", &FTexture::Specular }, + { "materials/metallic/", &FTexture::Metallic }, + { "materials/roughness/", &FTexture::Roughness }, + { "materials/ao/", &FTexture::AmbientOcclusion } + }; + + + int startindex = bFullNameTexture ? 1 : 0; + FString searchname = Name; + + if (bFullNameTexture) + { + auto dot = searchname.LastIndexOf('.'); + auto slash = searchname.LastIndexOf('/'); + if (dot > slash) searchname.Truncate(dot); + } + + for (size_t i = 0; i < countof(autosearchpaths); i++) + { + auto& layer = autosearchpaths[i]; + if (this->*(layer.pointer) == nullptr) // only if no explicit assignment had been done. + { + FStringf lookup("%s%s%s", layer.path, bFullNameTexture ? "" : "auto/", searchname.GetChars()); + auto lump = fileSystem.CheckNumForFullName(lookup, false, ns_global, true); + if (lump != -1) + { + auto bmtex = TexMan.FindTexture(fileSystem.GetFileFullName(lump), ETextureType::Any, FTextureManager::TEXMAN_TryAny); + if (bmtex != nullptr) + { + bmtex->bMasked = false; + this->*(layer.pointer) = bmtex; + } + } + } + } +} + +//=========================================================================== +// +// Checks if the texture has a default brightmap and creates it if so +// +//=========================================================================== +void FTexture::CreateDefaultBrightmap() +{ + if (!bBrightmapChecked) + { + // Check for brightmaps + if (GetImage() && GetImage()->UseGamePalette() && GPalette.HasGlobalBrightmap && + UseType != ETextureType::Decal && UseType != ETextureType::MiscPatch && UseType != ETextureType::FontChar && + Brightmap == NULL && bWarped == 0) + { + // May have one - let's check when we use this texture + auto texbuf = Get8BitPixels(false); + const int white = ColorMatcher.Pick(255, 255, 255); + + int size = GetTexelWidth() * GetTexelHeight(); + for (int i = 0; i < size; i++) + { + if (GPalette.GlobalBrightmap.Remap[texbuf[i]] == white) + { + // Create a brightmap + DPrintf(DMSG_NOTIFY, "brightmap created for texture '%s'\n", Name.GetChars()); + Brightmap = CreateBrightmapTexture(static_cast(this)->GetImage()); + bBrightmapChecked = true; + TexMan.AddTexture(Brightmap); + return; + } + } + // No bright pixels found + DPrintf(DMSG_SPAMMY, "No bright pixels found in texture '%s'\n", Name.GetChars()); + bBrightmapChecked = true; + } + else + { + // does not have one so set the flag to 'done' + bBrightmapChecked = true; + } + } +} + + +//========================================================================== +// +// Calculates glow color for a texture +// +//========================================================================== + +void FTexture::GetGlowColor(float* data) +{ + if (bGlowing && GlowColor == 0) + { + auto buffer = GetBgraBitmap(nullptr); + GlowColor = averageColor((uint32_t*)buffer.GetPixels(), buffer.GetWidth() * buffer.GetHeight(), 153); + + // Black glow equals nothing so switch glowing off + if (GlowColor == 0) bGlowing = false; + } + data[0] = GlowColor.r / 255.0f; + data[1] = GlowColor.g / 255.0f; + data[2] = GlowColor.b / 255.0f; +} + +//=========================================================================== +// +// Finds gaps in the texture which can be skipped by the renderer +// This was mainly added to speed up one area in E4M6 of 007LTSD +// +//=========================================================================== + +bool FTexture::FindHoles(const unsigned char* buffer, int w, int h) +{ + const unsigned char* li; + int y, x; + int startdraw, lendraw; + int gaps[5][2]; + int gapc = 0; + + + // already done! + if (areacount) return false; + if (UseType == ETextureType::Flat) return false; // flats don't have transparent parts + areacount = -1; //whatever happens next, it shouldn't be done twice! + + // large textures are excluded for performance reasons + if (h > 512) return false; + + startdraw = -1; + lendraw = 0; + for (y = 0; y < h; y++) + { + li = buffer + w * y * 4 + 3; + + for (x = 0; x < w; x++, li += 4) + { + if (*li != 0) break; + } + + if (x != w) + { + // non - transparent + if (startdraw == -1) + { + startdraw = y; + // merge transparent gaps of less than 16 pixels into the last drawing block + if (gapc && y <= gaps[gapc - 1][0] + gaps[gapc - 1][1] + 16) + { + gapc--; + startdraw = gaps[gapc][0]; + lendraw = y - startdraw; + } + if (gapc == 4) return false; // too many splits - this isn't worth it + } + lendraw++; + } + else if (startdraw != -1) + { + if (lendraw == 1) lendraw = 2; + gaps[gapc][0] = startdraw; + gaps[gapc][1] = lendraw; + gapc++; + + startdraw = -1; + lendraw = 0; + } + } + if (startdraw != -1) + { + gaps[gapc][0] = startdraw; + gaps[gapc][1] = lendraw; + gapc++; + } + if (startdraw == 0 && lendraw == h) return false; // nothing saved so don't create a split list + + if (gapc > 0) + { + FloatRect* rcs = new FloatRect[gapc]; + + for (x = 0; x < gapc; x++) + { + // gaps are stored as texture (u/v) coordinates + rcs[x].width = rcs[x].left = -1.0f; + rcs[x].top = (float)gaps[x][0] / (float)h; + rcs[x].height = (float)gaps[x][1] / (float)h; + } + areas = rcs; + } + else areas = nullptr; + areacount = gapc; + + return true; +} //---------------------------------------------------------------------------- // @@ -175,15 +533,15 @@ PalEntry FTexture::GetSkyCapColor(bool bottom) // //---------------------------------------------------------------------------- -void FTexture::CheckTrans(unsigned char * buffer, int size, int trans) +void FTexture::CheckTrans(unsigned char* buffer, int size, int trans) { if (bTranslucent == -1) { bTranslucent = trans; if (trans == -1) { - uint32_t * dwbuf = (uint32_t*)buffer; - for (int i = 0; i> 24; @@ -213,15 +571,15 @@ void FTexture::CheckTrans(unsigned char * buffer, int size, int trans) #define SOME_MASK 0x00ffffff #endif -#define CHKPIX(ofs) (l1[(ofs)*4+MSB]==TRANSPARENT_INDEX ? (( ((uint32_t*)l1)[0] = ((uint32_t*)l1)[ofs]&SOME_MASK), trans=true ) : false) +#define CHKPIX(ofs) (l1[(ofs)*4+MSB]==255 ? (( ((uint32_t*)l1)[0] = ((uint32_t*)l1)[ofs]&SOME_MASK), trans=true ) : false) -int FTexture::SmoothEdges(unsigned char * buffer, int w, int h) +bool FTexture::SmoothEdges(unsigned char* buffer, int w, int h) { int x, y; - int trans = buffer[MSB] == 0; // If I set this to false here the code won't detect textures + bool trans = buffer[MSB] == 0; // If I set this to false here the code won't detect textures // that only contain transparent pixels. - int semitrans = false; - unsigned char * l1; + bool semitrans = false; + unsigned char* l1; if (h <= 1 || w <= 1) return false; // makes (a) no sense and (b) doesn't work with this code! @@ -229,44 +587,44 @@ int FTexture::SmoothEdges(unsigned char * buffer, int w, int h) if (l1[MSB] == 0 && !CHKPIX(1)) CHKPIX(w); - else if (l1[MSB]<255) semitrans = true; + else if (l1[MSB] < 255) semitrans = true; l1 += 4; - for (x = 1; xPalette : nullptr, &trans); + bmp.Blit(exx, exx, Pixels); + + if (remap == nullptr) + { + CheckTrans(buffer, W * H, trans); + isTransparent = bTranslucent; + } + else + { + isTransparent = 0; + // A translated image is not conclusive for setting the texture's transparency info. + } + } + + if (GetImage()) + { + FContentIdBuilder builder; + builder.id = 0; + builder.imageID = GetImage()->GetId(); + builder.translation = MAX(0, translation); + builder.expand = exx; + result.mContentId = builder.id; + } + else result.mContentId = 0; // for non-image backed textures this has no meaning so leave it at 0. + + result.mBuffer = buffer; + result.mWidth = W; + result.mHeight = H; + + // Only do postprocessing for image-backed textures. (i.e. not for the burn texture which can also pass through here.) + if (GetImage() && flags & CTF_ProcessData) + { +#pragma message("Activate me") +#if 0 + CreateUpsampledTextureBuffer(result, !!isTransparent, checkonly); +#endif + if (!checkonly) ProcessData(result.mBuffer, result.mWidth, result.mHeight, false); + } + + return result; +} + +//=========================================================================== +// +// Dummy texture for the 0-entry. +// +//=========================================================================== + +bool FTexture::GetTranslucency() +{ + if (bTranslucent == -1) + { + if (!bHasCanvas) + { + // This will calculate all we need, so just discard the result. + CreateTexBuffer(0); + } + else + { + bTranslucent = 0; + } + } + return !!bTranslucent; +} + +//=========================================================================== +// +// the default just returns an empty texture. +// +//=========================================================================== + +TArray FTexture::Get8BitPixels(bool alphatex) +{ + TArray Pixels(Width * Height, true); + memset(Pixels.Data(), 0, Width * Height); + return Pixels; +} + +//=========================================================================== +// +// Coordinate helper. +// The only reason this is even needed is that many years ago someone +// was convinced that having per-texel panning on walls was a good idea. +// If it wasn't for this relatively useless feature the entire positioning +// code for wall textures could be a lot simpler. +// +//=========================================================================== + +//=========================================================================== +// +// +// +//=========================================================================== + +float FTexCoordInfo::RowOffset(float rowoffset) const +{ + float scale = fabs(mScale.Y); + if (scale == 1.f || mWorldPanning) return rowoffset; + else return rowoffset / scale; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +float FTexCoordInfo::TextureOffset(float textureoffset) const +{ + float scale = fabs(mScale.X); + if (scale == 1.f || mWorldPanning) return textureoffset; + else return textureoffset / scale; +} + +//=========================================================================== +// +// Returns the size for which texture offset coordinates are used. +// +//=========================================================================== + +float FTexCoordInfo::TextureAdjustWidth() const +{ + if (mWorldPanning) + { + float tscale = fabs(mTempScale.X); + if (tscale == 1.f) return (float)mRenderWidth; + else return mWidth / fabs(tscale); + } + else return (float)mWidth; +} + + +//=========================================================================== +// +// Retrieve texture coordinate info for per-wall scaling +// +//=========================================================================== + +void FTexCoordInfo::GetFromTexture(FTexture* tex, float x, float y, bool forceworldpanning) +{ + if (x == 1.f) + { + mRenderWidth = tex->GetScaledWidth(); + mScale.X = (float)tex->Scale.X; + mTempScale.X = 1.f; + } + else + { + float scale_x = x * (float)tex->Scale.X; + mRenderWidth = xs_CeilToInt(tex->GetTexelWidth() / scale_x); + mScale.X = scale_x; + mTempScale.X = x; + } + + if (y == 1.f) + { + mRenderHeight = tex->GetScaledHeight(); + mScale.Y = (float)tex->Scale.Y; + mTempScale.Y = 1.f; + } + else + { + float scale_y = y * (float)tex->Scale.Y; + mRenderHeight = xs_CeilToInt(tex->GetTexelHeight() / scale_y); + mScale.Y = scale_y; + mTempScale.Y = y; + } + if (tex->bHasCanvas) + { + mScale.Y = -mScale.Y; + mRenderHeight = -mRenderHeight; + } + mWorldPanning = tex->bWorldPanning || forceworldpanning; + mWidth = tex->GetTexelWidth(); +} + + +//========================================================================== +// +// this must be copied back to textures.cpp later. +// +//========================================================================== + +FWrapperTexture::FWrapperTexture(int w, int h, int bits) +{ + Width = w; + Height = h; + Format = bits; + UseType = ETextureType::SWCanvas; + bNoCompress = true; + auto hwtex = CreateHardwareTexture(); + // todo: Initialize here. + SystemTextures.AddHardwareTexture(0, false, hwtex); +} + +//=========================================================================== +// +// Initializes the buffer for the texture data +// +//=========================================================================== + +FTextureBuffer FTexture::CreateTexBuffer(const PalEntry* remap, int flags) +{ + FTextureBuffer result; + + unsigned char* buffer = nullptr; + int W, H; + int isTransparent = -1; + bool checkonly = !!(flags & CTF_CheckOnly); + + W = GetTexelWidth(); + H = GetTexelHeight(); + + if (!checkonly) + { + buffer = new unsigned char[W * (H + 1) * 4]; memset(buffer, 0, W * (H + 1) * 4); FBitmap bmp(buffer, W * 4, W, H); @@ -316,7 +899,7 @@ FTextureBuffer FTexture::CreateTexBuffer(const PalEntry * remap, int flags) if (remap == nullptr) { - CheckTrans(buffer, W*H, trans); + CheckTrans(buffer, W * H, trans); isTransparent = bTranslucent; } else @@ -339,59 +922,4 @@ FTextureBuffer FTexture::CreateTexBuffer(const PalEntry * remap, int flags) return result; } -//=========================================================================== -// -// Dummy texture for the 0-entry. -// -//=========================================================================== -bool FTexture::GetTranslucency() -{ - if (bTranslucent == -1) - { - if (true)//!bHasCanvas) - { - // This will calculate all we need, so just discard the result. - CreateTexBuffer(0); - } - /* - else - { - bTranslucent = 0; - }*/ - } - return !!bTranslucent; -} - -//=========================================================================== -// -// the default just returns an empty texture. -// -//=========================================================================== - -const uint8_t* FTexture::Get8BitPixels() -{ - return nullptr; // most textures do not provide a static buffer. -} - -void FTexture::Create8BitPixels(uint8_t *buffer) -{ - // The base class does not fill the texture. -} - -//=========================================================================== -// -// -// -//=========================================================================== - -void FTexture::DeleteHardwareTextures() -{ - decltype(HardwareTextures)::Iterator it(HardwareTextures); - decltype(HardwareTextures)::Pair *pair; - while (it.NextPair(pair)) - { - delete pair->Value; - } - HardwareTextures.Clear(); -} diff --git a/source/core/textures/textures.h b/source/core/textures/textures.h index a07f47ea2..5df9458a0 100644 --- a/source/core/textures/textures.h +++ b/source/core/textures/textures.h @@ -35,13 +35,18 @@ #ifndef __TEXTURES_H #define __TEXTURES_H -#include "compat.h" +#include "basics.h" +#include "vectors.h" +#include "colormatcher.h" +#include "renderstyle.h" #include "textureid.h" -#include "zstring.h" -#include "tarray.h" -#include "palentry.h" +#include +#include "hw_texcontainer.h" -class FHardwareTexture; +// 15 because 0th texture is our texture +#define MAX_CUSTOM_HW_SHADER_TEXTURES 15 + +typedef TMap SpriteHits; class FImageSource; enum MaterialShaderIndex @@ -199,116 +204,288 @@ struct FTextureBuffer // Base texture class class FTexture { - friend struct BuildTiles; - friend class FTextureManager; - friend bool tileLoad(int tileNum); - friend const uint8_t* tilePtr(int num); + 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); + + // For now only give access to classes which cannot be reworked yet. None of these should remain here when all is done. + friend class FSoftwareTexture; + friend class FWarpTexture; + friend class FMaterial; + friend class OpenGLRenderer::FGLRenderState; // For now this needs access to some fields in ApplyMaterial. This should be rerouted through the Material class + friend class VkRenderState; + friend class PolyRenderState; + friend struct FTexCoordInfo; + friend class OpenGLRenderer::FHardwareTexture; + friend class VkHardwareTexture; + friend class PolyHardwareTexture; + friend class FMultiPatchTexture; + friend class FSkyBox; + friend class FBrightmapTexture; + friend class FFont; + public: - static FTexture* CreateTexture(const char* name, int lumpnum, ETextureType useType); - + static FTexture *CreateTexture(const char *name, int lumpnum, ETextureType usetype); virtual ~FTexture (); virtual FImageSource *GetImage() const { return nullptr; } + void AddAutoMaterials(); + void CreateUpsampledTextureBuffer(FTextureBuffer &texbuffer, bool hasAlpha, bool checkonly); - const FString &GetName() const { return Name; } - bool isMasked() const { return bMasked; } - bool isTranslucent() const { return bMasked; } - PalEntry GetSkyCapColor(bool bottom); - - // Returns the whole texture, stored in column-major order - virtual void Create8BitPixels(uint8_t* buffer); - virtual const uint8_t* Get8BitPixels(); - virtual uint8_t* GetWritableBuffer() { return nullptr; } // For dynamic tiles. Requesting this must also invalidate the texture. - virtual FBitmap GetBgraBitmap(const PalEntry *remap, int *trans = nullptr); - - static int SmoothEdges(unsigned char * buffer,int w, int h); - static PalEntry averageColor(const uint32_t *data, int size, int maxout); - - int GetWidth() const { return Size.x; } - int GetHeight() const { return Size.y; } - int GetTexelWidth() const { return Size.x; } - int GetTexelHeight() const { return Size.y; } - int GetDisplayWidth() const { return Size.x; } - int GetDisplayHeight() const { return Size.y; } - const vec2_16_t &GetSize() const { return Size; } - int GetLeftOffset() const { return leftoffset; } - int GetTopOffset() const { return topoffset; } - int GetDisplayLeftOffset() const { return leftoffset; } - int GetDisplayTopOffset() const { return topoffset; } - void SetOffsets(int x, int y) { leftoffset = x; topoffset = y; } - FTextureBuffer CreateTexBuffer(const PalEntry *palette, int flags = 0); - bool GetTranslucency(); - void CheckTrans(unsigned char * buffer, int size, int trans); - bool ProcessData(unsigned char * buffer, int w, int h, bool ispatch); - virtual void Reload() {} - ETextureType GetUseType() const { return UseType; } - void DeleteHardwareTextures(); - int GetSourceLump() { return SourceLump; } - void SetUseType(ETextureType use) { UseType = use; } - - void SetHardwareTexture(int palid, FHardwareTexture* htex) + // These are mainly meant for 2D code which only needs logical information about the texture to position it properly. + int GetDisplayWidth() { int foo = int((Width * 2) / Scale.X); return (foo >> 1) + (foo & 1); } + int GetDisplayHeight() { int foo = int((Height * 2) / Scale.Y); return (foo >> 1) + (foo & 1); } + double GetDisplayWidthDouble() { return Width / Scale.X; } + double GetDisplayHeightDouble() { return Height / Scale.Y; } + int GetDisplayLeftOffset() { return GetScaledLeftOffset(0); } + int GetDisplayTopOffset() { return GetScaledTopOffset(0); } + double GetDisplayLeftOffsetDouble() { return GetScaledLeftOffsetDouble(0); } + double GetDisplayTopOffsetDouble() { return GetScaledTopOffsetDouble(0); } + void SetOffsets(int l, int t) { - HardwareTextures.Insert(palid, htex); + _LeftOffset[0] = _LeftOffset[1] = l; + _TopOffset[0] = _TopOffset[1] = l; } - FHardwareTexture** GetHardwareTexture(int palid) - { - return HardwareTextures.CheckKey(palid); - } - - int alphaThreshold = 128; - FixedBitArray<256> NoBrightmapFlag{ 0 }; - -protected: - void CopySize(FTexture *BaseTexture) + int GetTexelWidth() { return Width; } + int GetTexelHeight() { return Height; } + int GetTexelLeftOffset(int adjusted) { return _LeftOffset[adjusted]; } + int GetTexelTopOffset(int adjusted) { return _TopOffset[adjusted]; } + + + bool isValid() const { return UseType != ETextureType::Null; } + bool isSWCanvas() const { return UseType == ETextureType::SWCanvas; } + bool isSkybox() const { return bSkybox; } + bool isFullbrightDisabled() const { return bDisableFullbright; } + bool isHardwareCanvas() const { return bHasCanvas; } // There's two here so that this can deal with software canvases in the hardware renderer later. + bool isCanvas() const { return bHasCanvas; } + bool isMiscPatch() const { return UseType == ETextureType::MiscPatch; } // only used by the intermission screen to decide whether to tile the background image or not. + int isWarped() const { return bWarped; } + int GetRotations() const { return Rotations; } + void SetRotations(int rot) { Rotations = int16_t(rot); } + bool isSprite() const { return UseType == ETextureType::Sprite || UseType == ETextureType::SkinSprite || UseType == ETextureType::Decal; } + + const FString &GetName() const { return Name; } + void SetNoDecals(bool on) { bNoDecals = on; } + void SetWarpStyle(int style) { bWarped = style; } + bool allowNoDecals() const { return bNoDecals; } + bool isScaled() const { return Scale.X != 1 || Scale.Y != 1; } + bool isMasked() const { return bMasked; } + void SetSkyOffset(int offs) { SkyOffset = offs; } + int GetSkyOffset() const { return SkyOffset; } + FTextureID GetID() const { return id; } + PalEntry GetSkyCapColor(bool bottom); + FTexture *GetRawTexture(); + virtual int GetSourceLump() { return SourceLump; } // needed by the scripted GetName method. + void GetGlowColor(float *data); + bool isGlowing() const { return bGlowing; } + bool isAutoGlowing() const { return bAutoGlowing; } + int GetGlowHeight() const { return GlowHeight; } + bool isFullbright() const { return bFullbright; } + void CreateDefaultBrightmap(); + bool FindHoles(const unsigned char * buffer, int w, int h); + void SetUseType(ETextureType type) { UseType = type; } + int GetSourceLump() const { return SourceLump; } + ETextureType GetUseType() const { return UseType; } + void SetSpeed(float fac) { shaderspeed = fac; } + void SetWorldPanning(bool on) { bWorldPanning = on; } + void SetDisplaySize(int fitwidth, int fitheight); + void SetFrontSkyLayer(bool on = true) { bNoRemap0 = on; } + bool IsFrontSkyLayer() { return bNoRemap0; } + + FTexture* GetBrightmap() { - Size.x = BaseTexture->GetWidth(); - Size.y = BaseTexture->GetHeight(); - leftoffset = BaseTexture->leftoffset; - topoffset = BaseTexture->topoffset; + if (!bBrightmapChecked) + CreateDefaultBrightmap(); + return Brightmap; } + void CopySize(FTexture* BaseTexture) + { + Width = BaseTexture->GetTexelWidth(); + Height = BaseTexture->GetTexelHeight(); + _TopOffset[0] = BaseTexture->_TopOffset[0]; + _TopOffset[1] = BaseTexture->_TopOffset[1]; + _LeftOffset[0] = BaseTexture->_LeftOffset[0]; + _LeftOffset[1] = BaseTexture->_LeftOffset[1]; + Scale = BaseTexture->Scale; + } + + // This is only used for the null texture and for Heretic's skies. void SetSize(int w, int h) { - Size = { int16_t(w), int16_t(h) }; + Width = w; + Height = h; } + + // Returns the whole texture, stored in column-major order + virtual TArray Get8BitPixels(bool alphatex); + virtual FBitmap GetBgraBitmap(const PalEntry *remap, int *trans = nullptr); + + static bool SmoothEdges(unsigned char * buffer,int w, int h); + static PalEntry averageColor(const uint32_t *data, int size, int maxout); + + + FSoftwareTexture *GetSoftwareTexture(); + +protected: + + DVector2 Scale; + + int SourceLump; + FTextureID id; + + FMaterial *Material[2] = { nullptr, nullptr }; +public: + FHardwareTextureContainer SystemTextures; + int alphaThreshold = 0.5; + FixedBitArray<256> NoBrightmapFlag = false; +protected: + FSoftwareTexture *SoftwareTexture = nullptr; + + // None of the following pointers are owned by this texture, they are all controlled by the texture manager. + + // Offset-less version for COMPATF_MASKEDMIDTEX + FTexture *OffsetLess = nullptr; + // Paletted variant + FTexture *PalVersion = nullptr; + // Material layers + FTexture *Brightmap = nullptr; + FTexture *Normal = nullptr; // Normal map texture + FTexture *Specular = nullptr; // Specular light texture for the diffuse+normal+specular light model + FTexture *Metallic = nullptr; // Metalness texture for the physically based rendering (PBR) light model + FTexture *Roughness = nullptr; // Roughness texture for PBR + FTexture *AmbientOcclusion = nullptr; // Ambient occlusion texture for PBR + + FTexture *CustomShaderTextures[MAX_CUSTOM_HW_SHADER_TEXTURES] = { nullptr }; // Custom texture maps for custom hardware shaders + FString Name; - union + ETextureType UseType; // This texture's primary purpose + + uint8_t bNoDecals:1; // Decals should not stick to texture + uint8_t bNoRemap0:1; // Do not remap color 0 (used by front layer of parallax skies) + uint8_t bWorldPanning:1; // Texture is panned in world units rather than texels + uint8_t bMasked:1; // Texture (might) have holes + uint8_t bAlphaTexture:1; // Texture is an alpha channel without color information + uint8_t bHasCanvas:1; // Texture is based off FCanvasTexture + uint8_t bWarped:2; // This is a warped texture. Used to avoid multiple warps on one texture + uint8_t bComplex:1; // Will be used to mark extended MultipatchTextures that have to be + // fully composited before subjected to any kind of postprocessing instead of + // doing it per patch. + uint8_t bMultiPatch:2; // This is a multipatch texture (we really could use real type info for textures...) + uint8_t bFullNameTexture : 1; + uint8_t bBrightmapChecked : 1; // Set to 1 if brightmap has been checked + uint8_t bGlowing : 1; // Texture glow color + uint8_t bAutoGlowing : 1; // Glow info is determined from texture image. + uint8_t bFullbright : 1; // always draw fullbright + uint8_t bDisableFullbright : 1; // This texture will not be displayed as fullbright sprite + uint8_t bSkybox : 1; // is a cubic skybox + uint8_t bNoCompress : 1; + uint8_t bNoExpand : 1; + int8_t bTranslucent : 2; + bool bHiresHasColorKey = false; // Support for old color-keyed Doomsday textures + + uint16_t Rotations; + int16_t SkyOffset; + FloatRect *areas = nullptr; + int areacount = 0; + int GlowHeight = 128; + PalEntry GlowColor = 0; + int HiresLump = -1; // For external hires textures. + float Glossiness = 10.f; + float SpecularLevel = 0.1f; + float shaderspeed = 1.f; + int shaderindex = 0; + + int GetScaledWidth () { int foo = int((Width * 2) / Scale.X); return (foo >> 1) + (foo & 1); } + int GetScaledHeight () { int foo = int((Height * 2) / Scale.Y); return (foo >> 1) + (foo & 1); } + double GetScaledWidthDouble () { return Width / Scale.X; } + double GetScaledHeightDouble () { return Height / Scale.Y; } + double GetScaleY() const { return Scale.Y; } + + + // Now with improved offset adjustment. + int GetLeftOffset(int adjusted) { return _LeftOffset[adjusted]; } + int GetTopOffset(int adjusted) { return _TopOffset[adjusted]; } + int GetScaledLeftOffset (int adjusted) { int foo = int((_LeftOffset[adjusted] * 2) / Scale.X); return (foo >> 1) + (foo & 1); } + int GetScaledTopOffset (int adjusted) { int foo = int((_TopOffset[adjusted] * 2) / Scale.Y); return (foo >> 1) + (foo & 1); } + double GetScaledLeftOffsetDouble(int adjusted) { return _LeftOffset[adjusted] / Scale.X; } + double GetScaledTopOffsetDouble(int adjusted) { return _TopOffset[adjusted] / Scale.Y; } + + // Interfaces for the different renderers. Everything that needs to check renderer-dependent offsets + // should use these, so that if changes are needed, this is the only place to edit. + + // For the hardware renderer. The software renderer's have been offloaded to FSoftwareTexture + int GetLeftOffsetHW() { return _LeftOffset[r_spriteadjustHW]; } + int GetTopOffsetHW() { return _TopOffset[r_spriteadjustHW]; } + + virtual void ResolvePatches() {} + + void SetScale(const DVector2 &scale) { - vec2_16_t Size = { 0,0 }; // Keep this in the native format so that we can use it without copying it around. - struct { uint16_t Width, Height; }; - }; - int leftoffset = 0, topoffset = 0; - uint8_t bMasked = true; // Texture (might) have holes - int8_t bTranslucent = -1; // Does this texture have an active alpha channel? - bool skyColorDone = false; - ETextureType UseType = ETextureType::Any; + Scale = scale; + } + +protected: + uint16_t Width, Height; + int16_t _LeftOffset[2], _TopOffset[2]; + + FTexture (const char *name = NULL, int lumpnum = -1); + +public: + FTextureBuffer CreateTexBuffer(int translation, int flags = 0); + FTextureBuffer CreateTexBuffer(const PalEntry* translation, int flags = 0); + bool GetTranslucency(); + FMaterial* GetMaterial(int num) + { + return Material[num]; + } + FTexture* GetPalVersion() + { + return PalVersion; + } + + void DeleteHardwareTextures() + { + SystemTextures.Clean(true, true); + } + +private: + int CheckDDPK3(); + int CheckExternalFile(bool & hascolorkey); + + bool bSWSkyColorDone = false; PalEntry FloorSkyColor; PalEntry CeilingSkyColor; - TArray CachedPixels; - // Don't waste too much effort on efficient storage here. Polymost performs so many calculations on a single draw call that the minor map lookup hardly matters. - TMap HardwareTextures; // Note: These must be deleted by the backend. When the texture manager is taken down it may already be too late to delete them. - bool bFullNameTexture = false; - FTextureID id = FSetTextureID(-1); - int SourceLump = -1; - FTexture (const char *name = NULL); - friend struct BuildTiles; +public: + + void CheckTrans(unsigned char * buffer, int size, int trans); + bool ProcessData(unsigned char * buffer, int w, int h, bool ispatch); + int CheckRealHeight(); + + friend class FTextureManager; }; + +// A texture that can be drawn to. + class FCanvasTexture : public FTexture { public: FCanvasTexture(const char* name, int width, int height) { Name = name; - Size.x = width; - Size.y = height; + Width = width; + Height = height; bMasked = false; + bHasCanvas = true; bTranslucent = false; - //bNoExpand = true; + bNoExpand = true; UseType = ETextureType::Wall; } @@ -325,6 +502,60 @@ public: friend struct FCanvasTextureInfo; }; + +// A wrapper around a hardware texture, to allow using it in the 2D drawing interface. +class FWrapperTexture : public FTexture +{ + int Format; +public: + FWrapperTexture(int w, int h, int bits = 1); + IHardwareTexture *GetSystemTexture() + { + return SystemTextures.GetHardwareTexture(0, false); + } + + int GetColorFormat() const + { + return Format; + } +}; + + +class FImageTexture : public FTexture +{ + FImageSource* mImage; +public: + FImageTexture(FImageSource* image, const char* name = nullptr) noexcept; + virtual TArray Get8BitPixels(bool alphatex); + + void SetImage(FImageSource* img) // This is only for the multipatch texture builder! + { + mImage = img; + } + + FImageSource* GetImage() const override { return mImage; } + FBitmap GetBgraBitmap(const PalEntry* p, int* trans) override; + +}; + +struct FTexCoordInfo +{ + int mRenderWidth; + int mRenderHeight; + int mWidth; + FVector2 mScale; + FVector2 mTempScale; + bool mWorldPanning; + + float FloatToTexU(float v) const { return v / mRenderWidth; } + float FloatToTexV(float v) const { return v / mRenderHeight; } + float RowOffset(float ofs) const; + float TextureOffset(float ofs) const; + float TextureAdjustWidth() const; + void GetFromTexture(FTexture *tex, float x, float y, bool forceworldpanning); +}; + + #endif diff --git a/source/glbackend/gl_hwtexture.h b/source/glbackend/gl_hwtexture.h index f2db59b54..4abb812c5 100644 --- a/source/glbackend/gl_hwtexture.h +++ b/source/glbackend/gl_hwtexture.h @@ -3,8 +3,9 @@ class FBitmap; class FTexture; #include "tarray.h" +#include "hw_ihwtexture.h" -class FHardwareTexture //: public IHardwareTexture +class FHardwareTexture : public IHardwareTexture { public: enum @@ -33,6 +34,11 @@ public: ~FHardwareTexture(); + // Satisfy the virtual interface - currently this isn't being used. + virtual void AllocateBuffer(int w, int h, int texelsize) {} + virtual uint8_t* MapBuffer() { return nullptr; } + virtual unsigned int CreateTexture(unsigned char* buffer, int w, int h, int texunit, bool mipmap, const char* name) { return 0; } + //bool BindOrCreate(FTexture *tex, int texunit, int clampmode, int translation, int flags); unsigned int CreateTexture(int w, int h, bool type, bool mipmapped) = delete; diff --git a/source/glbackend/gl_texture.cpp b/source/glbackend/gl_texture.cpp index c3923613f..187a2755e 100644 --- a/source/glbackend/gl_texture.cpp +++ b/source/glbackend/gl_texture.cpp @@ -68,15 +68,10 @@ void FlipNonSquareBlock(T* dst, const T* src, int x, int y, int srcpitch) FHardwareTexture* GLInstance::CreateIndexedTexture(FTexture* tex) { - auto siz = tex->GetSize(); + vec2_t siz = { tex->GetTexelWidth(), tex->GetTexelHeight() }; - const uint8_t* p = tex->Get8BitPixels(); - TArray store(siz.x * siz.y, true); - if (!p) - { - tex->Create8BitPixels(store.Data()); - p = store.Data(); - } + auto store = tex->Get8BitPixels(false); + const uint8_t* p = store.Data(); auto glpic = GLInterface.NewTexture(); glpic->CreateTexture(siz.x, siz.y, FHardwareTexture::Indexed, false); @@ -132,8 +127,8 @@ FHardwareTexture* GLInstance::CreateTrueColorTexture(FTexture* tex, int palid, b FHardwareTexture* GLInstance::LoadTexture(FTexture* tex, int textype, int palid) { if (textype == TT_INDEXED) palid = -1; - auto phwtex = tex->GetHardwareTexture(palid); - if (phwtex) return *phwtex; + auto phwtex = tex->SystemTextures.GetHardwareTexture(palid, false); + if (phwtex) return (FHardwareTexture*)phwtex; FHardwareTexture *hwtex = nullptr; if (textype == TT_INDEXED) @@ -143,7 +138,7 @@ FHardwareTexture* GLInstance::LoadTexture(FTexture* tex, int textype, int palid) else hwtex = nullptr; - if (hwtex) tex->SetHardwareTexture(palid, hwtex); + if (hwtex) tex->SystemTextures.AddHardwareTexture(palid, false, hwtex); return hwtex; } @@ -392,7 +387,7 @@ bool GLInstance::SetTextureInternal(int picnum, FTexture* tex, int palette, int float al = 0.5f; if (TextureType == TT_HICREPLACE) { - al = ((unsigned)picnum < MAXTILES && alphahackarray[picnum] != 0) ? alphahackarray[picnum] * (1.f / 255.f) : + al = ((unsigned)picnum < MAXTILES&& alphahackarray[picnum] != 0) ? alphahackarray[picnum] * (1.f / 255.f) : (tex->alphaThreshold >= 0 ? tex->alphaThreshold * (1.f / 255.f) : 0.f); } GLInterface.SetAlphaThreshold(al); @@ -413,7 +408,7 @@ bool GLInstance::SetNamedTexture(FTexture* tex, int palette, int sampler) if (!mtex) return false; BindTexture(0, mtex, sampler); - GLInterface.SetAlphaThreshold(tex->isTranslucent()? 0.f : 0.5f); + GLInterface.SetAlphaThreshold(tex->GetTranslucency()? 0.f : 0.5f); return true; } diff --git a/source/platform/win32/i_system.cpp b/source/platform/win32/i_system.cpp index efa5b6aa7..b6b360817 100644 --- a/source/platform/win32/i_system.cpp +++ b/source/platform/win32/i_system.cpp @@ -659,8 +659,8 @@ bool I_SetCursor(FTexture *cursorpic) return false; } // Fixme: This should get a raw image, not a texture. (Once raw images get implemented.) - int lo = cursorpic->GetLeftOffset(); - int to = cursorpic->GetTopOffset(); + int lo = cursorpic->GetDisplayLeftOffset(); + int to = cursorpic->GetDisplayTopOffset(); cursor = CreateAlphaCursor(image, lo, to); if (cursor == NULL)