From 530a9944f742c245ed38e93d4aaa8c3f5646fe38 Mon Sep 17 00:00:00 2001
From: Christoph Oelckers <christoph.oelckers@go-2b.de>
Date: Fri, 18 Oct 2019 11:37:07 +0200
Subject: [PATCH] - texture loader redone but not tested yet

Models are currently non-functional and need to reroute their texture handling to the global texture manager instead of handling everything themselves.

Voxels also need a bit of work to make their texture management more automated.
---
 source/build/include/polymost.h         |   2 -
 source/build/src/engine.cpp             |  45 ---
 source/build/src/osd.cpp                |   6 +-
 source/build/src/polymost.cpp           |   3 +-
 source/build/src/texcache.cpp           | 373 +++++-------------------
 source/common/textures/buildtiles.cpp   |  46 +++
 source/common/textures/imagetexture.cpp |   2 +-
 source/common/textures/texture.cpp      |  61 +---
 source/common/textures/textures.h       |  19 +-
 source/glbackend/gl_palmanager.cpp      |   2 +-
 source/glbackend/glbackend.h            |   7 +-
 11 files changed, 158 insertions(+), 408 deletions(-)

diff --git a/source/build/include/polymost.h b/source/build/include/polymost.h
index f435f7f00..cc10bb610 100644
--- a/source/build/include/polymost.h
+++ b/source/build/include/polymost.h
@@ -61,8 +61,6 @@ enum {
     INVALIDATE_ART_NON_INDEXED
 };
 
-void gltexinvalidate(int32_t dapicnum, int32_t dapalnum, int32_t dameth);
-void gltexinvalidatetype(int32_t type);
 int32_t polymost_printext256(int32_t xpos, int32_t ypos, int16_t col, int16_t backcol, const char *name, char fontsize);
 
 extern float curpolygonoffset;
diff --git a/source/build/src/engine.cpp b/source/build/src/engine.cpp
index 33de62b29..c0be81c37 100644
--- a/source/build/src/engine.cpp
+++ b/source/build/src/engine.cpp
@@ -12969,48 +12969,3 @@ void renderSetRollAngle(int32_t rolla)
 #endif
 
 
-//
-// invalidatetile
-//  pal: pass -1 to invalidate all palettes for the tile, or >=0 for a particular palette
-//  how: pass -1 to invalidate all instances of the tile in texture memory, or a bitfield
-//         bit 0: opaque or masked (non-translucent) texture, using repeating
-//         bit 1: ignored
-//         bit 2: ignored (33% translucence, using repeating)
-//         bit 3: ignored (67% translucence, using repeating)
-//         bit 4: opaque or masked (non-translucent) texture, using clamping
-//         bit 5: ignored
-//         bit 6: ignored (33% translucence, using clamping)
-//         bit 7: ignored (67% translucence, using clamping)
-//       clamping is for sprites, repeating is for walls
-//
-void tileInvalidate(int16_t tilenume, int32_t pal, int32_t how)
-{
-#if !defined USE_OPENGL
-    UNREFERENCED_PARAMETER(tilenume);
-    UNREFERENCED_PARAMETER(pal);
-    UNREFERENCED_PARAMETER(how);
-#else
-    if (videoGetRenderMode() >= REND_POLYMOST)
-    {
-        const int32_t firstpal = (pal < 0) ? 0 : pal;
-        const int32_t numpals = (pal < 0) ? MAXPALOOKUPS : 1;
-
-        for (bssize_t hp = 0; hp <= 4; hp+=4)
-        {
-            if (how & pow2long[hp])
-                for (bssize_t np = firstpal; np < firstpal+numpals; np++)
-                    gltexinvalidate(tilenume, np, hp);
-        }
-
-#ifdef POLYMER
-        if (videoGetRenderMode() == REND_POLYMER)
-            polymer_invalidateartmap(tilenume);
-#endif
-    }
-#endif
-}
-
-/*
- * vim:ts=8:
- */
-
diff --git a/source/build/src/osd.cpp b/source/build/src/osd.cpp
index d5fd522b2..6cf1ba3c3 100644
--- a/source/build/src/osd.cpp
+++ b/source/build/src/osd.cpp
@@ -2161,10 +2161,10 @@ int osdcmd_cvar_set(osdcmdptr_t parm)
             //osdcmd_restartvid(NULL);
             break;
         case CVAR_INVALIDATEALL:
