- transitioned FTexture.

Mostly working. Note: Brightmaps must be per-translation!
This commit is contained in:
Christoph Oelckers 2020-05-24 19:12:22 +02:00
parent 71ab8c0b10
commit 7a84887fc4
18 changed files with 1425 additions and 395 deletions

View File

@ -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

View File

@ -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)

View File

@ -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];

View File

@ -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<uint8_t> Pixels(pixwidth * Size.y);
TArray<uint8_t> 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;
}

View File

@ -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<BoxPrecalc>& 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>(int(src_p - scale_factor_1 / 2.0 + 1), 0, oldDim - 1);
precalc.boxEnd = clamp<int>(MAX<int>(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<BoxPrecalc> vPrecalcs(height, true);
TArray<BoxPrecalc> 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;
}
}
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <stdint.h>
#include "tarray.h"
typedef TMap<int, bool> 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;
};

View File

@ -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<TranslatedTexture> 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<class T>
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);
}
};

View File

@ -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;

View File

@ -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);
}

View File

@ -396,3 +396,8 @@ void DFrameBuffer::FinishScene()
GLInterface.DoDraw();
}
}
IHardwareTexture* CreateHardwareTexture()
{
return screen->CreateHardwareTexture();
}

View File

@ -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<uint8_t> 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<uint8_t> FTileTexture::Get8BitPixels(bool alphatex)
{
auto pix = Get8BitPixels();
if (pix) memcpy(buffer, pix, Size.x * Size.y);
TArray<uint8_t> 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<uint8_t>& backingstore, uint32_t offset, int width, int height, int picanm)
FArtTile* GetTileTexture(const char* name, const TArray<uint8_t>& 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<uint8_t>& 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<FWritableTile*>(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<FWritableTile*>(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<uint8_t> buffer(tex->GetWidth() * tex->GetHeight(), true);
tex->Create8BitPixels(buffer.Data());
TArray<uint8_t> 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<uint8_t> buffer(tex->GetWidth() * tex->GetHeight(), true);
tex->Create8BitPixels(buffer.Data());
TArray<uint8_t> dbuffer(tex->GetWidth() * tex->GetHeight(), true);
if (!tex || tex->GetTexelWidth() <= 0 || tex->GetTexelHeight() <= 0) return tileNum;
TArray<uint8_t> buffer = tex->Get8BitPixels(false);
TArray<uint8_t> 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

View File

@ -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<uint8_t> 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<uint8_t>& RawPixels;
const uint32_t Offset;
public:
FArtTile(const TArray<uint8_t>& backingstore, uint32_t offset, int width, int height, int picanm)
FArtTile(const TArray<uint8_t>& 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<uint8_t> RawPixels;
public:
FLooseTile(TArray<uint8_t> &store, int width, int height)
FLooseTile(TArray<uint8_t>& 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<uint8_t> 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<FWritableTile*>(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.

View File

@ -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<uint8_t> 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);
}

View File

@ -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<FMultiPatchTexture*>(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<FImageTexture*>(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<size; i++)
uint32_t* dwbuf = (uint32_t*)buffer;
for (int i = 0; i < size; i++)
{
uint32_t alpha = dwbuf[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; x<w - 1; x++, l1 += 4)
for (x = 1; x < w - 1; x++, l1 += 4)
{
if (l1[MSB] == 0 && !CHKPIX(-1) && !CHKPIX(1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
else if (l1[MSB] < 255) semitrans = true;
}
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 (y = 1; y<h - 1; y++)
for (y = 1; y < h - 1; y++)
{
if (l1[MSB] == 0 && !CHKPIX(-w) && !CHKPIX(1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
else if (l1[MSB] < 255) semitrans = true;
l1 += 4;
for (x = 1; x<w - 1; x++, l1 += 4)
for (x = 1; x < w - 1; x++, l1 += 4)
{
if (l1[MSB] == 0 && !CHKPIX(-w) && !CHKPIX(-1) && !CHKPIX(1) && !CHKPIX(-w - 1) && !CHKPIX(-w + 1) && !CHKPIX(w - 1) && !CHKPIX(w + 1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
else if (l1[MSB] < 255) semitrans = true;
}
if (l1[MSB] == 0 && !CHKPIX(-w) && !CHKPIX(-1)) CHKPIX(w);
else if (l1[MSB]<255) semitrans = true;
else if (l1[MSB] < 255) semitrans = true;
l1 += 4;
}
if (l1[MSB] == 0 && !CHKPIX(-w)) CHKPIX(1);
else if (l1[MSB]<255) semitrans = true;
else if (l1[MSB] < 255) semitrans = true;
l1 += 4;
for (x = 1; x<w - 1; x++, l1 += 4)
for (x = 1; x < w - 1; x++, l1 += 4)
{
if (l1[MSB] == 0 && !CHKPIX(-w) && !CHKPIX(-1)) CHKPIX(1);
else if (l1[MSB]<255) semitrans = true;
else if (l1[MSB] < 255) semitrans = true;
}
if (l1[MSB] == 0 && !CHKPIX(-w)) CHKPIX(-1);
else if (l1[MSB]<255) semitrans = true;
else if (l1[MSB] < 255) semitrans = true;
return trans || (semitrans << 1);
return trans || semitrans;
}
//===========================================================================
@ -275,12 +633,12 @@ int FTexture::SmoothEdges(unsigned char * buffer, int w, int h)
//
//===========================================================================
bool FTexture::ProcessData(unsigned char * buffer, int w, int h, bool ispatch)
bool FTexture::ProcessData(unsigned char* buffer, int w, int h, bool ispatch)
{
if (bMasked)
{
int res = SmoothEdges(buffer, w, h);
bMasked = !!(res & 1);
bMasked = SmoothEdges(buffer, w, h);
if (bMasked && !ispatch) FindHoles(buffer, w, h);
}
return true;
}
@ -291,21 +649,246 @@ bool FTexture::ProcessData(unsigned char * buffer, int w, int h, bool ispatch)
//
//===========================================================================
FTextureBuffer FTexture::CreateTexBuffer(const PalEntry * remap, int flags)
FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
{
FTextureBuffer result;
unsigned char * buffer = nullptr;
unsigned char* buffer = nullptr;
int W, H;
int isTransparent = -1;
bool checkonly = !!(flags & CTF_CheckOnly);
W = GetWidth();
H = GetHeight();
int exx = !!(flags & CTF_Expand);
W = GetTexelWidth() + 2 * exx;
H = GetTexelHeight() + 2 * exx;
if (!checkonly)
{
buffer = new unsigned char[W*(H + 1) * 4];
buffer = new unsigned char[W * (H + 1) * 4];
memset(buffer, 0, W * (H + 1) * 4);
auto remap = translation <= 0 ? nullptr : GPalette.TranslationToTable(translation);
FBitmap bmp(buffer, W * 4, W, H);
int trans;
auto Pixels = GetBgraBitmap(remap ? remap->Palette : 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<uint8_t> FTexture::Get8BitPixels(bool alphatex)
{
TArray<uint8_t> 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();
}

View File

@ -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 <vector>
#include "hw_texcontainer.h"
class FHardwareTexture;
// 15 because 0th texture is our texture
#define MAX_CUSTOM_HW_SHADER_TEXTURES 15
typedef TMap<int, bool> 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<uint8_t> 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<uint8_t> 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<int, FHardwareTexture*> 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<uint8_t> 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

View File

@ -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;

View File

@ -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<uint8_t> 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;
}

View File

@ -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)