// "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 "colmatch.h" #include "cache1d.h" #include "palette.h" #include "a.h" #include "xxhash.h" #include "../../glbackend/glbackend.h" #include "vfs.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 curpalettefaded[256]; // the current palette, adjusted for brightness and tint (ie. what gets sent to the card) palette_t palfadergb = { 0, 0, 0, 0 }; char palfadedelta = 0; uint8_t blackcol; 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, gammabrightness = 0; static void paletteSetFade(uint8_t offset); #ifdef USE_OPENGL void fullscreen_tint_gl(uint8_t r, uint8_t g, uint8_t b, uint8_t f) { glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glDisable(GL_DEPTH_TEST); glDisable(GL_ALPHA_TEST); polymost_setFogEnabled(false); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); glColor4ub(r, g, b, f); polymost_useColorOnly(true); auto data = GLInterface.AllocVertices(3); auto vt = data.second; vt[0].Set(-2.5f, 1.f); vt[1].Set(2.5f, 1.f); vt[2].Set(.0f, -2.5f); GLInterface.Draw(DT_TRIANGLES, data.first, 3); polymost_useColorOnly(false); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } #endif void videoFadeToBlack(int32_t moreopaquep) { #ifdef USE_OPENGL if (videoGetRenderMode() >= REND_POLYMOST) fullscreen_tint_gl(0, 0, 0, moreopaquep ? 168 : 84); else #endif { Bassert(!offscreenrendering); videoBeginDrawing(); char *const p = (char *) frameplace; const char *const trans = paletteGetBlendTable(0); const int32_t shiftamnt = ((!!moreopaquep)*8); const int32_t dimprod = xdim*ydim; int32_t i = 0; #ifdef CLASSIC_SLICE_BY_4 for (; i= 256) { static char const * const seekfail = "Warning: klseek() failed in loadpalette()!\n"; uint16_t temp; if (kread_and_test(fil, &temp, 2)) return kclose(fil); temp = B_LITTLE16(temp); if (temp == 770 || numshades > 256) // 02 03 { if (klseek(fil, -4, BSEEK_CUR) < 0) { initputs(seekfail); return kclose(fil); } numshades = 32; lamedukep = 1; } else { if (klseek(fil, -2, BSEEK_CUR) < 0) { initputs(seekfail); return kclose(fil); } } } // Read base shade table (palookup 0). maybe_alloc_palookup(0); if (kread_and_test(fil, palookup[0], numshades<<8)) return kclose(fil); paletteloaded |= PALETTE_SHADE; // PALETTE_TRANSLUC char * const transluc = blendtable[0] = (char *) Xcalloc(256, 256); // Read translucency (blending) table. if (lamedukep) { for (bssize_t i=0; i<255; i++) { // NOTE: LameDuke's table doesn't have the last row or column (i==255). // Read the entries above and on the diagonal, if the table is // thought as being row-major. if (kread_and_test(fil, &transluc[256*i + i], 256-i-1)) return kclose(fil); // Duplicate the entries below the diagonal. for (bssize_t j=0; j= 1 && lognumalphatabs <= 7)) initprintf("invalid lognumalphatabs value, must be in [1 .. 7]\n"); else numalphatabs = 1<b, edcol->g, edcol->r, 239); } // Bmemset(PaletteIndexFullbrights, 0, sizeof(PaletteIndexFullbrights)); for (bssize_t c = 0; c < 255; ++c) // skipping transparent color { uint8_t const index = palookup0[c]; rgb24_t const & color = *(rgb24_t *)&palette[index*3]; // don't consider #000000 fullbright if (EDUKE32_PREDICT_FALSE(color.r == 0 && color.g == 0 && color.b == 0)) continue; for (size_t s = c + 256, s_end = 256*numshades; s < s_end; s += 256) if (EDUKE32_PREDICT_FALSE(palookup0[s] != index)) goto PostLoad_NotFullbright; SetPaletteIndexFullbright(c); PostLoad_NotFullbright: ; } if (realmaxshade == 0) { uint8_t const * const blackcolor = &palette[blackcol*3]; size_t s; for (s = numshades < 2 ? 0 : numshades-2; s > 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: kopen4load 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(buildvfs_kfd fp) { uint8_t numlookups; char remapbuf[256]; if (kread_and_test(fp, &numlookups, 1)) return -1; for (bssize_t j=0; j= 256-RESERVEDPALS) { initprintf("ERROR: attempt to load lookup at reserved pal %d\n", palnum); return -2; } if (kread_and_test(fp, 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; jsrc], blendFuncTokens[glbdef->dst]); } #endif int32_t paletteSetLookupTable(int32_t palnum, const uint8_t *shtab) { if (numshades != 32) return -1; 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]); } // // setbrightness // // flags: // 1: don't setpalette(), DON'T USE THIS FLAG! // 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* void videoSetPalette(char dabrightness, uint8_t dapalid, uint8_t flags) { int32_t i, j; const uint8_t *dapal; #ifdef USE_OPENGL int32_t paldidchange; #endif int32_t palsumdidchange; // uint32_t lastbright = curbrightness; Bassert((flags&4)==0); if (/*(unsigned)dapalid >= MAXBASEPALS ||*/ basepaltable[dapalid] == NULL) dapalid = 0; #ifdef USE_OPENGL paldidchange = (curbasepal != dapalid || basepalreset); #endif curbasepal = dapalid; basepalreset = 0; dapal = basepaltable[curbasepal]; if (!(flags&4)) { curbrightness = clamp(dabrightness, 0, 15); // if (lastbright != (unsigned)curbrightness) // vid_gamma = 1.0 + ((float)curbrightness / 10.0); } videoSetGamma(); j = (!gammabrightness || (flags&32) != 0) ? curbrightness : 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; // brightness adjust the palette curpalettefaded[i].b = britable[j][curpalette[i].b]; curpalettefaded[i].g = britable[j][curpalette[i].g]; curpalettefaded[i].r = britable[j][curpalette[i].r]; curpalettefaded[i].f = 0; } #ifdef USE_OPENGL if ((flags&32) != 0 && videoGetRenderMode() == REND_POLYMOST) r_brightnesshack = j; else r_brightnesshack = 0; #endif if ((flags&16) && palfadedelta) // keep the fade paletteSetFade(palfadedelta>>2); static uint32_t lastpalettesum=0; uint32_t newpalettesum = XXH32((uint8_t *) curpalettefaded, sizeof(curpalettefaded), sizeof(curpalettefaded)); palsumdidchange = (newpalettesum != lastpalettesum); if (palsumdidchange || newpalettesum != g_lastpalettesum) { // if ((flags&1) == 0) videoUpdatePalette(0, 256); } g_lastpalettesum = lastpalettesum = newpalettesum; #ifdef USE_OPENGL if (videoGetRenderMode() >= REND_POLYMOST) { // Only reset the textures if the corresponding preserve flags are clear and // either (a) the new palette is different to the last, or (b) the brightness // changed and we couldn't set it using hardware gamma. // XXX: no-HW-gamma OpenGL platforms will exhibit bad performance with // simultaneous basepal and tint changes? const int32_t doinvalidate = (paldidchange || (palsumdidchange && !gammabrightness)); if (!(flags&2) && doinvalidate) gltexinvalidatetype(INVALIDATE_ALL_NON_INDEXED); if (!(flags&8) && doinvalidate) gltexinvalidatetype(INVALIDATE_ART_NON_INDEXED); #ifdef POLYMER if ((videoGetRenderMode() == REND_POLYMER) && doinvalidate) polymer_texinvalidate(); #endif } #endif if ((flags&16)==0) { palfadergb.r = palfadergb.g = palfadergb.b = 0; palfadedelta = 0; } } palette_t paletteGetColor(int32_t col) { if (!gammabrightness) { palette_t const p = { britable[curbrightness][curpalette[col].r], britable[curbrightness][curpalette[col].g], britable[curbrightness][curpalette[col].b], 0 }; return p; } return curpalette[col]; } static void paletteSetFade(uint8_t offset) { for (native_t i=0; i<256; i++) { palette_t const p = paletteGetColor(i); curpalettefaded[i].b = p.b + (((palfadergb.b - p.b) * offset) >> 8); curpalettefaded[i].g = p.g + (((palfadergb.g - p.g) * offset) >> 8); curpalettefaded[i].r = p.r + (((palfadergb.r - p.r) * offset) >> 8); curpalettefaded[i].f = 0; } } //#define DEBUG_PALETTEFADE // // setpalettefade // void videoFadePalette(uint8_t r, uint8_t g, uint8_t b, uint8_t offset) { palfadergb.r = r; palfadergb.g = g; palfadergb.b = b; #ifdef DEBUG_PALETTEFADE if (offset) offset = max(offset, 128); #endif palfadedelta = offset; paletteSetFade(offset); static uint32_t lastpalettesum=0; uint32_t newpalettesum = XXH32((uint8_t *) curpalettefaded, sizeof(curpalettefaded), sizeof(curpalettefaded)); if (newpalettesum != lastpalettesum || newpalettesum != g_lastpalettesum) { videoUpdatePalette(0, 256); } g_lastpalettesum = lastpalettesum = newpalettesum; }