-            gltexinvalidatetype(INVALIDATE_ALL);
-            fallthrough__;
+			TileFiles.ClearTextureCache(false);
+			break;
         case CVAR_INVALIDATEART:
-            gltexinvalidatetype(INVALIDATE_ART);
+			TileFiles.ClearTextureCache(true);
             break;
         }
     }
diff --git a/source/build/src/polymost.cpp b/source/build/src/polymost.cpp
index ec8687e6d..6cc597dc3 100644
--- a/source/build/src/polymost.cpp
+++ b/source/build/src/polymost.cpp
@@ -17,7 +17,6 @@ Ken Silverman's official web site: http://www.advsys.net/ken
 #include "bitmap.h"
 #include "../../glbackend/glbackend.h"
 
-void cleartexturecache();
 
 extern char textfont[2048], smalltextfont[2048];
 
@@ -272,7 +271,7 @@ void polymost_glreset()
     }
     else
     {
-		cleartexturecache();
+		TileFiles.CleatTextureCache();
         clearskins(INVALIDATE_ALL);
     }
 
diff --git a/source/build/src/texcache.cpp b/source/build/src/texcache.cpp
index c9e4a6ab4..9b4b67c17 100644
--- a/source/build/src/texcache.cpp
+++ b/source/build/src/texcache.cpp
@@ -14,294 +14,10 @@
 #include "bitmap.h"
 #include "../../glbackend/glbackend.h"
 
+// External CVARs.
 extern int r_detailmapping, r_glowmapping, usehightile, r_useindexedcolortextures;
 extern int fixpalette, fixpalswap;
 
