raze/source/build/src/palette.cpp

432 lines
12 KiB
C++
Raw Normal View History

// "Build Engine & Tools" Copyright (c) 1993-1997 Ken Silverman
// Ken Silverman's official web site: "http://www.advsys.net/ken"
// See the included license file "BUILDLIC.TXT" for license info.
//
// This file has been modified from Ken Silverman's original release
// by Jonathon Fowler (jf@jonof.id.au)
// by the EDuke32 team (development@voidpoint.com)
#include "compat.h"
#include "build.h"
#include "engine_priv.h"
#include "baselayer.h"
#include "imagehelpers.h"
#include "palette.h"
#include "superfasthash.h"
#include "common.h"
#include "memarena.h"
#include "palettecontainer.h"
#include "palutil.h"
#include "colormatcher.h"
2020-05-23 12:40:54 +00:00
#include "m_swap.h"
#include "v_colortables.h"
#include "v_font.h"
#include "../../glbackend/glbackend.h"
2020-05-23 12:40:54 +00:00
// FString is a nice and convenient way to have automatically managed shared storage.
FString LookupTables[MAXPALOOKUPS];
uint8_t curbasepal;
int32_t globalblend;
2020-05-23 12:40:54 +00:00
PalEntry palfadergb;
#if defined(USE_OPENGL)
palette_t palookupfog[MAXPALOOKUPS];
#endif
// For every pal number, whether tsprite pal should not be taken over from
// floor pal.
// NOTE: g_noFloorPal[0] is irrelevant as it's never checked.
int8_t g_noFloorPal[MAXPALOOKUPS];
2020-05-23 12:40:54 +00:00
//==========================================================================
//
// Adds a palette to the global list of base palettes
//
//==========================================================================
2020-05-23 12:40:54 +00:00
void paletteSetColorTable(int32_t id, uint8_t const* table, bool notransparency, bool twodonly)
{
if (id == 0)
{
GPalette.SetPalette(table, 255);
GPalette.BaseColors[255] = 0;
BuildTransTable(GPalette.BaseColors);
}
FRemapTable remap;
remap.AddColors(0, 256, table, 255);
if (!notransparency)
{
remap.Palette[255] = 0;
remap.Remap[255] = 255;
}
2020-05-23 12:40:54 +00:00
remap.Inactive = twodonly; // use Inactive as a marker for the postprocessing so that for pure 2D palettes the creation of shade tables can be skipped.
GPalette.UpdateTranslation(TRANSLATION(Translation_BasePalettes, id), &remap);
}
2020-05-23 12:40:54 +00:00
//==========================================================================
//
2020-05-23 12:40:54 +00:00
// loads the main palette file.
//
2020-05-23 12:40:54 +00:00
//==========================================================================
void paletteLoadFromDisk(void)
{
for (auto & x : glblend)
x = defaultglblend;
2020-04-11 21:54:33 +00:00
auto fil = fileSystem.OpenFileReader("palette.dat");
if (!fil.isOpen())
return;
// PALETTE_MAIN
uint8_t palette[768];
if (768 != fil.Read(palette, 768))
return;
for (unsigned char & k : palette)
k <<= 2;
2020-05-23 12:40:54 +00:00
paletteSetColorTable(0, palette, false, false);
paletteloaded |= PALETTE_MAIN;
// PALETTE_SHADES
2020-05-23 12:40:54 +00:00
numshades = fil.ReadInt16();
if (numshades <= 1)
{
Printf("Warning: Invalid number of shades in \"palette.dat\"!\n");
numshades = 0;
return;
}
2020-05-23 12:40:54 +00:00
#if 0
// Reminder: Witchaven's shade table has no index and no easy means to autodetect.
if (numshades == 0 && (g_gameType & GAMEFLAG_WITCHAVEN))
{
2020-05-23 12:40:54 +00:00
numshades = 32;
fil.Seek(-2, FileReader::SeekCur);
}
else
#endif
{
// LameDuke's is yet another variant.
if (numshades >= 256)
{
2020-05-23 12:40:54 +00:00
uint16_t temp = fil.ReadUInt16();
if (temp == 770 || numshades > 256) // 02 03
{
2020-05-23 12:40:54 +00:00
fil.Seek(-4, FileReader::SeekCur);
numshades = 32;
}
2020-05-23 12:40:54 +00:00
else
{
2020-05-23 12:40:54 +00:00
fil.Seek(-2, FileReader::SeekCur);
}
}
}
2020-05-23 12:40:54 +00:00
// Read base shade table (lookuptables 0).
2020-05-23 12:40:54 +00:00
int length = numshades * 256;
auto buffer = fil.Read(length);
if (buffer.Size() != length) return;
LookupTables[0] = FString((char*)buffer.Data(), length);
paletteloaded |= PALETTE_SHADE;
paletteloaded |= PALETTE_TRANSLUC;
}
2020-05-23 12:40:54 +00:00
//==========================================================================
//
// postprocess the palette data after everything has been loaded
//
//==========================================================================
void palettePostLoadTables(void)
{
globalpal = 0;
GPalette.GenerateGlobalBrightmapFromColormap(paletteGetLookupTable(0), numshades);
}
2020-05-23 12:40:54 +00:00
//==========================================================================
//
// Ensure that all lookups map 255 to itself to preserve transparency.
//
//==========================================================================
void paletteFixTranslucencyMask(void)
{
2020-05-23 12:40:54 +00:00
for (auto &thispalookup : LookupTables)
{
2020-05-23 12:40:54 +00:00
if (thispalookup.IsEmpty())
continue;
2020-05-23 12:40:54 +00:00
for (int j = 0; j < numshades; j++)
{
auto p = thispalookup.LockBuffer();
p[(j << 8) + 255] = 255;
thispalookup.UnlockBuffer();
}
}
}
2020-05-23 12:40:54 +00:00
//==========================================================================
//
2020-05-23 12:40:54 +00:00
// load the lookup tables from lookup.dat
//
2020-05-23 12:40:54 +00:00
//==========================================================================
2019-10-20 20:26:53 +00:00
int32_t paletteLoadLookupTable(FileReader &fp)
{
uint8_t remapbuf[256];
2020-05-23 12:40:54 +00:00
int numlookups = fp.ReadUInt8();
if (numlookups < 1)
return -1;
2020-05-23 12:40:54 +00:00
for (int j=0; j<numlookups; j++)
{
2020-05-23 12:40:54 +00:00
int palnum = fp.ReadUInt8();
2020-05-23 12:40:54 +00:00
if (256 != fp.Read(remapbuf, 256))
return -1;
2020-05-23 12:40:54 +00:00
if (palnum >= 256 - RESERVEDPALS)
{
Printf("ERROR: attempt to load lookup at reserved pal %d\n", palnum);
}
2020-05-23 12:40:54 +00:00
else
paletteMakeLookupTable(palnum, remapbuf, 0, 0, 0, 0);
}
return 0;
}
2020-05-23 12:40:54 +00:00
//==========================================================================
//
// Find a gap of four consecutive unused pal numbers to generate fog shade tables.
//
//==========================================================================
void paletteSetupDefaultFog(void)
{
2020-05-23 12:40:54 +00:00
for (int j = 1; j <= 255 - 3; j++)
{
if (LookupTables[j].IsEmpty() && LookupTables[j + 1].IsEmpty() && LookupTables[j + 2].IsEmpty() && LookupTables[j + 3].IsEmpty())
{
paletteMakeLookupTable(j, NULL, 60, 60, 60, 1);
2020-05-23 12:40:54 +00:00
paletteMakeLookupTable(j + 1, NULL, 60, 0, 0, 1);
paletteMakeLookupTable(j + 2, NULL, 0, 60, 0, 1);
paletteMakeLookupTable(j + 3, NULL, 0, 0, 60, 1);
break;
}
}
}
2020-05-23 12:40:54 +00:00
//==========================================================================
//
// post process the lookup tables once everything has been loaded
//
//==========================================================================
2020-05-23 12:40:54 +00:00
void palettePostLoadLookups(void)
{
2020-05-23 12:40:54 +00:00
int numpalettes = GPalette.NumTranslations(Translation_BasePalettes);
if (numpalettes == 0) return;
auto basepalette = GPalette.GetTranslation(Translation_BasePalettes, 0);
2020-05-23 12:40:54 +00:00
for (int l = 0; l < MAXPALOOKUPS; l++)
{
2020-05-23 12:40:54 +00:00
if (!LookupTables[l].IsEmpty())
{
const uint8_t* lookup = paletteGetLookupTable(l);
2020-05-23 12:40:54 +00:00
FRemapTable remap;
for (int i = 0; i < numpalettes; i++)
{
auto palette = GPalette.GetTranslation(Translation_BasePalettes, i);
if (!palette) continue;
if (i == 0 || (palette != basepalette && !palette->Inactive))
{
memcpy(remap.Remap, lookup, 256);
for (int j = 0; j < 256; j++)
{
remap.Palette[j] = palette->Palette[remap.Remap[j]];
}
remap.NumEntries = 256;
GPalette.UpdateTranslation(TRANSLATION(i + Translation_Remap, l), &remap);
2020-05-23 12:40:54 +00:00
}
if (palette != basepalette) palette->Inactive = false; // clear the marker flag
}
}
}
// Swap colors 0 and 255 in all tables so that all paletted images have their transparent color at index 0.
// This means:
// - Swap palette and remap entries in all stored remap tables
// - change all remap entries of 255 to 0 and vice versa
auto colorswap = [](FRemapTable* remap)
{
std::swap(remap->Palette[0], remap->Palette[255]);
std::swap(remap->Remap[0], remap->Remap[255]);
for (auto& c : remap->Remap)
{
if (c == 0) c = 255;
else if (c == 255) c = 0;
}
};
for (auto remap : GPalette.uniqueRemaps)
{
if (!remap->ForFont) colorswap(remap);
}
colorswap(&GPalette.GlobalBrightmap);
std::swap(GPalette.BaseColors[0], GPalette.BaseColors[255]);
}
2020-05-23 12:40:54 +00:00
//==========================================================================
//
// set a lookup table from external data
//
//==========================================================================
int32_t paletteSetLookupTable(int32_t palnum, const uint8_t *shtab)
{
if (shtab != NULL)
{
2020-05-23 12:40:54 +00:00
int length = numshades * 256;
LookupTables[palnum] = FString((const char*)shtab, length);
}
return 0;
}
2020-05-23 12:40:54 +00:00
//==========================================================================
//
2020-05-23 12:40:54 +00:00
// creates a lookup table from scratch
//
2020-05-23 12:40:54 +00:00
//==========================================================================
void paletteMakeLookupTable(int32_t palnum, const uint8_t *remapbuf, uint8_t r, uint8_t g, uint8_t b, char noFloorPal)
{
uint8_t idmap[256];
// NOTE: palnum==0 is allowed
2020-05-23 12:40:54 +00:00
if (paletteloaded == 0 || (unsigned)palnum >= MAXPALOOKUPS)
return;
g_noFloorPal[palnum] = noFloorPal;
2020-05-23 12:40:54 +00:00
if (remapbuf == nullptr)
{
2020-05-23 12:40:54 +00:00
if (r == 0 || g == 0 || b == 0)
{
paletteClearLookupTable(palnum);
return;
}
2020-05-23 12:40:54 +00:00
for (int i = 0; i < 256; i++) idmap[i] = i;
remapbuf = idmap;
}
2020-05-23 12:40:54 +00:00
int length = numshades * 256;
auto p = LookupTables[palnum].LockNewBuffer(length);
if (r == 0 || g == 0 || b == 0)
{
// "black fog"/visibility case -- only remap color indices
auto src = paletteGetLookupTable(0);
2020-05-23 12:40:54 +00:00
for (int j = 0; j < numshades; j++)
for (int i = 0; i < 256; i++)
{
2020-05-23 12:40:54 +00:00
p[256 * j + i] = src[256 * j + remapbuf[i]];
}
}
else
{
// colored fog case
2020-05-23 12:40:54 +00:00
for (int i = 0; i < numshades; i++)
{
2020-05-23 12:40:54 +00:00
for (int j = 0; j < 256; j++)
{
PalEntry pe = GPalette.BaseColors[remapbuf[j]];
2020-05-23 12:40:54 +00:00
p[j] = ColorMatcher.Pick(
pe.r + Scale(r - pe.r, i, numshades - 1),
pe.g + Scale(g - pe.g, i, numshades - 1),
pe.b + Scale(b - pe.b, i, numshades - 1));
}
}
}
#if defined(USE_OPENGL)
palookupfog[palnum].r = r;
palookupfog[palnum].g = g;
palookupfog[palnum].b = b;
palookupfog[palnum].f = 1;
#endif
}
2020-05-23 12:40:54 +00:00
void videoSetPalette(int dabrightness, int palid, ESetPalFlags flags)
{
curbasepal = (GPalette.GetTranslation(Translation_BasePalettes, palid) == nullptr)? 0 : palid;
if ((flags & Pal_DontResetFade) == 0) palfadergb = 0;
}
//==========================================================================
//
2020-05-23 12:40:54 +00:00
// map Build blend definitions to actual render style / alpha combos.
//
2020-05-23 12:40:54 +00:00
//==========================================================================
glblend_t const nullglblend =
{
2020-05-23 12:40:54 +00:00
{
{ 1.f, STYLEALPHA_One, STYLEALPHA_Zero, 0 },
{ 1.f, STYLEALPHA_One, STYLEALPHA_Zero, 0 },
},
};
glblend_t const defaultglblend =
{
{
{ 2.f / 3.f, STYLEALPHA_Src, STYLEALPHA_InvSrc, 0 },
{ 1.f / 3.f, STYLEALPHA_Src, STYLEALPHA_InvSrc, 0 },
},
};
2020-05-23 12:40:54 +00:00
glblend_t glblend[MAXBLENDTABS];
2020-05-23 12:40:54 +00:00
FRenderStyle GetRenderStyle(int blend, int def)
{
FRenderStyle rs;
rs.BlendOp = STYLEOP_Add;
auto glbdef = &glblend[blend].def[def];
rs.SrcAlpha = glbdef->src;
rs.DestAlpha = glbdef->dst;
rs.Flags = 0;
return rs;
}
2020-05-23 12:40:54 +00:00
void SetRenderStyleFromBlend(uint8_t enable, uint8_t blend, uint8_t def)
{
2020-05-23 12:40:54 +00:00
if (!enable)
{
GLInterface.SetRenderStyle(LegacyRenderStyles[STYLE_Translucent]);
return;
}
auto rs = GetRenderStyle(blend, def);
GLInterface.SetRenderStyle(rs);
}
2020-04-12 05:44:55 +00:00
2020-05-23 12:40:54 +00:00
float GetAlphaFromBlend(uint32_t method, uint32_t blend)
2020-04-12 05:44:55 +00:00
{
2020-05-23 12:40:54 +00:00
return method == DAMETH_TRANS1 || method == DAMETH_TRANS2 ? glblend[blend].def[method - DAMETH_TRANS1].alpha : 1.f;
}