blankart/src/hardware/hw_cache.c
NepDisk 03b26a0723 Always reload texture arrays on renderer switch and bring back ingame renderer hotswapping
All this texture load blocking var does is cause issues due to order of operations and what not. Lets just kill it.

Thanks Alug for looking at this stuff for me!
2026-01-31 12:00:49 -05:00

1829 lines
49 KiB
C

// BLANKART
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2020 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file hw_cache.c
/// \brief load and convert graphics to the hardware format
#include "../doomdef.h"
#ifdef HWRENDER
#include "hw_main.h"
#include "hw_glob.h"
#include "hw_drv.h"
#include "hw_batching.h"
#include "../doomstat.h" //gamemode
#include "../i_video.h" //rendermode
#include "../r_data.h"
#include "../r_textures.h"
#include "../w_wad.h"
#include "../z_zone.h"
#include "../v_video.h"
#include "../r_draw.h"
#include "../r_patch.h"
#include "../r_main.h"
#include "../r_bsp.h"
#include "../r_picformats.h"
#include "../p_setup.h"
#include "../p_setup.h" // levelflats
#include "../p_spec.h" // anim_t
#include "../r_sky.h"
#include "../p_local.h"
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.
RGBA_t *HWR_GetTexturePalette(void)
{
return HWR_ShouldUsePaletteRendering() ? mapPalette : pLocalPalette;
}
static INT32 format2bpp(GLTextureFormat_t format)
{
if (format == GL_TEXFMT_RGBA)
return 4;
else if (format == GL_TEXFMT_ALPHA_INTENSITY_88 || format == GL_TEXFMT_AP_88)
return 2;
else
return 1;
}
// This code was originally placed directly in HWR_DrawPatchInCache.
// It is now split from it for my sanity! (and the sanity of others)
// -- Monster Iestyn (13/02/19)
static void HWR_DrawColumnInCache(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, RGBA_t *palette)
{
fixed_t yfrac, position, count;
UINT8 *dest;
const UINT8 *source;
INT32 topdelta, prevdelta = -1;
INT32 originy = 0;
// for writing a pixel to dest
RGBA_t colortemp;
UINT8 alpha;
UINT8 texel;
UINT16 texelu16;
(void)patchheight; // This parameter is unused
if (originPatch) // originPatch can be NULL here, unlike in the software version
originy = originPatch->originy;
while (patchcol->topdelta != 0xff)
{
topdelta = patchcol->topdelta;
if (topdelta <= prevdelta)
topdelta += prevdelta;
prevdelta = topdelta;
source = (const UINT8 *)patchcol + 3;
count = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
position = originy + topdelta;
yfrac = 0;
//yfracstep = (patchcol->length << FRACBITS) / count;
if (position < 0)
{
yfrac = -position<<FRACBITS;
count += (((position * scale_y) + (FRACUNIT/2)) >> FRACBITS);
position = 0;
}
position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS;
if (position < 0)
position = 0;
if (position + count >= pblockheight)
count = pblockheight - position;
dest = block + (position*blockmodulo);
while (count > 0)
{
count--;
texel = source[yfrac>>FRACBITS];
alpha = 0xFF;
// Make pixel transparent if chroma keyed
if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX))
alpha = 0x00;
//Hurdler: 25/04/2000: now support colormap in hardware mode
if (mipmap->colormap)
texel = mipmap->colormap->data[texel];
// hope compiler will get this switch out of the loops (dreams...)
// gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
// Alam: SRB2 uses Mingw, HUGS
switch (bpp)
{
case 2 : // uhhhhhhhh..........
if ((originPatch != NULL) && (originPatch->style != AST_COPY))
texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha);
texelu16 = (UINT16)((alpha<<8) | texel);
memcpy(dest, &texelu16, sizeof(UINT16));
break;
case 3 : colortemp = palette[texel];
if ((originPatch != NULL) && (originPatch->style != AST_COPY))
{
RGBA_t rgbatexel;
rgbatexel.rgba = *(UINT32 *)dest;
colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
}
memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
break;
case 4 : colortemp = palette[texel];
colortemp.s.alpha = alpha;
if ((originPatch != NULL) && (originPatch->style != AST_COPY))
{
RGBA_t rgbatexel;
rgbatexel.rgba = *(UINT32 *)dest;
colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
}
memcpy(dest, &colortemp, sizeof(RGBA_t));
break;
// default is 1
default:
if ((originPatch != NULL) && (originPatch->style != AST_COPY))
*dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha);
else
*dest = texel;
break;
}
dest += blockmodulo;
yfrac += yfracstep;
}
patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4);
}
}
static void HWR_DrawFlippedColumnInCache(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, RGBA_t *palette)
{
fixed_t yfrac, position, count;
UINT8 *dest;
const UINT8 *source;
INT32 topdelta, prevdelta = -1;
INT32 originy = 0;
// for writing a pixel to dest
RGBA_t colortemp;
UINT8 alpha;
UINT8 texel;
UINT16 texelu16;
if (originPatch) // originPatch can be NULL here, unlike in the software version
originy = originPatch->originy;
while (patchcol->topdelta != 0xff)
{
topdelta = patchcol->topdelta;
if (topdelta <= prevdelta)
topdelta += prevdelta;
prevdelta = topdelta;
topdelta = patchheight-patchcol->length-topdelta;
source = (const UINT8 *)patchcol + 3;
count = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
position = originy + topdelta;
yfrac = (patchcol->length-1) << FRACBITS;
if (position < 0)
{
yfrac += position<<FRACBITS;
count += (((position * scale_y) + (FRACUNIT/2)) >> FRACBITS);
position = 0;
}
position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS;
if (position < 0)
position = 0;
if (position + count >= pblockheight)
count = pblockheight - position;
dest = block + (position*blockmodulo);
while (count > 0)
{
count--;
texel = source[yfrac>>FRACBITS];
alpha = 0xFF;
// Make pixel transparent if chroma keyed
if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX))
alpha = 0x00;
//Hurdler: 25/04/2000: now support colormap in hardware mode
if (mipmap->colormap)
texel = mipmap->colormap->data[texel];
// hope compiler will get this switch out of the loops (dreams...)
// gcc do it ! but vcc not ! (why don't use cygwin gcc for win32 ?)
// Alam: SRB2 uses Mingw, HUGS
switch (bpp)
{
case 2 : // uhhhhhhhh..........
if ((originPatch != NULL) && (originPatch->style != AST_COPY))
texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha);
texelu16 = (UINT16)((alpha<<8) | texel);
memcpy(dest, &texelu16, sizeof(UINT16));
break;
case 3 : colortemp = palette[texel];
if ((originPatch != NULL) && (originPatch->style != AST_COPY))
{
RGBA_t rgbatexel;
rgbatexel.rgba = *(UINT32 *)dest;
colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
}
memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
break;
case 4 : colortemp = palette[texel];
colortemp.s.alpha = alpha;
if ((originPatch != NULL) && (originPatch->style != AST_COPY))
{
RGBA_t rgbatexel;
rgbatexel.rgba = *(UINT32 *)dest;
colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
}
memcpy(dest, &colortemp, sizeof(RGBA_t));
break;
// default is 1
default:
if ((originPatch != NULL) && (originPatch->style != AST_COPY))
*dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha);
else
*dest = texel;
break;
}
dest += blockmodulo;
yfrac -= yfracstep;
}
patchcol = (const column_t *)((const UINT8 *)patchcol + patchcol->length + 4);
}
}
// Simplified patch caching function
// for use by sprites and other patches that are not part of a wall texture
// no alpha or flipping should be present since we do not want non-texture graphics to have them
// no offsets are used either
// -- Monster Iestyn (13/02/19)
static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
INT32 pblockwidth, INT32 pblockheight,
INT32 pwidth, INT32 pheight,
const patch_t *realpatch)
{
INT32 ncols;
fixed_t xfrac, xfracstep;
fixed_t yfracstep, scale_y;
const column_t *patchcol;
UINT8 *block = mipmap->data;
INT32 bpp;
INT32 blockmodulo;
RGBA_t *palette;
if (pwidth <= 0 || pheight <= 0)
return;
palette = HWR_GetTexturePalette();
ncols = pwidth;
// source advance
xfrac = 0;
xfracstep = FRACUNIT;
yfracstep = FRACUNIT;
scale_y = FRACUNIT;
bpp = format2bpp(mipmap->format);
if (bpp < 1 || bpp > 4)
I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
// NOTE: should this actually be pblockwidth*bpp?
blockmodulo = pblockwidth*bpp;
// Draw each column to the block cache
for (; ncols--; block += bpp, xfrac += xfracstep)
{
patchcol = (const column_t *)((const UINT8 *)realpatch->columns + (realpatch->columnofs[xfrac>>FRACBITS]));
HWR_DrawColumnInCache(patchcol, block, mipmap,
pblockheight, blockmodulo,
yfracstep, scale_y,
NULL, pheight, // not that pheight is going to get used anyway...
bpp, palette);
}
}
// This function we use for caching patches that belong to textures
static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
INT32 pblockwidth, INT32 pblockheight,
texture_t *texture, texpatch_t *patch,
const softwarepatch_t *realpatch)
{
INT32 x, x1, x2;
INT32 col, ncols;
fixed_t xfrac, xfracstep;
fixed_t yfracstep, scale_y;
const column_t *patchcol;
UINT8 *block = mipmap->data;
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, RGBA_t *palette);
if (texture->width <= 0 || texture->height <= 0)
return;
ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedColumnInCache : HWR_DrawColumnInCache;
x1 = patch->originx;
width = SHORT(realpatch->width);
height = SHORT(realpatch->height);
x2 = x1 + width;
if (x1 > texture->width || x2 < 0)
return; // patch not located within texture's x bounds, ignore
if (patch->originy > texture->height || (patch->originy + height) < 0)
return; // patch not located within texture's y bounds, ignore
palette = HWR_GetTexturePalette();
// patch is actually inside the texture!
// now check if texture is partly off-screen and adjust accordingly
// left edge
if (x1 < 0)
x = 0;
else
x = x1;
// right edge
if (x2 > texture->width)
x2 = texture->width;
col = x * pblockwidth / texture->width;
ncols = ((x2 - x) * pblockwidth) / texture->width;
/*
CONS_Debug(DBG_RENDER, "patch %dx%d texture %dx%d block %dx%d\n",
width, height,
texture->width, texture->height,
pblockwidth, pblockheight);
CONS_Debug(DBG_RENDER, " col %d ncols %d x %d\n", col, ncols, x);
*/
// source advance
xfrac = 0;
if (x1 < 0)
xfrac = -x1<<FRACBITS;
xfracstep = (texture->width << FRACBITS) / pblockwidth;
yfracstep = (texture->height<< FRACBITS) / pblockheight;
scale_y = (pblockheight << FRACBITS) / texture->height;
bpp = format2bpp(mipmap->format);
if (bpp < 1 || bpp > 4)
I_Error("HWR_DrawTexturePatchInCache: no drawer defined for this bpp (%d)\n",bpp);
// NOTE: should this actually be pblockwidth*bpp?
blockmodulo = pblockwidth*bpp;
// Draw each column to the block cache
for (block += col*bpp; ncols--; block += bpp, xfrac += xfracstep)
{
if (patch->flip & 1)
patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[(width-1)-(xfrac>>FRACBITS)]));
else
patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
ColumnDrawerPointer(patchcol, block, mipmap,
pblockheight, blockmodulo,
yfracstep, scale_y,
patch, height,
bpp, palette);
}
}
static UINT8 *MakeBlock(GLMipmap_t *grMipmap)
{
UINT8 *block;
INT32 bpp, i;
UINT16 bu16 = ((0x00 <<8) | HWR_PATCHES_CHROMAKEY_COLORINDEX);
INT32 blocksize = (grMipmap->width * grMipmap->height);
bpp = format2bpp(grMipmap->format);
block = Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(grMipmap->data));
switch (bpp)
{
case 1: memset(block, HWR_PATCHES_CHROMAKEY_COLORINDEX, blocksize); break;
case 2:
// fill background with chromakey, alpha = 0
for (i = 0; i < blocksize; i++)
memcpy(block+i*sizeof(UINT16), &bu16, sizeof(UINT16));
break;
case 4: memset(block, 0x00, blocksize*sizeof(UINT32)); break;
}
return block;
}
//
// Create a composite texture from patches, adapt the texture size to a power of 2
// height and width for the hardware texture cache.
//
static void HWR_GenerateTexture(GLMapTexture_t *grtex, INT32 texnum, boolean noencoremap)
{
UINT8 *block;
texture_t *texture;
texpatch_t *patch;
softwarepatch_t *realpatch;
UINT8 *pdata;
INT32 blockwidth, blockheight, blocksize;
UINT8 *colormap = colormaps;
INT32 i, idx;
boolean skyspecial = false; //poor hack for Legacy large skies..
RGBA_t *palette;
palette = HWR_GetTexturePalette();
texture = textures[texnum];
grtex->mipmap.flags = TF_CHROMAKEYED | TF_WRAPXY;
grtex->mipmap.format = textureformat;
// hack the Legacy skies..
if (UNLIKELY(memcmp(texture->name, "SKY", 3) == 0 &&
(texture->name[4] == 0 || texture->name[5] == 0)))
{
skyspecial = true;
grtex->mipmap.flags &= ~TF_CHROMAKEYED; // don't use the chromakey for sky
grtex->mipmap.format = GL_TEXFMT_RGBA; // that skyspecial code below assumes this format ...
}
grtex->mipmap.width = (UINT16)texture->width;
grtex->mipmap.height = (UINT16)texture->height;
if (skyspecial)
grtex->mipmap.format = GL_TEXFMT_RGBA; // that skyspecial code below assumes this format ...
else
grtex->mipmap.format = textureformat;
if (!noencoremap && encoremap)
colormap += COLORMAP_REMAPOFFSET;
grtex->mipmap.colormap = Z_Calloc(sizeof(*grtex->mipmap.colormap), PU_HWRPATCHCOLMIPMAP, NULL);
grtex->mipmap.colormap->source = colormap;
memcpy(grtex->mipmap.colormap->data, colormap, 256 * sizeof(UINT8));
blockwidth = texture->width;
blockheight = texture->height;
blocksize = (blockwidth * blockheight);
block = MakeBlock(&grtex->mipmap);
if (skyspecial) //Hurdler: not efficient, but better than holes in the sky (and it's done only at level loading)
{
INT32 j;
RGBA_t col;
col = palette[HWR_PATCHES_CHROMAKEY_COLORINDEX];
for (idx = 0, j = 0; j < blockheight; j++)
{
for (i = 0; i < blockwidth; i++, idx++)
{
block[4*idx+0] = col.s.red;
block[4*idx+1] = col.s.green;
block[4*idx+2] = col.s.blue;
block[4*idx+3] = 0xff;
}
}
}
// Composite the columns together.
for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
{
boolean dealloc = true;
boolean doremap = W_NeedPaletteRemapPwad(patch->wad, patch->lump, false);
size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump);
pdata = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
realpatch = (softwarepatch_t *)pdata;
#ifndef NO_PNG_LUMPS
if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength))
realpatch = (softwarepatch_t *)Picture_PNGConvert(pdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
else
#endif
if (texture->type == TEXTURETYPE_FLAT)
{
if (doremap)
R_DoPaletteRemapFlat(pdata, lumplength);
realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_DOOMPATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
}
else
{
dealloc = false;
if (doremap)
R_DoPaletteRemapPatch(realpatch, lumplength);
}
HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, texture, patch, realpatch);
if (dealloc)
Z_Unlock(realpatch);
}
//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
if (format2bpp(grtex->mipmap.format)==4)
{
for (i = 3; i < blocksize*4; i += 4) // blocksize*4 because blocksize doesn't include the bpp
{
if (block[i] == 0)
{
grtex->mipmap.flags |= TF_TRANSPARENT;
break;
}
}
}
grtex->scaleX = 1.0f/(texture->width*FRACUNIT);
grtex->scaleY = 1.0f/(texture->height*FRACUNIT);
}
// patch may be NULL if grMipmap has been initialised already and makebitmap is false
void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap)
{
if (grMipmap->width == 0)
{
grMipmap->width = grMipmap->height = 1;
while (grMipmap->width < patch->width) grMipmap->width <<= 1;
while (grMipmap->height < patch->height) grMipmap->height <<= 1;
// no wrap around, no chroma key
grMipmap->flags = 0;
// setup the texture info
grMipmap->format = patchformat;
grPatch->max_s = (float)patch->width / (float)grMipmap->width;
grPatch->max_t = (float)patch->height / (float)grMipmap->height;
}
Z_Free(grMipmap->data);
grMipmap->data = NULL;
if (makebitmap)
{
MakeBlock(grMipmap);
HWR_DrawPatchInCache(grMipmap,
grMipmap->width, grMipmap->height,
patch->width, patch->height,
patch);
}
}
// =================================================
// CACHING HANDLING
// =================================================
static size_t gl_numtextures = 0; // Texture count
static GLMapTexture_t *gl_textures; // For all textures
static GLMapTexture_t *gl_flats; // For all (texture) flats, as normal flats don't need to be cached
void HWR_FreeTextureData(patch_t *patch)
{
GLPatch_t *grPatch;
if (!patch || !patch->hardware)
return;
grPatch = patch->hardware;
if (vid.glstate == VID_GL_LIBRARY_LOADED)
HWD.pfnDeleteTexture(grPatch->mipmap);
if (grPatch->mipmap->data)
Z_Free(grPatch->mipmap->data);
}
void HWR_FreeTexture(patch_t *patch)
{
if (!patch)
return;
if (patch->hardware)
{
GLPatch_t *grPatch = patch->hardware;
HWR_FreeTextureColormaps(patch);
if (grPatch->mipmap)
{
HWR_FreeTextureData(patch);
Z_Free(grPatch->mipmap);
}
Z_Free(patch->hardware);
}
patch->hardware = NULL;
}
// Called by HWR_FreePatchCache.
void HWR_FreeTextureColormaps(patch_t *patch)
{
GLPatch_t *pat;
// The patch must be valid, obviously
if (!patch)
return;
pat = patch->hardware;
if (!pat)
return;
// The mipmap must be valid, obviously
while (pat->mipmap)
{
// Confusing at first, but pat->mipmap->nextcolormap
// at the beginning of the loop is the first colormap
// from the linked list of colormaps.
GLMipmap_t *next = NULL;
// No mipmap in this patch, break out of the loop.
if (!pat->mipmap)
break;
// No colormap mipmaps either.
if (!pat->mipmap->nextcolormap)
break;
// Set the first colormap to the one that comes after it.
next = pat->mipmap->nextcolormap;
pat->mipmap->nextcolormap = next->nextcolormap;
// Free image data from memory.
if (next->data)
Z_Free(next->data);
if (next->colormap)
Z_Free(next->colormap);
next->data = NULL;
next->colormap = NULL;
HWD.pfnDeleteTexture(next);
// Free the old colormap mipmap from memory.
free(next);
}
}
static boolean FreeTextureCallback(void *mem)
{
patch_t *patch = (patch_t *)mem;
HWR_FreeTexture(patch);
return false;
}
static boolean FreeColormapsCallback(void *mem)
{
patch_t *patch = (patch_t *)mem;
HWR_FreeTextureColormaps(patch);
return false;
}
static void HWR_FreePatchCache(boolean freeall)
{
boolean (*callback)(void *mem) = FreeTextureCallback;
if (!freeall)
callback = FreeColormapsCallback;
Z_IterateTags(PU_PATCH, PU_PATCH_ROTATED, callback);
Z_IterateTags(PU_SPRITE, PU_HUDGFX, callback);
}
// free all textures after each level
void HWR_ClearAllTextures(void)
{
HWD.pfnClearMipMapCache(); // free references to the textures
HWR_FreePatchCache(true);
}
void HWR_FreeColormapCache(void)
{
HWR_FreePatchCache(false);
}
void HWR_InitMapTextures(void)
{
gl_textures = NULL;
gl_flats = NULL;
}
static void FreeMapTexture(GLMapTexture_t *tex)
{
HWD.pfnDeleteTexture(&tex->mipmap);
if (tex->mipmap.data)
Z_Free(tex->mipmap.data);
tex->mipmap.data = NULL;
}
void HWR_FreeMapTextures(void)
{
size_t i;
for (i = 0; i < gl_numtextures*2; i++)
{
FreeMapTexture(&gl_textures[i]);
FreeMapTexture(&gl_flats[i]);
}
// now the heap don't have any 'user' pointing to our
// texturecache info, we can free it
if (gl_textures)
free(gl_textures);
if (gl_flats)
free(gl_flats);
gl_textures = NULL;
gl_flats = NULL;
gl_numtextures = 0;
}
static void HWR_PrecacheLevelFlats(void)
{
levelflat_t levelflat;
size_t i, j;
// special case for encore
if (encoremap)
{
// go through all sectors to determine if it should be remapped for encore
for (i = 0; i < numsectors; i++)
{
sector_t *sec = &sectors[i];
// sector checked already?
if (sec->validcount == validcount)
continue;
sec->validcount = validcount;
// gotta check sector floor and ceiling
for (j = 0; j < 2; j++)
{
const boolean ceiling = (j == 0);
INT32 pic = ceiling ? sec->ceilingpic : sec->floorpic;
levelflat = levelflats[pic];
HWR_GetLevelFlat(&levelflat, R_NoEncore(sec, &levelflat, ceiling));
}
}
}
else
{
// on non encore we have it simple
// just load every flat in the level
for (i = 0; i < numlevelflats; i++)
{
levelflat = levelflats[i];
HWR_GetLevelFlat(&levelflat, false);
}
}
}
static void HWR_PrecacheLevelTextures(void)
{
char *texturepresent;
anim_t *anim;
size_t i, j;
INT32 h;
texturepresent = calloc(numtextures, sizeof (*texturepresent));
if (texturepresent == NULL) I_Error("%s: Out of memory looking up textures", "HWR_PrecacheLevel");
for (i = 0; i < numlines; i++)
{
line_t *line = &lines[i];
const int noencoremap = ((line->flags & ML_TFERLINE) ? 2 : 1);
// line checked already?
if (line->validcount == validcount)
continue;
line->validcount = validcount;
// two sides
for (j = 0; j < 2; j++)
{
side_t *side = &sides[line->sidenum[j]];
// Single-side linedef
if (line->sidenum[j] == 0xffff)
continue;
if (side->toptexture >= 0 && side->toptexture < numtextures)
{
texturepresent[side->toptexture] = 1|noencoremap;
}
if (side->midtexture >= 0 && side->midtexture < numtextures)
{
texturepresent[side->midtexture] = 1|noencoremap;
}
if (side->bottomtexture >= 0 && side->bottomtexture < numtextures)
{
texturepresent[side->bottomtexture] = 1|noencoremap;
}
}
}
// check for animated textures
for (anim = anims; anim < lastanim; anim++)
{
if (!texturepresent[anim->basepic])
continue;
const char texpresent = texturepresent[anim->basepic];
if (!texpresent)
continue;
if (texpresent & 1)
{
for (h = 1; h < anim->numpics; h++)
{
HWR_GetTexture(anim->basepic+h, false);
}
}
if (texpresent & 2)
{
for (h = 1; h < anim->numpics; h++)
{
HWR_GetTexture(anim->basepic+h, true);
}
}
}
// Sky texture is always present.
// Note that F_SKY1 is the name used to indicate a sky floor/ceiling as a flat,
// while the sky texture is stored like a wall texture, with a skynum dependent name.
texturepresent[skytexture] = 1;
for (i = 0; i < (unsigned)numtextures; i++)
{
const char texpresent = texturepresent[i];
if (!texpresent)
continue;
if (texpresent & 1)
{
HWR_GetTexture(i, false);
}
if (texpresent & 2)
{
HWR_GetTexture(i, true);
}
}
free(texturepresent);
}
/*static void HWR_PrecacheLevelSprites(void)
{
GLPatch_t *spritepatch;
char *spritepresent;
size_t i, j, k;
lumpnum_t lump;
thinker_t *th;
mobj_t *mo;
spriteframe_t *sf;
spritepresent = calloc(numsprites, sizeof (*spritepresent));
if (spritepresent == NULL) I_Error("%s: Out of memory looking up sprites", "HWR_PrecacheLevel");
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{
if (th->function != (actionf_p1)P_MobjThinker)
continue;
mo = (mobj_t *)th;
// ogl is weird
// for some reason it does not want to preload sprites with colormaps
// so just save us the work
if (mo->color || mo->colorized)
continue;
spritepresent[mo->sprite] = 1;
}
for (i = 0; i < numsprites; i++)
{
if (!spritepresent[i])
continue;
for (j = 0; j < sprites[i].numframes; j++)
{
sf = &sprites[i].spriteframes[j];
#define cacheang(a) {\
lump = sf->lumppat[a];\
spritepatch = W_CachePatchNum(lump, PU_CACHE);\
if (spritepatch != NULL)\
HWR_GetPatch((patch_t *)spritepatch);\
}
// see R_InitSprites for more about lumppat,lumpid
switch (sf->rotate)
{
case SRF_SINGLE:
cacheang(0);
break;
case SRF_2D:
cacheang(2);
cacheang(6);
break;
default:
k = 8;
while (k--)
cacheang(k);
break;
}
#undef cacheang
}
}
free(spritepresent);
}*/
void HWR_PrecacheLevel(void)
{
if (rendermode != render_opengl)
return;
// Precache flats.
HWR_PrecacheLevelFlats();
// prevent timeouts
NetKeepAlive();
// Precache textures.
HWR_PrecacheLevelTextures();
// prevent timeouts
NetKeepAlive();
// Precache sprites.
//HWR_PrecacheLevelSprites();
// prevent timeouts
//NetKeepAlive();
}
void HWR_LoadMapTextures(size_t pnumtextures)
{
// we must free it since numtextures may have changed
HWR_FreeMapTextures();
gl_numtextures = pnumtextures;
gl_textures = calloc(gl_numtextures, sizeof (*gl_textures)*2); // *2 - 1 for encore-remapped texture and another for noencore texture (unused when not in encore)
gl_flats = calloc(gl_numtextures, sizeof(*gl_flats)*2);
if (gl_textures == NULL || gl_flats == NULL)
I_Error("HWR_LoadMapTextures: ran out of memory for OpenGL textures");
}
// --------------------------------------------------------------------------
// Make sure texture is downloaded and set it as the source
// --------------------------------------------------------------------------
GLMapTexture_t *HWR_GetTexture(INT32 tex, boolean noencoremap)
{
GLMapTexture_t *grtex;
#ifdef PARANOIA
if ((unsigned)tex >= gl_numtextures)
I_Error("HWR_GetTexture: tex >= numtextures\n");
#endif
grtex = &gl_textures[tex*2 + (encoremap && !noencoremap ? 0 : 1)];
// Generate texture if missing from the cache
if (!grtex->mipmap.data && !grtex->mipmap.downloaded)
HWR_GenerateTexture(grtex, tex, noencoremap || !R_TextureCanRemap(tex));
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!grtex->mipmap.downloaded)
HWD.pfnSetTexture(&grtex->mipmap);
HWR_SetCurrentTexture(&grtex->mipmap);
// The system-memory data can be purged now.
Z_ChangeTag(grtex->mipmap.data, PU_HWRCACHE_UNLOCKED);
INT32 brightmapnum = R_GetTextureBrightmap(tex);
if (brightmapnum)
{
GLMapTexture_t *grtexbright;
// Every texture in memory, stored in the
// hardware renderer's bit depth format. Wow!
grtexbright = &gl_textures[brightmapnum*2 + (encoremap && !noencoremap ? 0 : 1)];
// Generate texture if missing from the cache
if (!grtexbright->mipmap.data && !grtexbright->mipmap.downloaded)
HWR_GenerateTexture(grtexbright, brightmapnum, true);
grtexbright->mipmap.flags |= TF_BRIGHTMAP;
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!grtexbright->mipmap.downloaded)
HWD.pfnSetTexture(&grtexbright->mipmap);
HWR_SetCurrentTexture(&grtexbright->mipmap);
// The system-memory data can be purged now.
Z_ChangeTag(grtexbright->mipmap.data, PU_HWRCACHE_UNLOCKED);
}
return grtex;
}
static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum)
{
UINT8 *flat;
size_t steppy;
size_t size, pflatsize;
boolean doremap;
// setup the texture info
grMipmap->format = GL_TEXFMT_P_8;
grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
size = W_LumpLength(flatlumpnum);
switch (size)
{
case 4194304: // 2048x2048 lump
pflatsize = 2048;
break;
case 1048576: // 1024x1024 lump
pflatsize = 1024;
break;
case 262144:// 512x512 lump
pflatsize = 512;
break;
case 65536: // 256x256 lump
pflatsize = 256;
break;
case 16384: // 128x128 lump
pflatsize = 128;
break;
case 1024: // 32x32 lump
pflatsize = 32;
break;
default: // 64x64 lump
pflatsize = 64;
break;
}
grMipmap->width = (UINT16)pflatsize;
grMipmap->height = (UINT16)pflatsize;
doremap = W_NeedPaletteRemap(flatlumpnum, true);
// the flat raw data needn't be converted with palettized textures
W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum),
PU_HWRCACHE, &grMipmap->data));
if (doremap)
R_DoPaletteRemapFlat(grMipmap->data, size);
flat = grMipmap->data;
for (steppy = 0; steppy < size; steppy++)
if (flat[steppy] != HWR_PATCHES_CHROMAKEY_COLORINDEX)
flat[steppy] = grMipmap->colormap->source[flat[steppy]];
}
// Download a Doom 'flat' to the hardware cache and make it ready for use
void HWR_GetRawFlat(lumpnum_t flatlumpnum, boolean noencoremap)
{
GLMipmap_t *grmip;
patch_t *patch;
UINT8 *colormap = colormaps;
if (flatlumpnum == LUMPERROR)
return;
patch = HWR_GetCachedGLPatch(flatlumpnum);
grmip = ((GLPatch_t *)Patch_AllocateHardwarePatch(patch))->mipmap;
if (!grmip->colormap)
{
if (!noencoremap && encoremap)
colormap += COLORMAP_REMAPOFFSET;
grmip->colormap = Z_Calloc(sizeof(*grmip->colormap), PU_HWRPATCHCOLMIPMAP, NULL);
grmip->colormap->source = colormap;
memcpy(grmip->colormap->data, colormap, 256 * sizeof(UINT8));
}
if (!grmip->downloaded && !grmip->data)
{
HWR_CacheFlat(grmip, flatlumpnum);
}
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!grmip->downloaded)
HWD.pfnSetTexture(grmip);
HWR_SetCurrentTexture(grmip);
// The system-memory data can be purged now.
Z_ChangeTag(grmip->data, PU_HWRCACHE_UNLOCKED);
}
void HWR_GetLevelFlat(levelflat_t *levelflat, boolean noencoremap)
{
if (levelflat->type == LEVELFLAT_NONE)
{
HWR_SetCurrentTexture(NULL);
return;
}
INT32 texturenum = texturetranslation[levelflat->texture_id];
INT32 brightmapnum = R_GetTextureBrightmap(texturenum);
if (texturenum <= 0)
{
HWR_SetCurrentTexture(NULL);
return;
}
GLMapTexture_t *grtex = &gl_flats[texturenum*2 + (encoremap && !noencoremap ? 0 : 1)];
GLMipmap_t *grMipmap = &grtex->mipmap;
if (!grMipmap->data && !grMipmap->downloaded)
{
UINT8 *flat;
UINT8 *colormap = colormaps;
grMipmap->format = GL_TEXFMT_P_8;
grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
grMipmap->width = (UINT16)textures[texturenum]->width;
grMipmap->height = (UINT16)textures[texturenum]->height;
size_t size = grMipmap->width * grMipmap->height;
flat = Picture_TextureToFlat(texturenum);
Z_ChangeTag(flat, PU_HWRCACHE);
Z_SetUser(flat, &grMipmap->data);
if (!noencoremap && encoremap)
colormap += COLORMAP_REMAPOFFSET;
grtex->mipmap.colormap = Z_Calloc(sizeof(*grtex->mipmap.colormap), PU_HWRPATCHCOLMIPMAP, NULL);
grtex->mipmap.colormap->source = colormap;
memcpy(grtex->mipmap.colormap->data, colormap, 256);
for (size_t steppy = 0; steppy < size; steppy++)
if (flat[steppy] != HWR_PATCHES_CHROMAKEY_COLORINDEX)
flat[steppy] = grtex->mipmap.colormap->source[flat[steppy]];
}
if (!grMipmap->downloaded)
HWD.pfnSetTexture(&grtex->mipmap);
HWR_SetCurrentTexture(&grtex->mipmap);
Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED);
if (brightmapnum)
{
grtex = &gl_flats[brightmapnum*2 + (encoremap && !noencoremap ? 0 : 1)];
grMipmap = &grtex->mipmap;
if (!grMipmap->data && !grMipmap->downloaded)
{
grMipmap->format = GL_TEXFMT_P_8;
grMipmap->flags = TF_WRAPXY|TF_CHROMAKEYED;
grMipmap->width = (UINT16)textures[brightmapnum]->width;
grMipmap->height = (UINT16)textures[brightmapnum]->height;
size_t size = grMipmap->width * grMipmap->height;
memcpy(Z_Malloc(size, PU_HWRCACHE, &grMipmap->data), R_GetFlatForTexture(brightmapnum), size);
}
grtex->mipmap.flags |= TF_BRIGHTMAP;
if (!grMipmap->downloaded)
HWD.pfnSetTexture(&grtex->mipmap);
HWR_SetCurrentTexture(&grtex->mipmap);
Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED);
}
}
// --------------------+
// HWR_LoadPatchMipmap : Generates a patch into a mipmap, usually the mipmap inside the patch itself
// --------------------+
static void HWR_LoadPatchMipmap(patch_t *patch, GLMipmap_t *grMipmap)
{
GLPatch_t *grPatch = patch->hardware;
if (!grMipmap->downloaded && !grMipmap->data)
HWR_MakePatch(patch, grPatch, grMipmap, true);
// If hardware does not have the texture, then call pfnSetTexture to upload it
if (!grMipmap->downloaded)
HWD.pfnSetTexture(grMipmap);
HWR_SetCurrentTexture(grMipmap);
// The system-memory data can be purged now.
Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED);
}
// ----------------------+
// HWR_UpdatePatchMipmap : Updates a mipmap.
// ----------------------+
static void HWR_UpdatePatchMipmap(patch_t *patch, GLMipmap_t *grMipmap)
{
GLPatch_t *grPatch = patch->hardware;
HWR_MakePatch(patch, grPatch, grMipmap, true);
// If hardware does not have the texture, then call pfnSetTexture to upload it
// If it does have the texture, then call pfnUpdateTexture to update it
if (!grMipmap->downloaded)
HWD.pfnSetTexture(grMipmap);
else
HWD.pfnUpdateTexture(grMipmap);
HWR_SetCurrentTexture(grMipmap);
// The system-memory data can be purged now.
Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED);
}
// -----------------+
// HWR_GetPatch : Downloads a patch to the hardware cache and make it ready for use
// -----------------+
void HWR_GetPatch(patch_t *patch)
{
if (!patch->hardware)
Patch_CreateGL(patch);
HWR_LoadPatchMipmap(patch, ((GLPatch_t *)patch->hardware)->mipmap);
}
// -------------------+
// HWR_GetMappedPatch : Same as HWR_GetPatch for sprite color
// -------------------+
void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap)
{
GLPatch_t *grPatch;
GLMipmap_t *grMipmap, *newMipmap;
if (!patch->hardware)
Patch_CreateGL(patch);
grPatch = patch->hardware;
// Blatant hack for encore colormapping aside...
if (colormap == colormaps || colormap == NULL || colormap == (const UINT8*)(COLORMAP_REMAPOFFSET))
{
// Load the default (green) color in hardware cache
HWR_GetPatch(patch);
return;
}
// search for the mipmap
// skip the first (no colormap translated)
for (grMipmap = grPatch->mipmap; LIKELY(grMipmap->nextcolormap); )
{
grMipmap = grMipmap->nextcolormap;
if (UNLIKELY(grMipmap->colormap && grMipmap->colormap->source == colormap))
{
if (memcmp(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8)))
{
memcpy(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8));
HWR_UpdatePatchMipmap(patch, grMipmap);
}
else
HWR_LoadPatchMipmap(patch, grMipmap);
return;
}
}
// not found, create it!
// If we are here, the sprite with the current colormap is not already in hardware memory
//BP: WARNING: don't free it manually without clearing the cache of harware renderer
// (it have a liste of mipmap)
// this malloc is cleared in HWR_FreeColormapCache
// (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
newMipmap = calloc(1, sizeof (*newMipmap));
if (newMipmap == NULL)
I_Error("%s: Out of memory", "HWR_GetMappedPatch");
grMipmap->nextcolormap = newMipmap;
newMipmap->colormap = Z_Calloc(sizeof(*newMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL);
newMipmap->colormap->source = colormap;
memcpy(newMipmap->colormap->data, colormap, 256 * sizeof(UINT8));
HWR_LoadPatchMipmap(patch, newMipmap);
}
void HWR_UnlockCachedPatch(GLPatch_t *gpatch)
{
if (!gpatch)
return;
Z_ChangeTag(gpatch->mipmap->data, PU_HWRCACHE_UNLOCKED);
}
static const INT32 picmode2GR[] =
{
GL_TEXFMT_P_8, // PALETTE
0, // INTENSITY (unsupported yet)
GL_TEXFMT_ALPHA_INTENSITY_88, // INTENSITY_ALPHA (corona use this)
0, // RGB24 (unsupported yet)
GL_TEXFMT_RGBA, // RGBA32 (opengl only)
};
static void HWR_DrawPicInCache(UINT8 *block, INT32 pblockwidth, INT32 pblockheight,
INT32 blockmodulo, pic_t *pic, INT32 bpp)
{
INT32 i,j;
fixed_t posx, posy, stepx, stepy;
UINT8 *dest, *src, texel;
UINT16 texelu16;
INT32 picbpp;
RGBA_t col;
RGBA_t *palette = HWR_GetTexturePalette();
stepy = ((INT32)SHORT(pic->height)<<FRACBITS)/pblockheight;
stepx = ((INT32)SHORT(pic->width)<<FRACBITS)/pblockwidth;
picbpp = format2bpp(picmode2GR[pic->mode]);
posy = 0;
for (j = 0; j < pblockheight; j++)
{
posx = 0;
dest = &block[j*blockmodulo];
src = &pic->data[(posy>>FRACBITS)*SHORT(pic->width)*picbpp];
for (i = 0; i < pblockwidth;i++)
{
switch (pic->mode)
{ // source bpp
case PALETTE :
texel = src[(posx+FRACUNIT/2)>>FRACBITS];
switch (bpp)
{ // destination bpp
case 1 :
*dest++ = texel; break;
case 2 :
texelu16 = (UINT16)(texel | 0xff00);
memcpy(dest, &texelu16, sizeof(UINT16));
dest += sizeof(UINT16);
break;
case 3 :
col = palette[texel];
memcpy(dest, &col, sizeof(RGBA_t)-sizeof(UINT8));
dest += sizeof(RGBA_t)-sizeof(UINT8);
break;
case 4 :
memcpy(dest, &palette[texel], sizeof(RGBA_t));
dest += sizeof(RGBA_t);
break;
}
break;
case INTENSITY :
*dest++ = src[(posx+FRACUNIT/2)>>FRACBITS];
break;
case INTENSITY_ALPHA : // assume dest bpp = 2
memcpy(dest, src + ((posx+FRACUNIT/2)>>FRACBITS)*sizeof(UINT16), sizeof(UINT16));
dest += sizeof(UINT16);
break;
case RGB24 :
break; // not supported yet
case RGBA32 : // assume dest bpp = 4
dest += sizeof(UINT32);
memcpy(dest, src + ((posx+FRACUNIT/2)>>FRACBITS)*sizeof(UINT32), sizeof(UINT32));
break;
}
posx += stepx;
}
posy += stepy;
}
}
// -----------------+
// HWR_GetPic : Download a Doom pic (raw row encoded with no 'holes')
// Returns :
// -----------------+
patch_t *HWR_GetPic(lumpnum_t lumpnum)
{
patch_t *patch = HWR_GetCachedGLPatch(lumpnum);
GLPatch_t *grPatch = (GLPatch_t *)(patch->hardware);
if (!grPatch->mipmap->downloaded && !grPatch->mipmap->data)
{
pic_t *pic;
UINT8 *block;
size_t len;
pic = W_CacheLumpNum(lumpnum, PU_CACHE);
patch->width = SHORT(pic->width);
patch->height = SHORT(pic->height);
len = W_LumpLength(lumpnum) - sizeof (pic_t);
grPatch->mipmap->width = (UINT16)patch->width;
grPatch->mipmap->height = (UINT16)patch->height;
if (pic->mode == PALETTE)
grPatch->mipmap->format = textureformat; // can be set by driver
else
grPatch->mipmap->format = picmode2GR[pic->mode];
Z_Free(grPatch->mipmap->data);
// allocate block
block = MakeBlock(grPatch->mipmap);
if (patch->width == SHORT(pic->width) &&
patch->height == SHORT(pic->height) &&
format2bpp(grPatch->mipmap->format) == format2bpp(picmode2GR[pic->mode]))
{
// no conversion needed
memcpy(grPatch->mipmap->data, pic->data,len);
}
else
HWR_DrawPicInCache(block, SHORT(pic->width), SHORT(pic->height),
SHORT(pic->width)*format2bpp(grPatch->mipmap->format),
pic,
format2bpp(grPatch->mipmap->format));
Z_Unlock(pic);
Z_ChangeTag(block, PU_HWRCACHE_UNLOCKED);
grPatch->mipmap->flags = 0;
grPatch->max_s = grPatch->max_t = 1.0f;
}
HWD.pfnSetTexture(grPatch->mipmap);
//CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grPatch->mipmap->data, grPatch->mipmap->downloaded);
return patch;
}
patch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum)
{
lumpcache_t *lumpcache = wadfiles[wadnum]->patchcache;
if (!lumpcache[lumpnum])
{
void *ptr = Z_Calloc(sizeof(patch_t), PU_PATCH, &lumpcache[lumpnum]);
Patch_Create(NULL, 0, ptr);
Patch_AllocateHardwarePatch(ptr);
}
return (patch_t *)(lumpcache[lumpnum]);
}
patch_t *HWR_GetCachedGLPatch(lumpnum_t lumpnum)
{
return HWR_GetCachedGLPatchPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum));
}
// Need to do this because they aren't powers of 2
static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 pblockheight,
lumpnum_t fademasklumpnum, UINT16 fmwidth, UINT16 fmheight)
{
INT32 i,j;
fixed_t posx, posy, stepx, stepy;
UINT8 *block = mipmap->data; // places the data directly into here
UINT8 *flat;
UINT8 *dest, *src, texel;
RGBA_t col;
RGBA_t *palette = HWR_GetTexturePalette();
// Place the flats data into flat
W_ReadLump(fademasklumpnum, Z_Malloc(W_LumpLength(fademasklumpnum),
PU_HWRCACHE, &flat));
stepy = ((INT32)fmheight<<FRACBITS)/pblockheight;
stepx = ((INT32)fmwidth<<FRACBITS)/pblockwidth;
posy = 0;
for (j = 0; j < pblockheight; j++)
{
posx = 0;
dest = &block[j*(mipmap->width)]; // 1bpp
src = &flat[(posy>>FRACBITS)*SHORT(fmwidth)];
for (i = 0; i < pblockwidth;i++)
{
// fademask bpp is always 1, and is used just for alpha
texel = src[(posx)>>FRACBITS];
col = palette[texel];
*dest = col.s.red; // take the red level of the colour and use it for alpha, as fademasks do
dest++;
posx += stepx;
}
posy += stepy;
}
Z_Free(flat);
}
static void HWR_CacheFadeMask(GLMipmap_t *grMipmap, lumpnum_t fademasklumpnum)
{
size_t size;
UINT16 fmheight = 0, fmwidth = 0;
// setup the texture info
grMipmap->format = GL_TEXFMT_ALPHA_8; // put the correct alpha levels straight in so I don't need to convert it later
grMipmap->flags = 0;
size = W_LumpLength(fademasklumpnum);
switch (size)
{
// None of these are powers of 2, so I'll need to do what is done for textures and make them powers of 2 before they can be used
case 256000: // 640x400
fmwidth = 640;
fmheight = 400;
break;
case 64000: // 320x200
fmwidth = 320;
fmheight = 200;
break;
case 16000: // 160x100
fmwidth = 160;
fmheight = 100;
break;
case 4000: // 80x50 (minimum)
fmwidth = 80;
fmheight = 50;
break;
default: // Bad lump
CONS_Alert(CONS_WARNING, "Fade mask lump of incorrect size, ignored\n"); // I should avoid this by checking the lumpnum in HWR_RunWipe
fmwidth = 0;
fmheight = 0;
return;
}
// Thankfully, this will still work for this scenario
grMipmap->width = fmwidth;
grMipmap->height = fmheight;
MakeBlock(grMipmap);
HWR_DrawFadeMaskInCache(grMipmap, fmwidth, fmheight, fademasklumpnum, fmwidth, fmheight);
// I DO need to convert this because it isn't power of 2 and we need the alpha
}
void HWR_GetFadeMask(lumpnum_t fademasklumpnum)
{
patch_t *patch = HWR_GetCachedGLPatch(fademasklumpnum);
GLMipmap_t *grmip = ((GLPatch_t *)Patch_AllocateHardwarePatch(patch))->mipmap;
if (!grmip->downloaded && !grmip->data)
HWR_CacheFadeMask(grmip, fademasklumpnum);
HWD.pfnSetTexture(grmip);
// The system-memory data can be purged now.
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;
}
// remap the palette from 2.1 to 2.2 indices in compatmode
RGBA_t *palcopy = NULL;
if (wadfiles[WADFILENUM(lumpnum)]->compatmode)
{
palcopy = malloc(sizeof(*palcopy)*256);
memcpy(palcopy, RGBA_converted, sizeof(*palcopy)*256);
}
if (palcopy)
{
for (i = 0; i < 256; i++)
{
RGBA_converted[i].rgba = palcopy[R_InvPaletteRemap(i)].rgba;
}
free(palcopy);
}
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;
}
// get hwr lighttable id for colormap, create it if it doesn't already exist
UINT32 HWR_GetLightTableID(extracolormap_t *colormap)
{
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);
}
return colormap->gl_lighttable_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