-#if 0
-
-void gltexinvalidate(int32_t dapicnum, int32_t dapalnum, int32_t dameth)
-{
-	const int32_t pic = (dapicnum & (GLTEXCACHEADSIZ - 1));
-
-	for (pthtyp* pth = texcache.list[pic]; pth; pth = pth->next)
-		if (pth->picnum == dapicnum && pth->palnum == dapalnum)
-		{
-			pth->flags |= PTH_INVALIDATED;
-			if (pth->flags & PTH_HASFULLBRIGHT)
-				pth->ofb->flags |= PTH_INVALIDATED;
-		}
-}
-
-//Make all textures "dirty" so they reload, but not re-allocate
-//This should be much faster than polymost_glreset()
-//Use this for palette effects ... but not ones that change every frame!
-void gltexinvalidatetype(int32_t type)
-{
-#if 0
-	for (bssize_t j = 0; j <= GLTEXCACHEADSIZ - 1; j++)
-	{
-		for (pthtyp* pth = texcache.list[j]; pth; pth = pth->next)
-		{
-			if (type == INVALIDATE_ALL ||
-				(type == INVALIDATE_ALL_NON_INDEXED && !(pth->flags & PTH_INDEXED)) ||
-				(type == INVALIDATE_ART && pth->hicr == NULL) ||
-				(type == INVALIDATE_ART_NON_INDEXED && pth->hicr == NULL && !(pth->flags & PTH_INDEXED)))
-			{
-				pth->flags |= PTH_INVALIDATED;
-				if (pth->flags & PTH_HASFULLBRIGHT)
-					pth->ofb->flags |= PTH_INVALIDATED;
-			}
-		}
-	}
-#endif
-
-	clearskins(type);
-
-#ifdef DEBUGGINGAIDS
-	OSD_Printf("gltexinvalidateall()\n");
-#endif
-}
-
-
-void cleartexturecache()
-{
-	for (bssize_t i = 0; i <= GLTEXCACHEADSIZ - 1; i++)
-	{
-		for (pthtyp* pth = texcache.list[i]; pth;)
-		{
-			pthtyp* const next = pth->next;
-
-			if (pth->flags & PTH_HASFULLBRIGHT)
-			{
-				delete pth->ofb->glpic;
-				Xfree(pth->ofb);
-			}
-
-			delete pth->glpic;
-			Xfree(pth);
-			pth = next;
-		}
-
-		texcache.list[i] = NULL;
-	}
-}
-
-static void polymost_setupsampler(FHardwareTexture* tex, const int32_t dameth, int filter)
-{
-
-	if (!(dameth & DAMETH_CLAMPED))
-	{
-		tex->SetSampler(SamplerRepeat);
-	}
-	else
-	{
-		// For sprite textures, clamping looks better than wrapping
-		tex->SetSampler(SamplerClampXY);
-	}
-}
-
-
-void gloadtile_art(int32_t dapic, int32_t dameth, pthtyp* pth, int32_t doalloc)
-{
-	vec2_16_t const& tsizart = tilesiz[dapic];
-	vec2_t siz = { tsizart.x, tsizart.y };
-	vec2_t ssiz = siz;
-	//POGOTODO: npoty
-	char npoty = 0;
-
-	tileLoad(globalpicnum);
-
-	const uint8_t* p = tilePtr(dapic);
-	if (!p)
-	{
-		static uint8_t pix = 255;
-		siz.x = siz.y = 1;
-
-		p = &pix;
-	}
-	{
-		if (doalloc)
-		{
-			assert(pth->glpic == nullptr);
-			pth->glpic = GLInterface.NewTexture();
-			pth->glpic->CreateTexture(siz.x, siz.y, true, false);
-			pth->glpic->SetSampler((dameth & DAMETH_CLAMPED) ? SamplerClampXY : SamplerRepeat);
-
-			polymost_setupsampler(pth->glpic, dameth, 0);
-		}
-		TArray<uint8_t> flipped(siz.x * siz.y, true);
-		FlipNonSquareBlock(flipped.Data(), p, siz.y, siz.x, siz.y);
-
-		pth->glpic->LoadTexture(flipped.Data());
-	}
-
-
-	pth->picnum = dapic;
-	pth->palnum = 0;
-	pth->shade = 0;
-	pth->effects = 0;
-	pth->flags = PTH_HASALPHA | PTH_ONEBITALPHA | PTH_INDEXED;
-	//pth->hicr = NULL;
-	pth->siz = ssiz;
-}
-
-
-#if 0
-int32_t gloadtile_hi(int32_t dapic, int32_t dapalnum, int32_t facen, hicreplctyp* hicr,
-	int32_t dameth, pthtyp* pth, int32_t doalloc, polytintflags_t effect)
-{
-	if (!hicr) return -1;
-
-	char* fn;
-
-	if (facen > 0)
-	{
-		if (!hicr->skybox || facen > 6 || !hicr->skybox->face[facen - 1])
-			return -1;
-
-		fn = hicr->skybox->face[facen - 1];
-	}
-	else
-	{
-		if (!hicr->filename)
-			return -1;
-
-		fn = hicr->filename;
-	}
-
-	auto texture = TileFiles.GetTexture(fn);
-
-	if (texture == nullptr)
-	{
-		OSD_Printf("hightile: %s (pic %d) not found\n", fn, dapic);
-		return -2;
-	}
-
-	if ((doalloc & 3) == 1)
-	{
-		pth->glpic = GLInterface.NewTexture();
-		pth->glpic->CreateTexture(texture->GetWidth(), texture->GetHeight(), false, true);
-	}
-	auto image = texture->GetBgraBitmap(nullptr, nullptr);
-	bool hasalpha = texture->GetTranslucency();
-	bool onebitalpha = texture->isMasked();
-
-	pth->glpic->LoadTexture(image);
-
-#if 0	// I don't really think this is a good idea. The hightile should look indistinguishable to the game compared to the regular one.
-	vec2_t tsiz = { texture->GetWidth(), texture->GetHeight() };
-	// precalculate scaling parameters for replacement
-	if (facen > 0)
-		pth->scale = { (float)tsiz.x * (1.0f / 64.f), (float)tsiz.y * (1.0f / 64.f) };
-	else
-		pth->scale = { (float)tsiz.x / (float)tilesiz[dapic].x, (float)tsiz.y / (float)tilesiz[dapic].y };
-#else
-	pth->scale = { 1.f,1.f };
-#endif
-
-	polymost_setupsampler(pth->glpic, dameth, (hicr->flags & HICR_FORCEFILTER) ? TEXFILTER_ON : -1);
-
-	pth->picnum = dapic;
-	pth->effects = effect;
-	pth->flags = PTH_HIGHTILE | ((facen > 0) * PTH_SKYBOX) |
-		(onebitalpha ? PTH_ONEBITALPHA : 0) |
-		(hasalpha ? PTH_HASALPHA : 0) |
-		((hicr->flags & HICR_FORCEFILTER) ? PTH_FORCEFILTER : 0);
-	pth->skyface = facen;
-	//pth->hicr = hicr;
-
-	if (facen > 0) pth->siz = { 64, 64 }; else pth->siz = { tilesiz[dapic].x, tilesiz[dapic].y };
-	return 0;
-}
-#endif
-
-#define TEXCACHE_FREEBUFS() { Xfree(pic), Xfree(packbuf), Xfree(midbuf); }
-
-globaltexcache texcache;
-
-
-
-
-// <dashade>: ignored if not in Polymost+r_usetileshades
-pthtyp *texcache_fetch(int32_t dapicnum, int32_t dapalnum, int32_t dashade, int32_t dameth)
-{
-#if 0
-    const int32_t j = dapicnum & (GLTEXCACHEADSIZ - 1);
-    hicreplctyp *si = usehightile ? hicfindsubst(dapicnum, dapalnum) : NULL;
-
-	if (drawingskybox && usehightile)
-	{
-		auto si = hicfindskybox(dapicnum, dapalnum);
-		if (si == nullptr)
-			return nullptr;
-	}
-
-    if (!si)
-#endif
-	{
-        return (dapalnum >= (MAXPALOOKUPS - RESERVEDPALS) || hicprecaching) ?
-                NULL : texcache_tryart(dapicnum, dapalnum, dashade, dameth);
-    }
-#if 0
-    /* if palette > 0 && replacement found
-     *    no effects are applied to the texture
-     * else if palette > 0 && no replacement found
-     *    effects are applied to the palette 0 texture if it exists
-     */
-
-    polytintflags_t const tintflags = hictinting[dapalnum].f;
-
-    const int32_t checktintpal = (tintflags & HICTINT_APPLYOVERALTPAL) ? 0 : si->palnum;
-    const int32_t checkcachepal = ((tintflags & HICTINT_IN_MEMORY) || ((tintflags & HICTINT_APPLYOVERALTPAL) && si->palnum > 0)) ? dapalnum : si->palnum;
-
-    // load a replacement
-    for (pthtyp *pth = texcache.list[j]; pth; pth = pth->next)
-    {
-        if (pth->picnum == dapicnum && pth->palnum == checkcachepal && (checktintpal > 0 ? 1 : (pth->effects == tintflags))
-            && (pth->flags & (PTH_HIGHTILE | PTH_SKYBOX)) == (PTH_HIGHTILE | (drawingskybox > 0) * PTH_SKYBOX)
-            && (drawingskybox > 0 ? (pth->skyface == drawingskybox) : 1))
-        {
-            if (pth->flags & PTH_INVALIDATED)
-            {
-                pth->flags &= ~PTH_INVALIDATED;
-
-				int32_t tilestat = gloadtile_hi(dapicnum, dapalnum, drawingskybox, si, dameth, pth, 0,
-                                        (checktintpal > 0) ? 0 : tintflags);  // reload tile
-
-                if (!tilestat)
-                    continue;
-
-                return (drawingskybox || hicprecaching) ? NULL : texcache_tryart(dapicnum, dapalnum, dashade, dameth);
-            }
-
-            return pth;
-        }
-    }
-
-    pthtyp *pth = (pthtyp *)Xcalloc(1, sizeof(pthtyp));
-
-    // possibly fetch an already loaded multitexture :_)
-    if (dapalnum == DETAILPAL && texcache_fetchmulti(pth, si, dapicnum, dameth))
-        return pth;
-
-    int32_t tilestat = gloadtile_hi(dapicnum, dapalnum, drawingskybox, si, dameth, pth, 1, (checktintpal > 0) ? 0 : tintflags);
-
-	if (!tilestat)
-    {
-        pth->next = texcache.list[j];
-        pth->palnum = checkcachepal;
-        texcache.list[j] = pth;
-        return pth;
-    }
-    Xfree(pth);
-
-    return (drawingskybox || hicprecaching) ? NULL : texcache_tryart(dapicnum, dapalnum, dashade, dameth);
-#endif
-
-}
-
-#endif
-
 
 template<class T>
 void FlipNonSquareBlock(T* dst, const T* src, int x, int y, int srcpitch)
