// "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 "../../glbackend/glbackend.h" uint8_t *basepaltable[MAXBASEPALS] = { palette }; uint8_t basepalreset=1; uint8_t curbasepal; int32_t globalblend; uint32_t g_lastpalettesum = 0; palette_t curpalette[256]; // the current palette, unadjusted for brightness or tint palette_t palfadergb = { 0, 0, 0, 0 }; unsigned char palfadedelta = 0; ESetPalFlags curpaletteflags; int32_t realmaxshade; float frealmaxshade; #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]; int32_t curbrightness = 0; void setBlendFactor(int index, int alpha); int DetermineTranslucency(const uint8_t *table) { uint8_t index; PalEntry newcolor; PalEntry newcolor2; index = table[blackcol * 256 + whitecol]; auto pp = &basepaltable[0][index]; newcolor = PalEntry(pp[0], pp[1], pp[2]); index = table[whitecol * 256 + blackcol]; pp = &basepaltable[0][index]; newcolor2 = PalEntry(pp[0], pp[1], pp[2]); if (newcolor2.r == 255) // if black on white results in white it's either // fully transparent or additive { return -newcolor.r; } return newcolor.r; } void fullscreen_tint_gl(PalEntry pe); static void alloc_palookup(int32_t pal) { // The asm functions vlineasm1, mvlineasm1 (maybe others?) access the next // palookup[...] shade entry for tilesizy==512 tiles. // See DEBUG_TILESIZY_512 and the comment in a.nasm: vlineasm1. palookup[pal] = (char *) Xaligned_alloc(16, (numshades + 1) * 256); memset(palookup[pal], 0, (numshades + 1) * 256); } static void maybe_alloc_palookup(int32_t palnum); void (*paletteLoadFromDisk_replace)(void) = NULL; inline bool read_and_test(FileReader& handle, void* buffer, int32_t leng) { return handle.Read(buffer, leng) != leng; }; // // loadpalette (internal) // void paletteLoadFromDisk(void) { #ifdef USE_OPENGL for (auto & x : glblend) x = defaultglblend; #endif if (paletteLoadFromDisk_replace) { paletteLoadFromDisk_replace(); return; } auto fil = fileSystem.OpenFileReader("palette.dat", 0); if (!fil.isOpen()) return; // PALETTE_MAIN if (768 != fil.Read(palette, 768)) return; for (unsigned char & k : palette) k <<= 2; paletteloaded |= PALETTE_MAIN; // PALETTE_SHADES if (2 != fil.Read(&numshades, 2)) return; numshades = B_LITTLE16(numshades); if (numshades <= 1) { Printf("Warning: Invalid number of shades in \"palette.dat\"!\n"); numshades = 0; return; } // Auto-detect LameDuke. Its PALETTE.DAT doesn't have a 'numshades' 16-bit // int after the base palette, but starts directly with the shade tables. // Thus, the first two bytes will be 00 01, which is 256 if read as // little-endian int16_t. int32_t lamedukep = 0; if (numshades >= 256) { uint16_t temp; if (read_and_test(fil, &temp, 2)) return; temp = B_LITTLE16(temp); if (temp == 770 || numshades > 256) // 02 03 { if (fil.Seek(-4, FileReader::SeekCur) < 0) { Printf("Warning: seek failed in loadpalette()!\n"); return; } numshades = 32; lamedukep = 1; } else { if (fil.Seek(-2, FileReader::SeekCur) < 0) { Printf("Warning: seek failed in loadpalette()!\n"); return; } } } // Read base shade table (palookup 0). maybe_alloc_palookup(0); if (read_and_test(fil, palookup[0], numshades<<8)) return; paletteloaded |= PALETTE_SHADE; paletteloaded |= PALETTE_TRANSLUC; // additional blending tables uint8_t magic[12]; if (!read_and_test(fil, magic, sizeof(magic)) && !Bmemcmp(magic, "MoreBlendTab", sizeof(magic))) { uint8_t addblendtabs; if (read_and_test(fil, &addblendtabs, 1)) { Printf("Warning: failed reading additional blending table count\n"); return; } uint8_t blendnum; char *tab = (char *) Xmalloc(256*256); for (bssize_t i=0; i= 1 && lognumalphatabs <= 7)) Printf("invalid lognumalphatabs value, must be in [1 .. 7]\n"); else numalphatabs = 1< 0; --s) { for (size_t c = s*256, c_end = c+255; c < c_end; ++c) // skipping transparent color { uint8_t const index = palookup0[c]; uint8_t const * const color = &palette[index*3]; if (!IsPaletteIndexFullbright(index) && memcmp(blackcolor, color, sizeof(rgb24_t))) goto PostLoad_FoundShade; } } PostLoad_FoundShade: ; frealmaxshade = (float)(realmaxshade = s+1); } } void paletteFixTranslucencyMask(void) { for (auto thispalookup : palookup) { if (thispalookup == NULL) continue; for (bssize_t j=0; j: open file handle // // Returns: // - on success, 0 // - on error, -1 (didn't read enough data) // - -2: error, we already wrote an error message ourselves int32_t paletteLoadLookupTable(FileReader &fp) { uint8_t numlookups; char remapbuf[256]; if (1 != fp.Read(&numlookups, 1)) return -1; for (bssize_t j=0; j= 256-RESERVEDPALS) { Printf("ERROR: attempt to load lookup at reserved pal %d\n", palnum); return -2; } if (256 != fp.Read(remapbuf, 256)) return -1; paletteMakeLookupTable(palnum, remapbuf, 0, 0, 0, 0); } return 0; } void paletteSetupDefaultFog(void) { // Find a gap of four consecutive unused pal numbers to generate fog shade // tables. for (bssize_t j=1; j<=255-3; j++) if (!palookup[j] && !palookup[j+1] && !palookup[j+2] && !palookup[j+3]) { paletteMakeLookupTable(j, NULL, 60, 60, 60, 1); 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; } } void palettePostLoadLookups(void) { // Alias remaining unused pal numbers to the base shade table. for (bssize_t j=1; j= 0 && index < MAXBLENDTABS) { auto& myblend = glblend[index]; if (index >= 0) { myblend.def[0].alpha = index / 255.f; myblend.def[1].alpha = 1.f - (index / 255.f); myblend.def[0].src = myblend.def[1].src = BLENDFACTOR_ONE_MINUS_SRC_ALPHA; myblend.def[0].dst = myblend.def[1].dst = BLENDFACTOR_ONE_MINUS_SRC_ALPHA; } else { myblend.def[0].alpha = 1; myblend.def[1].alpha = 1; myblend.def[0].src = myblend.def[1].src = BLENDFACTOR_ONE; myblend.def[0].dst = myblend.def[1].dst = BLENDFACTOR_ONE; } } } FRenderStyle GetBlend(int blend, int def) { static uint8_t const blendFuncTokens[NUMBLENDFACTORS] = { STYLEALPHA_Zero, STYLEALPHA_One, STYLEALPHA_SrcCol, STYLEALPHA_InvSrcCol, STYLEALPHA_Src, STYLEALPHA_InvSrc, STYLEALPHA_Dst, STYLEALPHA_InvDst, STYLEALPHA_DstCol, STYLEALPHA_InvDstCol, }; FRenderStyle rs; rs.BlendOp = STYLEOP_Add; glblenddef_t const* const glbdef = glblend[blend].def + def; rs.SrcAlpha = blendFuncTokens[glbdef->src]; rs.DestAlpha = blendFuncTokens[glbdef->dst]; rs.Flags = 0; return rs; } void handle_blend(uint8_t enable, uint8_t blend, uint8_t def) { if (!enable) { GLInterface.SetRenderStyle(LegacyRenderStyles[STYLE_Translucent]); return; } auto rs = GetBlend(blend, def); GLInterface.SetRenderStyle(rs); } float float_trans(uint32_t maskprops, uint8_t blend) { switch (maskprops) { case DAMETH_TRANS1: case DAMETH_TRANS2: return glblend[blend].def[maskprops - 2].alpha; default: return 1.0f; } } #endif int32_t paletteSetLookupTable(int32_t palnum, const uint8_t *shtab) { if (shtab != NULL) { maybe_alloc_palookup(palnum); Bmemcpy(palookup[palnum], shtab, 256*numshades); } return 0; } void paletteFreeLookupTable(int32_t const palnum) { if (palnum == 0 && palookup[palnum] != NULL) { for (bssize_t i = 1; i < MAXPALOOKUPS; i++) if (palookup[i] == palookup[palnum]) palookup[i] = NULL; ALIGNED_FREE_AND_NULL(palookup[palnum]); } else if (palookup[palnum] == palookup[0]) palookup[palnum] = NULL; else ALIGNED_FREE_AND_NULL(palookup[palnum]); } // // makepalookup // void paletteMakeLookupTable(int32_t palnum, const char *remapbuf, uint8_t r, uint8_t g, uint8_t b, char noFloorPal) { int32_t i, j; static char idmap[256] = { 1 }; if (paletteloaded == 0) return; // NOTE: palnum==0 is allowed if ((unsigned) palnum >= MAXPALOOKUPS) return; g_noFloorPal[palnum] = noFloorPal; if (remapbuf==NULL) { if ((r|g|b) == 0) { palookup[palnum] = palookup[0]; // Alias to base shade table! return; } if (idmap[0]==1) // init identity map for (i=0; i<256; i++) idmap[i] = i; remapbuf = idmap; } maybe_alloc_palookup(palnum); if ((r|g|b) == 0) { // "black fog"/visibility case -- only remap color indices for (j=0; j= REND_POLYMOST) { uploadbasepalette(id); } #endif } void paletteFreeColorTable(int32_t const id) { if (id == 0) Bmemset(basepaltable[id], 0, 768); else DO_FREE_AND_NULL(basepaltable[id]); } void paletteFreeColorTables() { for (int i = 0; i < countof(basepaltable); i++) { paletteFreeColorTable(i); } } // // setbrightness // // flags: // 1: don't setpalette(), not checked anymore. // 2: don't gltexinvalidateall() // 4: don't calc curbrightness from dabrightness, DON'T USE THIS FLAG! // 8: don't gltexinvalidate8() // 16: don't reset palfade* // 32: apply brightness to scene in OpenGL void videoSetPalette(int dabrightness, int dapalid, ESetPalFlags flags) { int32_t i; if (/*(unsigned)dapalid >= MAXBASEPALS ||*/ basepaltable[dapalid] == NULL) dapalid = 0; curbasepal = dapalid; basepalreset = 0; auto dapal = basepaltable[curbasepal]; // In-scene brightness mode for RR's thunderstorm. This shouldn't affect the global gamma ramp. if ((videoGetRenderMode() >= REND_POLYMOST) && (flags & Pal_SceneBrightness)) { r_scenebrightness = clamp(dabrightness, 0, 15); } else { r_scenebrightness = 0; } for (i = 0; i < 256; i++) { // save palette without any brightness adjustment curpalette[i].r = dapal[i * 3 + 0]; curpalette[i].g = dapal[i * 3 + 1]; curpalette[i].b = dapal[i * 3 + 2]; curpalette[i].f = 0; } if ((flags & Pal_DontResetFade) == 0) { palfadergb.r = palfadergb.g = palfadergb.b = 0; palfadedelta = 0; } curpaletteflags = flags; } palette_t paletteGetColor(int32_t col) { return curpalette[col]; } // // setpalettefade // void videoFadePalette(uint8_t r, uint8_t g, uint8_t b, uint8_t offset) { palfadergb.r = r; palfadergb.g = g; palfadergb.b = b; palfadedelta = offset; }