diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 83a4e2e03..99dba2c61 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -32,6 +32,14 @@ INT32 patchformat = GL_TEXFMT_AP_88; // use alpha for holes INT32 textureformat = GL_TEXFMT_P_8; // use chromakey for hole +RGBA_t mapPalette[256] = {0}; // the palette for the currently loaded level or menu etc. + +// Returns a pointer to the palette which should be used for caching textures. +static RGBA_t *HWR_GetTexturePalette(void) +{ + return HWR_ShouldUsePaletteRendering() ? mapPalette : pLocalPalette; +} + static INT32 format2bpp(GLTextureFormat_t format) { if (format == GL_TEXFMT_RGBA) @@ -49,7 +57,7 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm INT32 pblockheight, INT32 blockmodulo, fixed_t yfracstep, fixed_t scale_y, texpatch_t *originPatch, INT32 patchheight, - INT32 bpp) + INT32 bpp, RGBA_t *palette) { fixed_t yfrac, position, count; UINT8 *dest; @@ -121,7 +129,7 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm texelu16 = (UINT16)((alpha<<8) | texel); memcpy(dest, &texelu16, sizeof(UINT16)); break; - case 3 : colortemp = V_GetColor(texel); + case 3 : colortemp = palette[texel]; if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; @@ -130,7 +138,7 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; - case 4 : colortemp = V_GetColor(texel); + case 4 : colortemp = palette[texel]; colortemp.s.alpha = alpha; if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { @@ -160,7 +168,7 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, INT32 pblockheight, INT32 blockmodulo, fixed_t yfracstep, fixed_t scale_y, texpatch_t *originPatch, INT32 patchheight, - INT32 bpp) + INT32 bpp, RGBA_t *palette) { fixed_t yfrac, position, count; UINT8 *dest; @@ -231,7 +239,7 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, texelu16 = (UINT16)((alpha<<8) | texel); memcpy(dest, &texelu16, sizeof(UINT16)); break; - case 3 : colortemp = V_GetColor(texel); + case 3 : colortemp = palette[texel]; if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { RGBA_t rgbatexel; @@ -240,7 +248,7 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, } memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8)); break; - case 4 : colortemp = V_GetColor(texel); + case 4 : colortemp = palette[texel]; colortemp.s.alpha = alpha; if ((originPatch != NULL) && (originPatch->style != AST_COPY)) { @@ -284,10 +292,13 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap, UINT8 *block = mipmap->data; INT32 bpp; INT32 blockmodulo; + RGBA_t *palette; if (pwidth <= 0 || pheight <= 0) return; + palette = HWR_GetTexturePalette(); + ncols = pwidth; // source advance @@ -313,7 +324,7 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap, pblockheight, blockmodulo, yfracstep, scale_y, NULL, pheight, // not that pheight is going to get used anyway... - bpp); + bpp, palette); } } @@ -332,16 +343,19 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap, INT32 bpp; INT32 blockmodulo; INT32 width, height; + RGBA_t *palette; // Column drawing function pointer. static void (*ColumnDrawerPointer)(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap, INT32 pblockheight, INT32 blockmodulo, fixed_t yfracstep, fixed_t scale_y, texpatch_t *originPatch, INT32 patchheight, - INT32 bpp); + INT32 bpp, RGBA_t *palette); if (texture->width <= 0 || texture->height <= 0) return; + palette = HWR_GetTexturePalette(); + ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedColumnInCache : HWR_DrawColumnInCache; x1 = patch->originx; @@ -409,7 +423,7 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap, pblockheight, blockmodulo, yfracstep, scale_y, patch, height, - bpp); + bpp, palette); } } @@ -454,6 +468,9 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex) INT32 i; boolean skyspecial = false; //poor hack for Legacy large skies.. + RGBA_t *palette; + palette = HWR_GetTexturePalette(); + texture = textures[texnum]; // hack the Legacy skies.. @@ -472,7 +489,10 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex) grtex->mipmap.width = (UINT16)texture->width; grtex->mipmap.height = (UINT16)texture->height; - grtex->mipmap.format = textureformat; + if (skyspecial) + grtex->mipmap.format = GL_TEXFMT_RGBA; // that skyspecial code below assumes this format ... + else + grtex->mipmap.format = textureformat; blockwidth = texture->width; blockheight = texture->height; @@ -484,7 +504,7 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex) INT32 j; RGBA_t col; - col = V_GetColor(HWR_PATCHES_CHROMAKEY_COLORINDEX); + col = palette[HWR_PATCHES_CHROMAKEY_COLORINDEX]; for (j = 0; j < blockheight; j++) { for (i = 0; i < blockwidth; i++) @@ -759,19 +779,6 @@ void HWR_LoadMapTextures(size_t pnumtextures) gl_maptexturesloaded = true; } -void HWR_SetPalette(RGBA_t *palette) -{ - HWD.pfnSetPalette(palette); - - // hardware driver will flush there own cache if cache is non paletized - // now flush data texture cache so 32 bit texture are recomputed - if (patchformat == GL_TEXFMT_RGBA || textureformat == GL_TEXFMT_RGBA) - { - Z_FreeTag(PU_HWRCACHE); - Z_FreeTag(PU_HWRCACHE_UNLOCKED); - } -} - // -------------------------------------------------------------------------- // Make sure texture is downloaded and set it as the source // -------------------------------------------------------------------------- @@ -1111,6 +1118,7 @@ static void HWR_DrawPicInCache(UINT8 *block, INT32 pblockwidth, INT32 pblockheig UINT16 texelu16; INT32 picbpp; RGBA_t col; + RGBA_t *palette = HWR_GetTexturePalette(); stepy = ((INT32)SHORT(pic->height)<width)<>FRACBITS]; - col = V_GetColor(texel); + col = palette[texel]; *dest = col.s.red; // take the red level of the colour and use it for alpha, as fademasks do dest++; @@ -1341,4 +1350,159 @@ void HWR_GetFadeMask(lumpnum_t fademasklumpnum) Z_ChangeTag(grmip->data, PU_HWRCACHE_UNLOCKED); } +// ================================================= +// PALETTE HANDLING +// ================================================= + +void HWR_SetPalette(RGBA_t *palette) +{ + if (HWR_ShouldUsePaletteRendering()) + { + // set the palette for palette postprocessing + + if (cv_glpalettedepth.value == 16) + { + // crush to 16-bit rgb565, like software currently does in the standard configuration + // Note: Software's screenshots have the 24-bit palette, but the screen gets + // the 16-bit version! For making comparison screenshots either use an external screenshot + // tool or set the palette depth to 24 bits. + RGBA_t crushed_palette[256]; + int i; + for (i = 0; i < 256; i++) + { + float fred = (float)(palette[i].s.red >> 3); + float fgreen = (float)(palette[i].s.green >> 2); + float fblue = (float)(palette[i].s.blue >> 3); + crushed_palette[i].s.red = (UINT8)(fred / 31.0f * 255.0f); + crushed_palette[i].s.green = (UINT8)(fgreen / 63.0f * 255.0f); + crushed_palette[i].s.blue = (UINT8)(fblue / 31.0f * 255.0f); + crushed_palette[i].s.alpha = 255; + } + HWD.pfnSetScreenPalette(crushed_palette); + } + else + { + HWD.pfnSetScreenPalette(palette); + } + + // this part is responsible for keeping track of the palette OUTSIDE of a level. + if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + HWR_SetMapPalette(); + } + else + { + // set the palette for the textures + HWD.pfnSetTexturePalette(palette); + // reset mapPalette so next call to HWR_SetMapPalette will update everything correctly + memset(mapPalette, 0, sizeof(mapPalette)); + // hardware driver will flush there own cache if cache is non paletized + // now flush data texture cache so 32 bit texture are recomputed + if (patchformat == GL_TEXFMT_RGBA || textureformat == GL_TEXFMT_RGBA) + { + Z_FreeTag(PU_HWRCACHE); + Z_FreeTag(PU_HWRCACHE_UNLOCKED); + } + } +} + +static void HWR_SetPaletteLookup(RGBA_t *palette) +{ + int r, g, b; + UINT8 *lut = Z_Malloc( + HWR_PALETTE_LUT_SIZE*HWR_PALETTE_LUT_SIZE*HWR_PALETTE_LUT_SIZE*sizeof(UINT8), + PU_STATIC, NULL); +#define STEP_SIZE (256/HWR_PALETTE_LUT_SIZE) + for (b = 0; b < HWR_PALETTE_LUT_SIZE; b++) + { + for (g = 0; g < HWR_PALETTE_LUT_SIZE; g++) + { + for (r = 0; r < HWR_PALETTE_LUT_SIZE; r++) + { + lut[b*HWR_PALETTE_LUT_SIZE*HWR_PALETTE_LUT_SIZE+g*HWR_PALETTE_LUT_SIZE+r] = + NearestPaletteColor(r*STEP_SIZE, g*STEP_SIZE, b*STEP_SIZE, palette); + } + } + } +#undef STEP_SIZE + HWD.pfnSetPaletteLookup(lut); + Z_Free(lut); +} + +// Updates mapPalette to reflect the loaded level or other game state. +// Textures are flushed if needed. +// Call this function only in palette rendering mode. +void HWR_SetMapPalette(void) +{ + RGBA_t RGBA_converted[256]; + RGBA_t *palette; + int i; + + if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + { + // outside of a level, pMasterPalette should have PLAYPAL ready for us + palette = pMasterPalette; + } + else + { + // in a level pMasterPalette might have a flash palette, but we + // want the map's original palette. + lumpnum_t lumpnum = W_GetNumForName(GetPalette()); + size_t palsize = W_LumpLength(lumpnum); + UINT8 *RGB_data; + if (palsize < 768) // 256 * 3 + I_Error("HWR_SetMapPalette: A programmer assumed palette lumps are at least 768 bytes long, but apparently this was a wrong assumption!\n"); + RGB_data = W_CacheLumpNum(lumpnum, PU_CACHE); + // we got the RGB palette now, but we need it in RGBA format. + for (i = 0; i < 256; i++) + { + RGBA_converted[i].s.red = *(RGB_data++); + RGBA_converted[i].s.green = *(RGB_data++); + RGBA_converted[i].s.blue = *(RGB_data++); + RGBA_converted[i].s.alpha = 255; + } + palette = RGBA_converted; + } + + // check if the palette has changed from the previous one + if (memcmp(mapPalette, palette, sizeof(mapPalette))) + { + memcpy(mapPalette, palette, sizeof(mapPalette)); + // in palette rendering mode, this means that all rgba textures now have wrong colors + // and the lookup table is outdated + HWR_SetPaletteLookup(mapPalette); + HWD.pfnSetTexturePalette(mapPalette); + if (patchformat == GL_TEXFMT_RGBA || textureformat == GL_TEXFMT_RGBA) + { + Z_FreeTag(PU_HWRCACHE); + Z_FreeTag(PU_HWRCACHE_UNLOCKED); + } + } +} + +// Creates a hardware lighttable from the supplied lighttable. +// Returns the id of the hw lighttable, usable in FSurfaceInfo. +UINT32 HWR_CreateLightTable(UINT8 *lighttable) +{ + UINT32 i, id; + RGBA_t *palette = HWR_GetTexturePalette(); + RGBA_t *hw_lighttable = Z_Malloc(256 * 32 * sizeof(RGBA_t), PU_STATIC, NULL); + + // To make the palette index -> RGBA mapping easier for the shader, + // the hardware lighttable is composed of RGBA colors instead of palette indices. + for (i = 0; i < 256 * 32; i++) + hw_lighttable[i] = palette[lighttable[i]]; + + id = HWD.pfnCreateLightTable(hw_lighttable); + Z_Free(hw_lighttable); + return id; +} + +// Note: all hardware lighttable ids assigned before this +// call become invalid and must not be used. +void HWR_ClearLightTables(void) +{ + if (vid.glstate == VID_GL_LIBRARY_LOADED) + HWD.pfnClearLightTables(); +} + #endif //HWRENDER diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index bbf9d8424..287c3a2be 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -18,6 +18,12 @@ #define ZCLIP_PLANE 4.0f // Used for the actual game drawing #define NZCLIP_PLANE 0.9f // Seems to be only used for the HUD and screen textures +// The width/height/depth of the palette lookup table used by palette rendering. +// Changing this also requires changing the shader code! +// Also assumed to be a power of two in some parts of the code. +// 64 seems to work perfectly for the vanilla palette. +#define HWR_PALETTE_LUT_SIZE 64 + // ========================================================================== // SIMPLE TYPES // ========================================================================== @@ -146,6 +152,7 @@ enum SHADER_WATER, SHADER_FOG, SHADER_SKY, + SHADER_PALETTE_POSTPROCESS, NUMSHADERTARGETS, }; @@ -275,6 +282,7 @@ struct FSurfaceInfo RGBA_t PolyColor; RGBA_t TintColor; RGBA_t FadeColor; + UINT32 LightTableId; FLightInfo LightInfo; }; typedef struct FSurfaceInfo FSurfaceInfo; @@ -282,7 +290,7 @@ typedef struct FSurfaceInfo FSurfaceInfo; #define GL_DEFAULTMIX 0x00000000 #define GL_DEFAULTFOG 0xFF000000 -//Hurdler: added for backward compatibility +// Various settings and states for the rendering backend. enum hwdsetspecialstate { HWD_SET_MODEL_LIGHTING = 1, diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index 8eddeebea..629f616da 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -32,7 +32,7 @@ EXPORT void HWRAPI(Shutdown) (void); #ifdef _WINDOWS EXPORT void HWRAPI(GetModeList) (vmode_t **pvidmodes, INT32 *numvidmodes); #endif -EXPORT void HWRAPI(SetPalette) (RGBA_t *ppal); +EXPORT void HWRAPI(SetTexturePalette) (RGBA_t *ppal); EXPORT void HWRAPI(FinishUpdate) (INT32 waitvbl); EXPORT void HWRAPI(Draw2DLine) (F2DCoord *v1, F2DCoord *v2, RGBA_t Color); EXPORT void HWRAPI(DrawPolygon) (FSurfaceInfo *pSurf, FOutVector *pOutVerts, FUINT iNumPts, FBITFIELD PolyFlags); @@ -47,10 +47,8 @@ EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip); EXPORT void HWRAPI(ClearMipMapCache) (void); -//Hurdler: added for backward compatibility EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value); -//Hurdler: added for new development EXPORT void HWRAPI(DrawModel) (model_t *model, INT32 frameIndex, INT32 duration, INT32 tics, INT32 nextFrameIndex, FTransform *pos, float scale, UINT8 flipped, UINT8 hflipped, FSurfaceInfo *Surface); EXPORT void HWRAPI(CreateModelVBOs) (model_t *model); EXPORT void HWRAPI(SetTransform) (FTransform *ptransform); @@ -76,6 +74,11 @@ EXPORT void HWRAPI(UnSetShader) (void); EXPORT void HWRAPI(SetShaderInfo) (hwdshaderinfo_t info, INT32 value); +EXPORT void HWRAPI(SetPaletteLookup)(UINT8 *lut); +EXPORT UINT32 HWRAPI(CreateLightTable)(RGBA_t *hw_lighttable); +EXPORT void HWRAPI(ClearLightTables)(void); +EXPORT void HWRAPI(SetScreenPalette)(RGBA_t *palette); + // ========================================================================== // HWR DRIVER OBJECT, FOR CLIENT PROGRAM // ========================================================================== @@ -85,7 +88,7 @@ EXPORT void HWRAPI(SetShaderInfo) (hwdshaderinfo_t info, INT32 value); struct hwdriver_s { Init pfnInit; - SetPalette pfnSetPalette; + SetTexturePalette pfnSetTexturePalette; FinishUpdate pfnFinishUpdate; Draw2DLine pfnDraw2DLine; DrawPolygon pfnDrawPolygon; @@ -99,7 +102,7 @@ struct hwdriver_s ReadRect pfnReadRect; GClipRect pfnGClipRect; ClearMipMapCache pfnClearMipMapCache; - SetSpecialState pfnSetSpecialState;//Hurdler: added for backward compatibility + SetSpecialState pfnSetSpecialState; DrawModel pfnDrawModel; CreateModelVBOs pfnCreateModelVBOs; SetTransform pfnSetTransform; @@ -127,6 +130,11 @@ struct hwdriver_s UnSetShader pfnUnSetShader; SetShaderInfo pfnSetShaderInfo; + + SetPaletteLookup pfnSetPaletteLookup; + CreateLightTable pfnCreateLightTable; + ClearLightTables pfnClearLightTables; + SetScreenPalette pfnSetScreenPalette; }; extern struct hwdriver_s hwdriver; diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index f0f1fad46..316f32ef8 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -128,6 +128,9 @@ void HWR_FreeColormapCache(void); void HWR_UnlockCachedPatch(GLPatch_t *gpatch); void HWR_SetPalette(RGBA_t *palette); +void HWR_SetMapPalette(void); +UINT32 HWR_CreateLightTable(UINT8 *lighttable); +void HWR_ClearLightTables(void); // -------- diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 79f4eec0c..cb976a8f8 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -126,26 +126,6 @@ static line_t *gl_linedef; static sector_t *gl_frontsector; static sector_t *gl_backsector; -// -------------------------------------------------------------------------- -// STUFF FOR THE PROJECTION CODE -// -------------------------------------------------------------------------- - -FTransform atransform; -// duplicates of the main code, set after R_SetupFrame() passed them into sharedstruct, -// copied here for local use -static fixed_t dup_viewx, dup_viewy, dup_viewz; -static angle_t dup_viewangle; - -static float gl_viewx, gl_viewy, gl_viewz; -static float gl_viewsin, gl_viewcos; - -// Maybe not necessary with the new T&L code (needs to be checked!) -static float gl_viewludsin, gl_viewludcos; // look up down kik test -static float gl_fovlud; - -static angle_t gl_aimingangle; -static void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox); - // Render stats precise_t ps_hw_skyboxtime = 0; precise_t ps_hw_nodesorttime = 0; @@ -170,6 +150,29 @@ boolean gl_sessioncommandsadded = false; // false if shaders have not been initialized yet, or if shaders are not available boolean gl_shadersavailable = false; +// Whether the internal state is set to palette rendering or not. +static boolean gl_palette_rendering_state = false; + +// -------------------------------------------------------------------------- +// STUFF FOR THE PROJECTION CODE +// -------------------------------------------------------------------------- + +FTransform atransform; +// duplicates of the main code, set after R_SetupFrame() passed them into sharedstruct, +// copied here for local use +static fixed_t dup_viewx, dup_viewy, dup_viewz; +static angle_t dup_viewangle; + +static float gl_viewx, gl_viewy, gl_viewz; +static float gl_viewsin, gl_viewcos; + +// Maybe not necessary with the new T&L code (needs to be checked!) +static float gl_viewludsin, gl_viewludcos; // look up down kik test +static float gl_fovlud; + +static angle_t gl_aimingangle; +static void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox); + // ========================================================================== // Lighting // ========================================================================== @@ -234,6 +237,33 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col Surface->LightInfo.light_level = light_level; Surface->LightInfo.fade_start = (colormap != NULL) ? colormap->fadestart : 0; Surface->LightInfo.fade_end = (colormap != NULL) ? colormap->fadeend : 31; + + if (HWR_ShouldUsePaletteRendering()) + { + boolean default_colormap = false; + if (!colormap) + { + colormap = R_GetDefaultColormap(); // a place to store the hw lighttable id + // alternatively could just store the id in a global variable if there are issues + default_colormap = true; + } + // create hw lighttable if there isn't one + if (!colormap->gl_lighttable_id) + { + UINT8 *colormap_pointer; + + if (default_colormap) + colormap_pointer = colormaps; // don't actually use the data from the "default colormap" + else + colormap_pointer = colormap->colormap; + colormap->gl_lighttable_id = HWR_CreateLightTable(colormap_pointer); + } + Surface->LightTableId = colormap->gl_lighttable_id; + } + else + { + Surface->LightTableId = 0; + } } UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap) // Let's see if this can work @@ -905,13 +935,15 @@ static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, { if (pfloor && (pfloor->flags & FF_FOG)) { - lightnum = HWR_CalcWallLight(pfloor->master->frontsector->lightlevel, v1x, v1y, v2x, v2y); + lightnum = pfloor->master->frontsector->lightlevel; colormap = pfloor->master->frontsector->extra_colormap; + lightnum = colormap ? lightnum : HWR_CalcWallLight(lightnum, v1x, v1y, v2x, v2y); } else { - lightnum = HWR_CalcWallLight(*list[i].lightlevel, v1x, v1y, v2x, v2y); + lightnum = *list[i].lightlevel; colormap = *list[i].extra_colormap; + lightnum = colormap ? lightnum : HWR_CalcWallLight(lightnum, v1x, v1y, v2x, v2y); } } @@ -1113,8 +1145,9 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom cliphigh = (float)(texturehpeg + (gl_curline->flength*FRACUNIT)); } - lightnum = HWR_CalcWallLight(gl_frontsector->lightlevel, vs.x, vs.y, ve.x, ve.y); + lightnum = gl_frontsector->lightlevel; colormap = gl_frontsector->extra_colormap; + lightnum = colormap ? lightnum : HWR_CalcWallLight(lightnum, vs.x, vs.y, ve.x, ve.y); if (gl_frontsector) Surf.PolyColor.s.alpha = 255; @@ -1739,8 +1772,9 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom blendmode = PF_Fog|PF_NoTexture; - lightnum = HWR_CalcWallLight(rover->master->frontsector->lightlevel, vs.x, vs.y, ve.x, ve.y); + lightnum = rover->master->frontsector->lightlevel; colormap = rover->master->frontsector->extra_colormap; + lightnum = colormap ? lightnum : HWR_CalcWallLight(lightnum, vs.x, vs.y, ve.x, ve.y); Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap); @@ -1851,8 +1885,9 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom blendmode = PF_Fog|PF_NoTexture; - lightnum = HWR_CalcWallLight(rover->master->frontsector->lightlevel, vs.x, vs.y, ve.x, ve.y); + lightnum = rover->master->frontsector->lightlevel; colormap = rover->master->frontsector->extra_colormap; + lightnum = colormap ? lightnum : HWR_CalcWallLight(lightnum, vs.x, vs.y, ve.x, ve.y); Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap); @@ -5867,6 +5902,7 @@ void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player) else type = &postimgtype; + if (!HWR_ShouldUsePaletteRendering()) { // do we really need to save player (is it not the same)? player_t *saved_player = stplyr; @@ -6080,6 +6116,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) HWR_RenderSkyboxView(viewnumber, player); // This is drawn before everything else so it is placed behind ps_hw_skyboxtime = I_GetPreciseTime() - ps_hw_skyboxtime; + if (!HWR_ShouldUsePaletteRendering()) { // do we really need to save player (is it not the same)? player_t *saved_player = stplyr; @@ -6277,6 +6314,56 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) HWD.pfnGClipRect(0, 0, vid.width, vid.height, NZCLIP_PLANE); } +// Returns whether palette rendering is "actually enabled." +// Can't have palette rendering if shaders are disabled. +boolean HWR_ShouldUsePaletteRendering(void) +{ + return (cv_glpaletterendering.value && HWR_UseShader()); +} + +// enable or disable palette rendering state depending on settings and availability +// called when relevant settings change +// shader recompilation is done in the cvar callback +static void HWR_TogglePaletteRendering(void) +{ + // which state should we go to? + if (HWR_ShouldUsePaletteRendering()) + { + // are we not in that state already? + if (!gl_palette_rendering_state) + { + gl_palette_rendering_state = true; + + // The textures will still be converted to RGBA by r_opengl. + // This however makes hw_cache use paletted blending for composite textures! + // (patchformat is not touched) + textureformat = GL_TEXFMT_P_8; + + HWR_SetMapPalette(); + HWR_SetPalette(pLocalPalette); + + // If the r_opengl "texture palette" stays the same during this switch, the textures + // will not be cleared out. However they are still out of date since the + // composite texture blending method has changed. Therefore they need to be cleared. + HWD.pfnClearMipMapCache(); + } + } + else + { + // are we not in that state already? + if (gl_palette_rendering_state) + { + gl_palette_rendering_state = false; + textureformat = GL_TEXFMT_RGBA; + HWR_SetPalette(pLocalPalette); + // If the r_opengl "texture palette" stays the same during this switch, the textures + // will not be cleared out. However they are still out of date since the + // composite texture blending method has changed. Therefore they need to be cleared. + HWD.pfnClearMipMapCache(); + } + } +} + void HWR_LoadLevel(void) { #ifdef ALAM_LIGHTING @@ -6290,6 +6377,9 @@ void HWR_LoadLevel(void) HWR_ClearSkyDome(); HWR_BuildSkyDome(); + if (HWR_ShouldUsePaletteRendering()) + HWR_SetMapPalette(); + gl_maploaded = true; } @@ -6305,6 +6395,9 @@ static CV_PossibleValue_t glshearing_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Thi static void CV_glfiltermode_OnChange(void); static void CV_glanisotropic_OnChange(void); static void CV_glmodellighting_OnChange(void); +static void CV_glpaletterendering_OnChange(void); +static void CV_glpalettedepth_OnChange(void); +static void CV_glshaders_OnChange(void); static CV_PossibleValue_t glfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSAMPLED, "Nearest"}, {HWD_SET_TEXTUREFILTER_BILINEAR, "Bilinear"}, {HWD_SET_TEXTUREFILTER_TRILINEAR, "Trilinear"}, @@ -6314,7 +6407,7 @@ static CV_PossibleValue_t glfiltermode_cons_t[]= {{HWD_SET_TEXTUREFILTER_POINTSA {0, NULL}}; CV_PossibleValue_t glanisotropicmode_cons_t[] = {{1, "MIN"}, {16, "MAX"}, {0, NULL}}; -consvar_t cv_glshaders = CVAR_INIT ("gr_shaders", "On", CV_SAVE, glshaders_cons_t, NULL); +consvar_t cv_glshaders = CVAR_INIT ("gr_shaders", "On", CV_SAVE|CV_CALL, glshaders_cons_t, CV_glshaders_OnChange); consvar_t cv_glallowshaders = CVAR_INIT ("gr_allowclientshaders", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_fovchange = CVAR_INIT ("gr_fovchange", "Off", CV_SAVE, CV_OnOff, NULL); @@ -6342,6 +6435,11 @@ consvar_t cv_glsolvetjoin = CVAR_INIT ("gr_solvetjoin", "On", 0, CV_OnOff, NULL) consvar_t cv_glbatching = CVAR_INIT ("gr_batching", "On", 0, CV_OnOff, NULL); +static CV_PossibleValue_t glpalettedepth_cons_t[] = {{16, "16 bits"}, {24, "24 bits"}, {0, NULL}}; + +consvar_t cv_glpaletterendering = CVAR_INIT ("gr_paletterendering", "Off", CV_SAVE|CV_CALL, CV_OnOff, CV_glpaletterendering_OnChange); +consvar_t cv_glpalettedepth = CVAR_INIT ("gr_palettedepth", "16 bits", CV_SAVE|CV_CALL, glpalettedepth_cons_t, CV_glpalettedepth_OnChange); + static void CV_glfiltermode_OnChange(void) { if (rendermode == render_opengl) @@ -6361,6 +6459,31 @@ static void CV_glmodellighting_OnChange(void) HWR_CompileShaders(); } +static void CV_glpaletterendering_OnChange(void) +{ + if (gl_shadersavailable) + { + HWR_CompileShaders(); + HWR_TogglePaletteRendering(); + } +} + +static void CV_glpalettedepth_OnChange(void) +{ + // refresh the screen palette + if (HWR_ShouldUsePaletteRendering()) + HWR_SetPalette(pLocalPalette); +} + +static void CV_glshaders_OnChange(void) +{ + if (cv_glpaletterendering.value) + { + // can't do palette rendering without shaders, so update the state if needed + HWR_TogglePaletteRendering(); + } +} + //added by Hurdler: console varibale that are saved void HWR_AddCommands(void) { @@ -6389,6 +6512,9 @@ void HWR_AddCommands(void) CV_RegisterVar(&cv_glbatching); + CV_RegisterVar(&cv_glpaletterendering); + CV_RegisterVar(&cv_glpalettedepth); + #ifndef NEWCLIP CV_RegisterVar(&cv_glclipwalls); #endif @@ -6411,6 +6537,8 @@ void HWR_Startup(void) { CONS_Printf("HWR_Startup()...\n"); + textureformat = patchformat = GL_TEXFMT_RGBA; + HWR_InitPolyPool(); HWR_AddSessionCommands(); HWR_InitMapTextures(); @@ -6421,11 +6549,9 @@ void HWR_Startup(void) gl_shadersavailable = HWR_InitShaders(); HWR_LoadAllCustomShaders(); + HWR_TogglePaletteRendering(); } - if (rendermode == render_opengl) - textureformat = patchformat = GL_TEXFMT_RGBA; - gl_init = true; } @@ -6564,7 +6690,7 @@ void HWR_DoPostProcessor(player_t *player) // Armageddon Blast Flash! // Could this even be considered postprocessor? - if (player->flashcount) + if (player->flashcount && !HWR_ShouldUsePaletteRendering()) { FOutVector v[4]; FSurfaceInfo Surf; diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 9a3477b03..71a1a6984 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -74,6 +74,8 @@ FBITFIELD HWR_GetBlendModeFlag(INT32 ast); FBITFIELD HWR_SurfaceBlend(INT32 style, INT32 transtablenum, FSurfaceInfo *pSurf); FBITFIELD HWR_TranstableToAlpha(INT32 transtablenum, FSurfaceInfo *pSurf); +boolean HWR_ShouldUsePaletteRendering(void); + extern CV_PossibleValue_t glanisotropicmode_cons_t[]; #ifdef ALAM_LIGHTING @@ -96,8 +98,9 @@ extern consvar_t cv_glspritebillboarding; extern consvar_t cv_glskydome; extern consvar_t cv_glfakecontrast; extern consvar_t cv_glslopecontrast; - extern consvar_t cv_glbatching; +extern consvar_t cv_glpaletterendering; +extern consvar_t cv_glpalettedepth; extern float gl_viewwidth, gl_viewheight, gl_baseviewwindowy; diff --git a/src/hardware/hw_shaders.c b/src/hardware/hw_shaders.c index c5e04fb8f..e28624226 100644 --- a/src/hardware/hw_shaders.c +++ b/src/hardware/hw_shaders.c @@ -38,7 +38,7 @@ #define GLSL_MODEL_VERTEX_SHADER \ "void main()\n" \ "{\n" \ - "#ifdef MODEL_LIGHTING\n" \ + "#ifdef SRB2_MODEL_LIGHTING\n" \ "float nDotVP = dot(gl_Normal, vec3(0, 1, 0));\n" \ "float light = 0.75 + max(nDotVP, 0.0);\n" \ "gl_FrontColor = vec4(light, light, light, 1.0);\n" \ @@ -69,6 +69,7 @@ // Software fragment shader // +// Include GLSL_FLOOR_FUDGES or GLSL_WALL_FUDGES or define the fudges in shaders that use this macro. #define GLSL_DOOM_COLORMAP \ "float R_DoomColormap(float light, float z)\n" \ "{\n" \ @@ -76,8 +77,12 @@ "float lightz = clamp(z / 16.0, 0.0, 127.0);\n" \ "float startmap = (15.0 - lightnum) * 4.0;\n" \ "float scale = 160.0 / (lightz + 1.0);\n" \ - "return startmap - scale * 0.5;\n" \ + "float cap = (155.0 - light) * 0.26;\n" \ + "return max(startmap * STARTMAP_FUDGE - scale * 0.5 * SCALE_FUDGE, cap);\n" \ "}\n" +// lighting cap adjustment: +// first num (155.0), increase to make it start to go dark sooner +// second num (0.26), increase to make it go dark faster #define GLSL_DOOM_LIGHT_EQUATION \ "float R_DoomLightingEquation(float light)\n" \ @@ -106,7 +111,28 @@ "}\n" \ "final_color = mix(final_color, fade_color, darkness);\n" +#define GLSL_PALETTE_RENDERING \ + "float tex_pal_idx = texture3D(palette_lookup_tex, vec3((texel * 63.0 + 0.5) / 64.0))[0] * 255.0;\n" \ + "float z = gl_FragCoord.z / gl_FragCoord.w;\n" \ + "float light_y = clamp(floor(R_DoomColormap(lighting, z)), 0.0, 31.0);\n" \ + "vec2 lighttable_coord = vec2((tex_pal_idx + 0.5) / 256.0, (light_y + 0.5) / 32.0);\n" \ + "vec4 final_color = texture2D(lighttable_tex, lighttable_coord);\n" \ + "final_color.a = texel.a * poly_color.a;\n" \ + "gl_FragColor = final_color;\n" \ + #define GLSL_SOFTWARE_FRAGMENT_SHADER \ + "#ifdef SRB2_PALETTE_RENDERING\n" \ + "uniform sampler2D tex;\n" \ + "uniform sampler3D palette_lookup_tex;\n" \ + "uniform sampler2D lighttable_tex;\n" \ + "uniform vec4 poly_color;\n" \ + "uniform float lighting;\n" \ + GLSL_DOOM_COLORMAP \ + "void main(void) {\n" \ + "vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \ + GLSL_PALETTE_RENDERING \ + "}\n" \ + "#else\n" \ "uniform sampler2D tex;\n" \ "uniform vec4 poly_color;\n" \ "uniform vec4 tint_color;\n" \ @@ -124,11 +150,45 @@ GLSL_SOFTWARE_FADE_EQUATION \ "final_color.a = texel.a * poly_color.a;\n" \ "gl_FragColor = final_color;\n" \ - "}\0" + "}\n" \ + "#endif\0" + +// hand tuned adjustments for light level calculation +#define GLSL_FLOOR_FUDGES \ + "#define STARTMAP_FUDGE 1.06\n" \ + "#define SCALE_FUDGE 1.15\n" + +#define GLSL_WALL_FUDGES \ + "#define STARTMAP_FUDGE 1.05\n" \ + "#define SCALE_FUDGE 2.2\n" + +#define GLSL_SOFTWARE_FRAGMENT_SHADER_FLOORS \ + GLSL_FLOOR_FUDGES \ + GLSL_SOFTWARE_FRAGMENT_SHADER + +#define GLSL_SOFTWARE_FRAGMENT_SHADER_WALLS \ + GLSL_WALL_FUDGES \ + GLSL_SOFTWARE_FRAGMENT_SHADER // same as above but multiplies results with the lighting value from the -// accompanying vertex shader (stored in gl_Color) +// accompanying vertex shader (stored in gl_Color) if model lighting is enabled #define GLSL_SOFTWARE_MODEL_FRAGMENT_SHADER \ + GLSL_WALL_FUDGES \ + "#ifdef SRB2_PALETTE_RENDERING\n" \ + "uniform sampler2D tex;\n" \ + "uniform sampler3D palette_lookup_tex;\n" \ + "uniform sampler2D lighttable_tex;\n" \ + "uniform vec4 poly_color;\n" \ + "uniform float lighting;\n" \ + GLSL_DOOM_COLORMAP \ + "void main(void) {\n" \ + "vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \ + "#ifdef SRB2_MODEL_LIGHTING\n" \ + "texel *= gl_Color;\n" \ + "#endif\n" \ + GLSL_PALETTE_RENDERING \ + "}\n" \ + "#else\n" \ "uniform sampler2D tex;\n" \ "uniform vec4 poly_color;\n" \ "uniform vec4 tint_color;\n" \ @@ -144,12 +204,13 @@ "vec4 final_color = base_color;\n" \ GLSL_SOFTWARE_TINT_EQUATION \ GLSL_SOFTWARE_FADE_EQUATION \ - "#ifdef MODEL_LIGHTING\n" \ + "#ifdef SRB2_MODEL_LIGHTING\n" \ "final_color *= gl_Color;\n" \ "#endif\n" \ "final_color.a = texel.a * poly_color.a;\n" \ "gl_FragColor = final_color;\n" \ - "}\0" + "}\n" \ + "#endif\0" // // Water surface shader @@ -158,7 +219,32 @@ // Still needs to distort things underneath/around the water... // +#define GLSL_WATER_TEXEL \ + "float water_z = (gl_FragCoord.z / gl_FragCoord.w) / 2.0;\n" \ + "float a = -pi * (water_z * freq) + (leveltime * speed);\n" \ + "float sdistort = sin(a) * amp;\n" \ + "float cdistort = cos(a) * amp;\n" \ + "vec4 texel = texture2D(tex, vec2(gl_TexCoord[0].s - sdistort, gl_TexCoord[0].t - cdistort));\n" + #define GLSL_WATER_FRAGMENT_SHADER \ + GLSL_FLOOR_FUDGES \ + "const float freq = 0.025;\n" \ + "const float amp = 0.025;\n" \ + "const float speed = 2.0;\n" \ + "const float pi = 3.14159;\n" \ + "#ifdef SRB2_PALETTE_RENDERING\n" \ + "uniform sampler2D tex;\n" \ + "uniform sampler3D palette_lookup_tex;\n" \ + "uniform sampler2D lighttable_tex;\n" \ + "uniform vec4 poly_color;\n" \ + "uniform float lighting;\n" \ + "uniform float leveltime;\n" \ + GLSL_DOOM_COLORMAP \ + "void main(void) {\n" \ + GLSL_WATER_TEXEL \ + GLSL_PALETTE_RENDERING \ + "}\n" \ + "#else\n" \ "uniform sampler2D tex;\n" \ "uniform vec4 poly_color;\n" \ "uniform vec4 tint_color;\n" \ @@ -167,25 +253,18 @@ "uniform float fade_start;\n" \ "uniform float fade_end;\n" \ "uniform float leveltime;\n" \ - "const float freq = 0.025;\n" \ - "const float amp = 0.025;\n" \ - "const float speed = 2.0;\n" \ - "const float pi = 3.14159;\n" \ GLSL_DOOM_COLORMAP \ GLSL_DOOM_LIGHT_EQUATION \ "void main(void) {\n" \ - "float z = (gl_FragCoord.z / gl_FragCoord.w) / 2.0;\n" \ - "float a = -pi * (z * freq) + (leveltime * speed);\n" \ - "float sdistort = sin(a) * amp;\n" \ - "float cdistort = cos(a) * amp;\n" \ - "vec4 texel = texture2D(tex, vec2(gl_TexCoord[0].s - sdistort, gl_TexCoord[0].t - cdistort));\n" \ + GLSL_WATER_TEXEL \ "vec4 base_color = texel * poly_color;\n" \ "vec4 final_color = base_color;\n" \ GLSL_SOFTWARE_TINT_EQUATION \ GLSL_SOFTWARE_FADE_EQUATION \ "final_color.a = texel.a * poly_color.a;\n" \ "gl_FragColor = final_color;\n" \ - "}\0" + "}\n" \ + "#endif\0" // // Fog block shader @@ -193,7 +272,10 @@ // Alpha of the planes themselves are still slightly off -- see HWR_FogBlockAlpha // +// The floor fudges are used, but should the wall fudges be used instead? or something inbetween? +// or separate values for floors and walls? (need to change more than this shader for that) #define GLSL_FOG_FRAGMENT_SHADER \ + GLSL_FLOOR_FUDGES \ "uniform vec4 tint_color;\n" \ "uniform vec4 fade_color;\n" \ "uniform float lighting;\n" \ @@ -220,6 +302,19 @@ "gl_FragColor = texture2D(tex, gl_TexCoord[0].st) * gl_Color * poly_color;\n" \ "}\0" +// Shader for the palette rendering postprocess step +#define GLSL_PALETTE_POSTPROCESS_SHADER \ + "uniform sampler2D tex;\n" \ + "uniform sampler3D palette_lookup_tex;\n" \ + "uniform sampler1D screen_palette_tex;\n" \ + "void main(void) {\n" \ + "vec4 texel = texture2D(tex, gl_TexCoord[0].st);\n" \ + "float tex_pal_idx = texture3D(palette_lookup_tex, vec3((texel * 63.0 + 0.5) / 64.0))[0] * 255.0;\n" \ + "float palette_coord = (tex_pal_idx + 0.5) / 256.0;\n" \ + "vec4 final_color = texture1D(screen_palette_tex, palette_coord);\n" \ + "gl_FragColor = final_color;\n" \ + "}\0" + // ================ // Shader sources // ================ @@ -229,13 +324,13 @@ static struct { const char *fragment; } const gl_shadersources[] = { // Floor shader - {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER}, + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER_FLOORS}, // Wall shader - {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER}, + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER_WALLS}, // Sprite shader - {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER}, + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SOFTWARE_FRAGMENT_SHADER_WALLS}, // Model shader {GLSL_MODEL_VERTEX_SHADER, GLSL_SOFTWARE_MODEL_FRAGMENT_SHADER}, @@ -249,6 +344,9 @@ static struct { // Sky shader {GLSL_DEFAULT_VERTEX_SHADER, GLSL_SKY_FRAGMENT_SHADER}, + // Palette postprocess shader + {GLSL_DEFAULT_VERTEX_SHADER, GLSL_PALETTE_POSTPROCESS_SHADER}, + {NULL, NULL}, }; @@ -272,7 +370,8 @@ static shader_t gl_shaders[NUMSHADERTARGETS*2]; static shadertarget_t gl_shadertargets[NUMSHADERTARGETS]; -#define MODEL_LIGHTING_DEFINE "#define MODEL_LIGHTING" +#define MODEL_LIGHTING_DEFINE "#define SRB2_MODEL_LIGHTING" +#define PALETTE_RENDERING_DEFINE "#define SRB2_PALETTE_RENDERING" // Initialize shader variables and the backend's shader system. Load the base shaders. // Returns false if shaders cannot be used. @@ -446,6 +545,8 @@ static char *HWR_PreprocessShader(char *original) new_len = original_len; if (cv_glmodellighting.value) new_len += sizeof(MODEL_LIGHTING_DEFINE) - 1 + 2 * line_ending_len; + if (cv_glpaletterendering.value) + new_len += sizeof(PALETTE_RENDERING_DEFINE) - 1 + 2 * line_ending_len; // Allocate memory for modified shader. new_shader = Z_Malloc(new_len + 1, PU_STATIC, NULL); @@ -458,16 +559,23 @@ static char *HWR_PreprocessShader(char *original) read_pos += insertion_pos; write_pos += insertion_pos; +#define WRITE_DEFINE(define) \ + { \ + strcpy(write_pos, line_ending); \ + write_pos += line_ending_len; \ + strcpy(write_pos, define); \ + write_pos += sizeof(define) - 1; \ + strcpy(write_pos, line_ending); \ + write_pos += line_ending_len; \ + } + // Write the additions. if (cv_glmodellighting.value) - { - strcpy(write_pos, line_ending); - write_pos += line_ending_len; - strcpy(write_pos, MODEL_LIGHTING_DEFINE); - write_pos += sizeof(MODEL_LIGHTING_DEFINE) - 1; - strcpy(write_pos, line_ending); - write_pos += line_ending_len; - } + WRITE_DEFINE(MODEL_LIGHTING_DEFINE) + if (cv_glpaletterendering.value) + WRITE_DEFINE(PALETTE_RENDERING_DEFINE) + +#undef WRITE_DEFINE // Copy the part after our additions. M_Memcpy(write_pos, read_pos, original_len - insertion_pos); @@ -557,6 +665,7 @@ customshaderxlat_t shaderxlat[] = {"WaterRipple", SHADER_WATER}, {"Fog", SHADER_FOG}, {"Sky", SHADER_SKY}, + {"PalettePostprocess", SHADER_PALETTE_POSTPROCESS}, {NULL, 0}, }; @@ -739,18 +848,13 @@ const char *HWR_GetShaderName(INT32 shader) { INT32 i; - if (shader) + for (i = 0; shaderxlat[i].type; i++) { - for (i = 0; shaderxlat[i].type; i++) - { - if (shaderxlat[i].id == shader) - return shaderxlat[i].type; - } - - return "Unknown"; + if (shaderxlat[i].id == shader) + return shaderxlat[i].type; } - return "Default"; + return "Unknown"; } #endif // HWRENDER diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 9321934b7..3e9959757 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -34,12 +34,21 @@ struct GLRGBAFloat GLfloat alpha; }; typedef struct GLRGBAFloat GLRGBAFloat; -static const GLubyte white[4] = { 255, 255, 255, 255 }; + +// lighttable list item +struct LTListItem +{ + UINT32 id; + struct LTListItem *next; +}; +typedef struct LTListItem LTListItem; // ========================================================================== // CONSTANTS // ========================================================================== +static const GLubyte white[4] = { 255, 255, 255, 255 }; + // With OpenGL 1.1+, the first texture should be 1 static GLuint NOTEXTURE_NUM = 0; @@ -55,6 +64,7 @@ static float NEAR_CLIPPING_PLANE = NZCLIP_PLANE; static GLuint tex_downloaded = 0; +static GLuint lt_downloaded = 0; // currently bound lighttable texture static GLfloat fov = 90.0f; static FBITFIELD CurrentPolyFlags; @@ -62,7 +72,14 @@ static FBITFIELD CurrentPolyFlags; static FTextureInfo *TexCacheTail = NULL; static FTextureInfo *TexCacheHead = NULL; -RGBA_t myPaletteData[256]; +// Linked list of all lighttables. +static LTListItem *LightTablesTail = NULL; +static LTListItem *LightTablesHead = NULL; + +static RGBA_t screenPalette[256] = {0}; // the palette for the postprocessing step in palette rendering +static GLuint screenPaletteTex = 0; // 1D texture containing the screen palette +static GLuint paletteLookupTex = 0; // 3D texture containing RGB -> palette index lookup table +RGBA_t myPaletteData[256]; // the palette for converting textures to RGBA GLint screen_width = 0; // used by Draw2DLine() GLint screen_height = 0; GLbyte screen_depth = 0; @@ -377,6 +394,8 @@ typedef void (APIENTRY * PFNglTexEnvi) (GLenum target, GLenum pname, GLint param static PFNglTexEnvi pglTexEnvi; typedef void (APIENTRY * PFNglTexParameteri) (GLenum target, GLenum pname, GLint param); static PFNglTexParameteri pglTexParameteri; +typedef void (APIENTRY * PFNglTexImage1D) (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +static PFNglTexImage1D pglTexImage1D; typedef void (APIENTRY * PFNglTexImage2D) (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); static PFNglTexImage2D pglTexImage2D; typedef void (APIENTRY * PFNglTexSubImage2D) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); @@ -400,6 +419,10 @@ static PFNglCopyTexSubImage2D pglCopyTexSubImage2D; typedef GLint (APIENTRY * PFNgluBuild2DMipmaps) (GLenum target, GLint internalFormat, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data); static PFNgluBuild2DMipmaps pgluBuild2DMipmaps; +/* 1.2 functions for 3D textures */ +typedef void (APIENTRY * PFNglTexImage3D) (GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +static PFNglTexImage3D pglTexImage3D; + /* 1.3 functions for multitexturing */ typedef void (APIENTRY *PFNglActiveTexture) (GLenum); static PFNglActiveTexture pglActiveTexture; @@ -444,6 +467,9 @@ static PFNglBlendEquation pglBlendEquation; #ifndef GL_TEXTURE1 #define GL_TEXTURE1 0x84C1 #endif +#ifndef GL_TEXTURE2 +#define GL_TEXTURE2 0x84C2 +#endif /* 1.5 Parms */ #ifndef GL_ARRAY_BUFFER @@ -515,6 +541,7 @@ boolean SetupGLfunc(void) GETOPENGLFUNC(pglTexEnvi, glTexEnvi) GETOPENGLFUNC(pglTexParameteri, glTexParameteri) + GETOPENGLFUNC(pglTexImage1D, glTexImage1D) GETOPENGLFUNC(pglTexImage2D, glTexImage2D) GETOPENGLFUNC(pglTexSubImage2D, glTexSubImage2D) @@ -590,7 +617,12 @@ typedef enum gluniform_fade_start, gluniform_fade_end, - // misc. (custom shaders) + // palette rendering + gluniform_screen_palette_tex, + gluniform_palette_lookup_tex, + gluniform_lighttable_tex, + + // misc. gluniform_leveltime, gluniform_max, @@ -656,6 +688,9 @@ static GLRGBAFloat shader_defaultcolor = {1.0f, 1.0f, 1.0f, 1.0f}; void SetupGLFunc4(void) { + /* 1.2 funcs */ + pglTexImage3D = GetGLFunc("glTexImage3D"); + /* 1.3 funcs */ pglActiveTexture = GetGLFunc("glActiveTexture"); pglMultiTexCoord2f = GetGLFunc("glMultiTexCoord2f"); pglClientActiveTexture = GetGLFunc("glClientActiveTexture"); @@ -1496,6 +1531,9 @@ EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) w = pTexInfo->width; h = pTexInfo->height; + if (w*h > 2048*2048 && pTexInfo->format != GL_TEXFMT_RGBA) + I_Error("Tried to convert too big texture: %dx%d", w, h); + if ((pTexInfo->format == GL_TEXFMT_P_8) || (pTexInfo->format == GL_TEXFMT_AP_88)) { @@ -1889,11 +1927,31 @@ static boolean Shader_CompileProgram(gl_shader_t *shader, GLint i) shader->uniforms[gluniform_fade_start] = GETUNI("fade_start"); shader->uniforms[gluniform_fade_end] = GETUNI("fade_end"); - // misc. (custom shaders) - shader->uniforms[gluniform_leveltime] = GETUNI("leveltime"); + // palette rendering + shader->uniforms[gluniform_screen_palette_tex] = GETUNI("screen_palette_tex"); + shader->uniforms[gluniform_palette_lookup_tex] = GETUNI("palette_lookup_tex"); + shader->uniforms[gluniform_lighttable_tex] = GETUNI("lighttable_tex"); + // misc. + shader->uniforms[gluniform_leveltime] = GETUNI("leveltime"); #undef GETUNI + // set permanent uniform values +#define UNIFORM_1(uniform, a, function) \ + if (uniform != -1) \ + function (uniform, a); + + pglUseProgram(shader->program); + + // texture unit numbers for the samplers used for palette rendering + UNIFORM_1(shader->uniforms[gluniform_screen_palette_tex], 2, pglUniform1i); + UNIFORM_1(shader->uniforms[gluniform_palette_lookup_tex], 1, pglUniform1i); + UNIFORM_1(shader->uniforms[gluniform_lighttable_tex], 2, pglUniform1i); + + // restore gl shader state + pglUseProgram(gl_shaderstate.program); +#undef UNIFORM_1 + return true; } @@ -1956,6 +2014,14 @@ static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FBITFIELD fade.green = byte2float[pSurf->FadeColor.s.green]; fade.blue = byte2float[pSurf->FadeColor.s.blue]; fade.alpha = byte2float[pSurf->FadeColor.s.alpha]; + + if (pSurf->LightTableId && pSurf->LightTableId != lt_downloaded) + { + pglActiveTexture(GL_TEXTURE2); + pglBindTexture(GL_TEXTURE_2D, pSurf->LightTableId); + pglActiveTexture(GL_TEXTURE0); + lt_downloaded = pSurf->LightTableId; + } } } @@ -2148,9 +2214,6 @@ EXPORT void HWRAPI(RenderSkyDome) (gl_sky_t *sky) pglDisableClientState(GL_COLOR_ARRAY); } -// ========================================================================== -// -// ========================================================================== EXPORT void HWRAPI(SetSpecialState) (hwdspecialstate_t IdState, INT32 Value) { switch (IdState) @@ -2514,6 +2577,14 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 else if (Surface->PolyColor.s.alpha == 0xFF) flags |= (PF_Occlude | PF_Masked); + if (Surface->LightTableId && Surface->LightTableId != lt_downloaded) + { + pglActiveTexture(GL_TEXTURE2); + pglBindTexture(GL_TEXTURE_2D, Surface->LightTableId); + pglActiveTexture(GL_TEXTURE0); + lt_downloaded = Surface->LightTableId; + } + SetBlend(flags); Shader_SetUniforms(Surface, &poly, &tint, &fade); @@ -3249,6 +3320,9 @@ EXPORT void HWRAPI(DrawScreenFinalTexture)(int width, int height) ClearBuffer(true, false, &clearColour); pglBindTexture(GL_TEXTURE_2D, finalScreenTexture); + // prepare shader, if it is enabled + Shader_SetUniforms(NULL, NULL, NULL, NULL); + pglColor4ubv(white); pglTexCoordPointer(2, GL_FLOAT, 0, fix); @@ -3258,4 +3332,76 @@ EXPORT void HWRAPI(DrawScreenFinalTexture)(int width, int height) tex_downloaded = finalScreenTexture; } +EXPORT void HWRAPI(SetPaletteLookup)(UINT8 *lut) +{ + if (!paletteLookupTex) + pglGenTextures(1, &paletteLookupTex); + pglActiveTexture(GL_TEXTURE1); + pglBindTexture(GL_TEXTURE_3D, paletteLookupTex); + pglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + pglTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + pglTexImage3D(GL_TEXTURE_3D, 0, GL_R8, HWR_PALETTE_LUT_SIZE, HWR_PALETTE_LUT_SIZE, HWR_PALETTE_LUT_SIZE, + 0, GL_RED, GL_UNSIGNED_BYTE, lut); + pglActiveTexture(GL_TEXTURE0); +} + +EXPORT UINT32 HWRAPI(CreateLightTable)(RGBA_t *hw_lighttable) +{ + LTListItem *item = malloc(sizeof(LTListItem)); + if (!LightTablesTail) + { + LightTablesHead = LightTablesTail = item; + } + else + { + LightTablesTail->next = item; + LightTablesTail = item; + } + item->next = NULL; + pglGenTextures(1, &item->id); + pglBindTexture(GL_TEXTURE_2D, item->id); + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + pglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + pglTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, hw_lighttable); + + // restore previously bound texture + pglBindTexture(GL_TEXTURE_2D, tex_downloaded); + + return item->id; +} + +// Delete light table textures, ids given before become invalid and must not be used. +EXPORT void HWRAPI(ClearLightTables)(void) +{ + while (LightTablesHead) + { + LTListItem *item = LightTablesHead; + pglDeleteTextures(1, (GLuint *)&item->id); + LightTablesHead = item->next; + free(item); + } + + LightTablesTail = NULL; + + // we no longer have a bound light table (if we had one), we just deleted it! + lt_downloaded = 0; +} + +// This palette is used for the palette rendering postprocessing step. +EXPORT void HWRAPI(SetScreenPalette)(RGBA_t *palette) +{ + if (memcmp(screenPalette, palette, sizeof(screenPalette))) + { + memcpy(screenPalette, palette, sizeof(screenPalette)); + if (!screenPaletteTex) + pglGenTextures(1, &screenPaletteTex); + pglActiveTexture(GL_TEXTURE2); + pglBindTexture(GL_TEXTURE_1D, screenPaletteTex); + pglTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + pglTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + pglTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, palette); + pglActiveTexture(GL_TEXTURE0); + } +} + #endif //HWRENDER diff --git a/src/m_menu.c b/src/m_menu.c index 13531f1b8..300cbcbc3 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -1443,18 +1443,20 @@ static menuitem_t OP_OpenGLOptionsMenu[] = {IT_HEADER, NULL, "General", NULL, 51}, {IT_STRING|IT_CVAR, NULL, "Shaders", &cv_glshaders, 63}, - {IT_STRING|IT_CVAR, NULL, "Lack of perspective", &cv_glshearing, 73}, - {IT_STRING|IT_CVAR, NULL, "Field of view", &cv_fov, 83}, + {IT_STRING|IT_CVAR, NULL, "Palette rendering", &cv_glpaletterendering, 73}, + {IT_STRING|IT_CVAR, NULL, "Palette bit depth", &cv_glpalettedepth, 83}, + {IT_STRING|IT_CVAR, NULL, "Lack of perspective", &cv_glshearing, 93}, + {IT_STRING|IT_CVAR, NULL, "Field of view", &cv_fov, 103}, - {IT_HEADER, NULL, "Miscellaneous", NULL, 102}, - {IT_STRING|IT_CVAR, NULL, "Bit depth", &cv_scr_depth, 114}, - {IT_STRING|IT_CVAR, NULL, "Texture filter", &cv_glfiltermode, 124}, - {IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_glanisotropicmode, 134}, + {IT_HEADER, NULL, "Miscellaneous", NULL, 122}, + {IT_STRING|IT_CVAR, NULL, "Bit depth", &cv_scr_depth, 134}, + {IT_STRING|IT_CVAR, NULL, "Texture filter", &cv_glfiltermode, 144}, + {IT_STRING|IT_CVAR, NULL, "Anisotropic", &cv_glanisotropicmode, 154}, #ifdef ALAM_LIGHTING - {IT_SUBMENU|IT_STRING, NULL, "Lighting...", &OP_OpenGLLightingDef, 144}, + {IT_SUBMENU|IT_STRING, NULL, "Lighting...", &OP_OpenGLLightingDef, 164}, #endif #if defined (_WINDOWS) && (!((defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL))) - {IT_STRING|IT_CVAR, NULL, "Fullscreen", &cv_fullscreen, 154}, + {IT_STRING|IT_CVAR, NULL, "Fullscreen", &cv_fullscreen, 174}, #endif }; diff --git a/src/r_data.c b/src/r_data.c index 2cfe9cb7a..340c1721e 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -30,6 +30,10 @@ #include "byteptr.h" #include "dehacked.h" +#ifdef HWRENDER +#include "hardware/hw_glob.h" // HWR_ClearLightTables +#endif + // // Graphics. // SRB2 graphics for walls and sprites @@ -425,6 +429,9 @@ void R_ClearColormaps(void) { // Purged by PU_LEVEL, just overwrite the pointer extra_colormaps = R_CreateDefaultColormap(true); +#ifdef HWRENDER + HWR_ClearLightTables(); +#endif } // diff --git a/src/r_defs.h b/src/r_defs.h index 9c649fbc4..0f83c7545 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -69,6 +69,11 @@ typedef struct extracolormap_s lighttable_t *colormap; +#ifdef HWRENDER + // The id of the hardware lighttable. Zero means it does not exist yet. + UINT32 gl_lighttable_id; +#endif + #ifdef EXTRACOLORMAPLUMPS lumpnum_t lump; // for colormap lump matching, init to LUMPERROR char lumpname[9]; // for netsyncing diff --git a/src/sdl/hwsym_sdl.c b/src/sdl/hwsym_sdl.c index e0c90cef4..e1441744c 100644 --- a/src/sdl/hwsym_sdl.c +++ b/src/sdl/hwsym_sdl.c @@ -74,7 +74,7 @@ void *hwSym(const char *funcName,void *handle) { void *funcPointer = NULL; #ifdef HWRENDER - if (0 == strcmp("SetPalette", funcName)) + if (0 == strcmp("SetTexturePalette", funcName)) funcPointer = &OglSdlSetPalette; GETFUNC(Init); @@ -113,6 +113,11 @@ void *hwSym(const char *funcName,void *handle) GETFUNC(SetShaderInfo); + GETFUNC(SetPaletteLookup); + GETFUNC(CreateLightTable); + GETFUNC(ClearLightTables); + GETFUNC(SetScreenPalette); + #else //HWRENDER if (0 == strcmp("FinishUpdate", funcName)) return funcPointer; //&FinishUpdate; diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 5aaf97b10..c0168d2e5 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1241,6 +1241,17 @@ void I_FinishUpdate(void) #ifdef HWRENDER else if (rendermode == render_opengl) { + // Final postprocess step of palette rendering, after everything else has been drawn. + if (HWR_ShouldUsePaletteRendering()) + { + // not using the function for its original named purpose but can be used like this too + HWR_MakeScreenFinalTexture(); + HWD.pfnSetSpecialState(HWD_SET_SHADERS, 1); + HWD.pfnSetShader(HWR_GetShaderFromTarget(SHADER_PALETTE_POSTPROCESS)); + HWR_DrawScreenFinalTexture(vid.width, vid.height); + HWD.pfnUnSetShader(); + HWD.pfnSetSpecialState(HWD_SET_SHADERS, 0); + } OglSdlFinishUpdate(cv_vidwait.value); } #endif @@ -1863,7 +1874,7 @@ void VID_StartupOpenGL(void) HWD.pfnGClipRect = hwSym("GClipRect",NULL); HWD.pfnClearMipMapCache = hwSym("ClearMipMapCache",NULL); HWD.pfnSetSpecialState = hwSym("SetSpecialState",NULL); - HWD.pfnSetPalette = hwSym("SetPalette",NULL); + HWD.pfnSetTexturePalette= hwSym("SetTexturePalette",NULL); HWD.pfnGetTextureUsed = hwSym("GetTextureUsed",NULL); HWD.pfnDrawModel = hwSym("DrawModel",NULL); HWD.pfnCreateModelVBOs = hwSym("CreateModelVBOs",NULL); @@ -1886,6 +1897,11 @@ void VID_StartupOpenGL(void) HWD.pfnSetShaderInfo = hwSym("SetShaderInfo",NULL); + HWD.pfnSetPaletteLookup = hwSym("SetPaletteLookup",NULL); + HWD.pfnCreateLightTable = hwSym("CreateLightTable",NULL); + HWD.pfnClearLightTables = hwSym("ClearLightTables",NULL); + HWD.pfnSetScreenPalette = hwSym("SetScreenPalette",NULL); + vid.glstate = HWD.pfnInit() ? VID_GL_LIBRARY_LOADED : VID_GL_LIBRARY_ERROR; // let load the OpenGL library if (vid.glstate == VID_GL_LIBRARY_ERROR) diff --git a/src/st_stuff.c b/src/st_stuff.c index a1fbbec03..cd5fdaaa5 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -209,8 +209,8 @@ void ST_doPaletteStuff(void) palette = 0; #ifdef HWRENDER - if (rendermode == render_opengl) - palette = 0; // No flashpals here in OpenGL + if (rendermode == render_opengl && !HWR_ShouldUsePaletteRendering()) + palette = 0; // Don't set the palette to a flashpal in OpenGL's truecolor mode #endif if (palette != st_palette) @@ -2786,7 +2786,7 @@ void ST_Drawer(void) //25/08/99: Hurdler: palette changes is done for all players, // not only player1! That's why this part // of code is moved somewhere else. - if (rendermode == render_soft) + if (rendermode == render_soft || HWR_ShouldUsePaletteRendering()) #endif if (rendermode != render_none) ST_doPaletteStuff();