@@ -322,7 +38,7 @@ void FlipNonSquareBlock(T* dst, const T* src, int x, int y, int srcpitch)
 //
 //===========================================================================
 
-FHardwareTexture* CreateIndexedTexture(FTexture* tex)
+FHardwareTexture* GLInstance::CreateIndexedTexture(FTexture* tex)
 {
 	auto siz = tex->GetSize();
 	bool npoty = false;
@@ -344,6 +60,41 @@ FHardwareTexture* CreateIndexedTexture(FTexture* tex)
 	return glpic;
 }
 
+//===========================================================================
+//
+//	Create a true color version of the requested tile
+//
+//===========================================================================
+
+FHardwareTexture* GLInstance::CreateTrueColorTexture(FTexture* tex, int palid, bool checkfulltransparency)
+{
+	auto siz = tex->GetSize();
+	bool npoty = false;
+
+	auto palette = palid < 0? nullptr : palmanager.GetPaletteData(palid);
+	auto texbuffer = tex->CreateTexBuffer(palette, CTF_ProcessData);
+	// Check if the texture is fully transparent. When creating a brightmap such textures can be discarded.
+	if (checkfulltransparency)
+	{
+		int siz = texbuffer.mWidth * texbuffer.mHeight * 4;
+		bool found = false;
+		for (int i = 3; i < siz; i+=4)
+		{
+			if (texbuffer.mBuffer[i] > 0)
+			{
+				found = true;
+				break;
+			}
+		}
+		if (!found) return nullptr;
+	}
+
+	auto glpic = GLInterface.NewTexture();
+	glpic->CreateTexture(texbuffer.mWidth, texbuffer.mHeight, false, true);
+	glpic->LoadTexture(texbuffer.mBuffer);
+	return glpic;
+}
+
 //===========================================================================
 // 
 //	Retrieve the texture to be used.
@@ -352,18 +103,18 @@ FHardwareTexture* CreateIndexedTexture(FTexture* tex)
 
 FHardwareTexture* GLInstance::LoadTexture(FTexture* tex, int textype, int palid)
 {
-	if (textype == TT_TRUECOLOR && palid == 0) textype = TT_HICREPLACE;	// true color tiles with the base palette won't get processed.
+	if (textype == TT_INDEXED) palid = -1;
+	auto phwtex = tex->GetHardwareTexture(palid);
+	if (phwtex) return *phwtex;
+
+	FHardwareTexture *hwtex;
 	if (textype == TT_INDEXED)
-	{
-		auto hwtex = tex->GetHardwareTexture(-1);
-		if (hwtex) return hwtex;
-		else
-		{
-			hwtex = CreateIndexedTexture(tex);
-			tex->SetHardwareTexture(-1, hwtex);
-			return hwtex;
-		}
-	}
+		auto hwtex = CreateIndexedTexture(tex);
+	else
+		auto hwtex = CreateTrueColorTexture(tex, textype == TT_HICREPLACE? -1 : palid, textype == TT_BRIGHTMAP);
+	
+	tex->SetHardwareTexture(palid, hwtex);
+	return hwtex;
 }
 
 //===========================================================================
@@ -394,7 +145,7 @@ bool GLInstance::SetTexture(FTexture* tex, int palette, int method, int samplero
 	else
 	{
 		// Only look up the palette if we really want to use it (i.e. when creating a true color texture of an ART tile.)
-		if (!r_useindexedcolortextures) lookuppal = palmanager.LookupPalette(usepalette, usepalswap);
+		if (!r_useindexedcolortextures) lookuppal = palmanager.LookupPalette(usepalette, usepalswap, false);
 	}
 
 	// Load the main texture
@@ -449,11 +200,29 @@ bool GLInstance::SetTexture(FTexture* tex, int palette, int method, int samplero
 
 			}
 		}
-		auto brep = currentTexture->FindReplacement(BRIGHTPAL);
-		if (brep)
+		if (!(tex->PicAnim.sf & PICANM_NOFULLBRIGHT_BIT) && !(globalflags & GLOBAL_NO_GL_FULLBRIGHT))
 		{
-			auto htex = LoadTexture(brep->faces[0], TT_HICREPLACE, 0);
-			BindTexture(5, mtex, sampler);
+			if (TextureType == TT_HICREPLACE)
+			{
+				auto brep = currentTexture->FindReplacement(BRIGHTPAL);
+				if (brep)
+				{
+					auto htex = LoadTexture(brep->faces[0], TT_HICREPLACE, 0);
+					// UseBrightmapping(true);
+					BindTexture(5, mtex, sampler);
+				}
+			}
+			else if (TextureType == TT_TRUECOLOR)
+			{
+				// Todo: brightmaps for true color tiles
+				lookuppal = palmanager.LookupPalette(usepalette, usepalswap, true);
+				if (lookuppal >= 0)
+				{
+					auto htex = LoadTexture(tex, TT_BRIGHTMAP, lookuppal);
+					// UseBrightmapping(true);
+					BindTexture(5, mtex, sampler);
+				}
+			}
 		}
 	}
 
diff --git a/source/common/textures/buildtiles.cpp b/source/common/textures/buildtiles.cpp
index 4d0898977..1420e8dd2 100644
--- a/source/common/textures/buildtiles.cpp
+++ b/source/common/textures/buildtiles.cpp
@@ -224,6 +224,52 @@ void BuildFiles::CloseAllMapArt()
 	PerMapArtFiles.DeleteAndClear();
 }
 
+//===========================================================================
+//
+// ClearTextureCache
+//
+// Deletes all hardware textures
+//
+//===========================================================================
+
+void BuildFiles::CleatTextureCache(bool artonly)
+{
+	for (auto tex : AllTiles)
+	{
+		tex->DeleteHardwareTextures();
+	}
+	for (auto tex : AllMapTiles)
+	{
+		tex->DeleteHardwareTextures();
+	}
+	if (!artonly)
+	{
+		decltype(textures)::Iterator it(textures);
+		decltype(textures)::Pair* pair;
+		while (it.NextPair(pair))
+		{
+			pair->Value->DeleteHardwareTextures();
+		}
+	}
+}
+
+
+void BuildFiles::InvalidateTile(int num)
+{
+	if ((unsigned) num < MAXTILES)
+	{
+		auto tex = tiles[num];
+		tex->DeleteHardwareTextures();
+		for (auto &rep : tex->Hightiles)
+		{
+			for (auto &reptex : rep.Faces)
+			{
+				if (reptex) reptex->DeleteHardwareTextures();
+			}
+		}
+	}
+}
+
 //===========================================================================
 //
 // LoadArtFile
diff --git a/source/common/textures/imagetexture.cpp b/source/common/textures/imagetexture.cpp
index 518edcfa1..03f1655ca 100644
--- a/source/common/textures/imagetexture.cpp
+++ b/source/common/textures/imagetexture.cpp
@@ -75,7 +75,7 @@ FBitmap FImageTexture::GetBgraBitmap(PalEntry *p, int *trans)
 	bmp.Create(Size.x, Size.y);
 	if (p == nullptr)
 	{
-		mImage->CopyPixels(&bmp, 0);	// Todo: Handle translations.
+		mImage->CopyPixels(&bmp, 0);
 	}
 	else 
 	{
diff --git a/source/common/textures/texture.cpp b/source/common/textures/texture.cpp
index 7c5e88b93..904aa41f9 100644
--- a/source/common/textures/texture.cpp
+++ b/source/common/textures/texture.cpp
@@ -288,7 +288,7 @@ bool FTexture::ProcessData(unsigned char * buffer, int w, int h, bool ispatch)
 //
 //===========================================================================
 
-FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
+FTextureBuffer FTexture::CreateTexBuffer(PalEntry * remap, int flags)
 {
 	FTextureBuffer result;
 
@@ -297,29 +297,19 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
 	int isTransparent = -1;
 	bool checkonly = !!(flags & CTF_CheckOnly);
 
-#if 0
-	if (flags & CTF_CheckHires)
-	{
-		// No image means that this cannot be checked,
-		if (GetImage() && LoadHiresTexture(result, checkonly)) return result;
-	}
-#endif
-	int exx = !!(flags & CTF_Expand);
-
-	W = GetWidth() + 2 * exx;
-	H = GetHeight() + 2 * exx;
+	W = GetWidth();
+	H = GetHeight();
 
 	if (!checkonly)
 	{
 		buffer = new unsigned char[W*(H + 1) * 4];
 		memset(buffer, 0, W * (H + 1) * 4);
 
-		auto remap = nullptr;// translation <= 0 ? nullptr : FUniquePalette::GetPalette(translation);
 		FBitmap bmp(buffer, W * 4, W, H);
 
 		int trans;
 		auto Pixels = GetBgraBitmap(remap, &trans);
-		bmp.Blit(exx, exx, Pixels);
+		bmp.Blit(0, 0, Pixels);
 
 		if (remap == nullptr)
 		{
@@ -333,27 +323,13 @@ FTextureBuffer FTexture::CreateTexBuffer(int translation, int flags)
 		}
 	}
 
-	if (GetImage())
-	{
-		FContentIdBuilder builder;
-		builder.id = 0;
-		builder.imageID = GetImage()->GetId();
-		builder.translation = std::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) 
+	if (flags & CTF_ProcessData)
 	{
-#if 0
-		CreateUpsampledTextureBuffer(result, !!isTransparent, checkonly);
-#endif
 		if (!checkonly) ProcessData(result.mBuffer, result.mWidth, result.mHeight, false);
 	}
 
@@ -447,24 +423,19 @@ HightileReplacement *FTexture::FindReplacement(int palnum, bool skybox)
     return nullptr;	// no replacement found
 }
 
-#if 0
-//==========================================================================
+//===========================================================================
 //
-// 
 //
-//==========================================================================
+//
+//===========================================================================
 
-FWrapperTexture::FWrapperTexture(int w, int h, int bits)
+void FTexture::DeleteHardwareTextures()
 {
-	Width = w;
-	Height = h;
-	Format = bits;
-	UseType = ETextureType::SWCanvas;
-	bNoCompress = true;
-	auto hwtex = screen->CreateHardwareTexture();
-	// todo: Initialize here.
-	SystemTextures.AddHardwareTexture(0, false, hwtex);
+	decltype(HardwareTextures)::Iterator it(HardwareTextures);
+	decltype(HardwareTextures)::Pair *pair;
+	while (it.NextPair(pair))
+	{
+		delete pair;
+	}
+	HardwareTextures.Clear();
 }
-
-#endif
-
diff --git a/source/common/textures/textures.h b/source/common/textures/textures.h
index 708d69602..3a0bf086a 100644
--- a/source/common/textures/textures.h
+++ b/source/common/textures/textures.h
@@ -162,7 +162,6 @@ struct FTextureBuffer
 	uint8_t *mBuffer = nullptr;
 	int mWidth = 0;
 	int mHeight = 0;
-	uint64_t mContentId = 0;	// unique content identifier. (Two images created from the same image source with the same settings will return the same value.)
 
 	FTextureBuffer() = default;
 
@@ -237,12 +236,13 @@ public:
 	int GetTopOffset() const { return PicAnim.yofs; }
 	picanm_t& GetAnim() { return PicAnim;  }	// This must be modifiable. There's quite a bit of code messing around with the flags in here.
 	rottile_t& GetRotTile() { return RotTile; }
-	FTextureBuffer CreateTexBuffer(int translation, int flags = 0);
+	FTextureBuffer CreateTexBuffer(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() {}
 	UseType GetUseType() const { return useType; }
+	void DeleteHardwareTextures();
 	void AddReplacement(const HightileReplacement &);
 	void DeleteReplacement(int palnum);
 	void DeleteReplacements()
@@ -254,10 +254,9 @@ public:
 	{
 		HardwareTextures.Insert(palid, htex);
 	}
-	FHardwareTexture* GetHardwareTexture(int palid)
+	FHardwareTexture** GetHardwareTexture(int palid)
 	{
-		auto k = HardwareTextures.CheckKey(palid);
-		return k ? *k : nullptr;
+		return HardwareTextures.CheckKey(palid);
 	}
 
 	HightileReplacement * FindReplacement(int palnum, bool skybox = false);
@@ -314,6 +313,7 @@ protected:
 	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.
 
 	FTexture (const char *name = NULL);
+	friend class BuildFiles;
 };
 
 class FTileTexture : public FTexture
@@ -548,6 +548,8 @@ struct BuildFiles
 	void tileSetExternal(int tilenum, int width, int height, uint8_t* data);
 	int findUnusedTile(void);
 	int tileCreateRotated(int owner);
+	void CleatTextureCache(bool artonly = false);
+	void InvalidateTile(int num);
 };
 
 int tileCRC(int tileNum);
@@ -621,6 +623,13 @@ inline rottile_t& RotTile(int tile)
 	assert(tile < MAXTILES);
 	return TileFiles.tiles[tile]->GetRotTile();
 }
+
+
+inline void tileInvalidate(int16_t tilenume, int32_t, int32_t)
+{
+	TileFiles.InvalidateTile(tilenume);
+}
+
 #endif
 
 
diff --git a/source/glbackend/gl_palmanager.cpp b/source/glbackend/gl_palmanager.cpp
index fed8a2cc6..b5661694c 100644
--- a/source/glbackend/gl_palmanager.cpp
+++ b/source/glbackend/gl_palmanager.cpp
@@ -257,7 +257,7 @@ void PaletteManager::BindPalswap(int index)
 }
 
 
-int PaletteManager::LookupPalette(int palette, int palswap)
+int PaletteManager::LookupPalette(int palette, int palswap, bool brightmap)
 {
 	int realpal = palettemap[palette];
 	int realswap = palswapmap[palswap];
diff --git a/source/glbackend/glbackend.h b/source/glbackend/glbackend.h
index 90742d8a2..5eaa95efe 100644
--- a/source/glbackend/glbackend.h
+++ b/source/glbackend/glbackend.h
@@ -82,7 +82,8 @@ public:
 	void BindPalette(int index);
 	void BindPalswap(int index);
 	int ActivePalswap() const { return lastsindex; }
-	int LookupPalette(int palette, int palswap);
+	int LookupPalette(int palette, int palswap, bool brightmap);
+	const PalEntry *GetPaletteData(int palid) const { return palettes[palid].colors; }
 };
 
 
@@ -193,7 +194,8 @@ enum ETexType
 {
 	TT_INDEXED,
 	TT_TRUECOLOR,
-	TT_HICREPLACE
+	TT_HICREPLACE,
+	TT_BRIGHTMAP
 };
 
 class GLInstance
@@ -378,6 +380,7 @@ public:
 	}
 	
 	FHardwareTexture* CreateIndexedTexture(FTexture* tex);
+	FHardwareTexture* CreateTrueColorTexture(FTexture* tex, int palid, bool checkfulltransparency = false);
 	FHardwareTexture *LoadTexture(FTexture* tex, int texturetype, int palid);
 	bool SetTexture(FTexture* tex, int palette, int method, int sampleroverride = -1);
 };