1457 lines
37 KiB
C++
1457 lines
37 KiB
C++
// SONIC ROBO BLAST 2
|
||
//-----------------------------------------------------------------------------
|
||
// Copyright (C) 1993-1996 by id Software, Inc.
|
||
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
||
// Copyright (C) 1999-2019 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
|
||
/// \brief load and convert graphics to the hardware format
|
||
|
||
#include "../doomdef.h"
|
||
|
||
#ifdef HWRENDER
|
||
|
||
#include <unordered_set>
|
||
|
||
#include "hw_main.h"
|
||
#include "hw_glob.h"
|
||
#include "hw_gl.h"
|
||
#include "hw_batching.h"
|
||
|
||
#include "../doomstat.h" //gamemode
|
||
#include "../i_video.h" //rendermode
|
||
#include "../r_data.h"
|
||
#include "../w_wad.h"
|
||
#include "../z_zone.h"
|
||
#include "../v_video.h"
|
||
#include "../r_draw.h"
|
||
#include "../r_main.h"
|
||
#include "../r_patch.h"
|
||
#include "../p_setup.h" // levelflats
|
||
#include "../p_spec.h" // anim_t
|
||
#include "../r_sky.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] = {}; // the palette for the currently loaded level or menu etc.
|
||
|
||
static void HWR_FreeTextureColormaps(patch_t *patch);
|
||
|
||
static INT32 format2bpp(GLTextureFormat_t format)
|
||
{
|
||
switch (format)
|
||
{
|
||
case GL_TEXFMT_RGBA:
|
||
return 4;
|
||
case GL_TEXFMT_ALPHA_INTENSITY_88:
|
||
case GL_TEXFMT_AP_88:
|
||
return 2;
|
||
default:
|
||
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;
|
||
if (position < 0)
|
||
{
|
||
yfrac = -position<<FRACBITS;
|
||
count += (((position * scale_y) + (FRACUNIT/2)) >> FRACBITS);
|
||
position = 0;
|
||
}
|
||
|
||
position = std::max(((position * scale_y) + (FRACUNIT/2)) >> FRACBITS, 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..........
|
||
texelu16 = (UINT16)((alpha<<8) | texel);
|
||
memcpy(dest, &texelu16, sizeof(UINT16));
|
||
break;
|
||
case 3 : colortemp = palette[texel];
|
||
memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
|
||
break;
|
||
case 4 : colortemp = palette[texel];
|
||
colortemp.s.alpha = alpha;
|
||
memcpy(dest, &colortemp, sizeof(RGBA_t));
|
||
break;
|
||
// default is 1
|
||
default: *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 = static_cast<UINT8 *>(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 = static_cast<UINT8 *>(mipmap->data);
|
||
INT32 bpp;
|
||
INT32 blockmodulo;
|
||
INT32 width, height;
|
||
RGBA_t *palette;
|
||
|
||
if (texture->width <= 0 || texture->height <= 0)
|
||
return;
|
||
|
||
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;
|
||
|
||
// 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)
|
||
{
|
||
patchcol = (const column_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
|
||
|
||
HWR_DrawColumnInCache(patchcol, block, mipmap,
|
||
pblockheight, blockmodulo,
|
||
yfracstep, scale_y,
|
||
patch, height,
|
||
bpp, palette);
|
||
}
|
||
}
|
||
|
||
static UINT8 *MakeBlock(GLMipmap_t *glMipmap)
|
||
{
|
||
UINT8 *block;
|
||
INT32 bpp, i;
|
||
UINT16 bu16 = ((0x00 <<8) | HWR_PATCHES_CHROMAKEY_COLORINDEX);
|
||
INT32 blocksize = (glMipmap->width * glMipmap->height);
|
||
|
||
bpp = format2bpp(glMipmap->format);
|
||
block = static_cast<UINT8 *>(Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(glMipmap->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(INT32 texnum, GLMapTexture_t *gltex, boolean noencore)
|
||
{
|
||
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..
|
||
|
||
texture = textures[texnum];
|
||
|
||
gltex->mipmap.flags = TF_CHROMAKEYED | TF_WRAPXY;
|
||
gltex->mipmap.format = static_cast<GLTextureFormat_t>(textureformat);
|
||
|
||
// hack the Legacy skies..
|
||
if (UNLIKELY(memcmp(texture->name, "SKY", 3) == 0 &&
|
||
(texture->name[4] == 0 || texture->name[5] == 0)))
|
||
{
|
||
skyspecial = true;
|
||
gltex->mipmap.flags &= ~TF_CHROMAKEYED; // don't use the chromakey for sky
|
||
gltex->mipmap.format = GL_TEXFMT_RGBA; // that skyspecial code below assumes this format ...
|
||
}
|
||
|
||
gltex->mipmap.width = (UINT16)(texture->width);
|
||
gltex->mipmap.height = (UINT16)(texture->height);
|
||
|
||
#ifdef GLENCORE
|
||
if (encoremap && !noencore)
|
||
colormap += COLORMAP_REMAPOFFSET;
|
||
#endif
|
||
|
||
gltex->mipmap.colormap = static_cast<GLColormap_t *>(Z_Calloc(sizeof(*gltex->mipmap.colormap), PU_HWRPATCHCOLMIPMAP, NULL));
|
||
gltex->mipmap.colormap->source = colormap;
|
||
memcpy(gltex->mipmap.colormap->data, colormap, 256 * sizeof(UINT8));
|
||
|
||
blockwidth = texture->width;
|
||
blockheight = texture->height;
|
||
blocksize = (blockwidth * blockheight);
|
||
block = MakeBlock(&gltex->mipmap);
|
||
|
||
if (UNLIKELY(skyspecial)) // Hurdler: not efficient, but better than holes in the sky (and it's done only at level loading)
|
||
{
|
||
INT32 j;
|
||
RGBA_t col;
|
||
|
||
const RGBA_t *palette = HWR_GetTexturePalette();
|
||
|
||
col = palette[HWR_CHROMAKEY_EQUIVALENTCOLORINDEX];
|
||
|
||
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++)
|
||
{
|
||
pdata = static_cast<UINT8 *>(W_CacheLumpNumPwad(patch->wad, patch->lump, PU_LEVEL));
|
||
realpatch = (softwarepatch_t *)pdata;
|
||
|
||
HWR_DrawTexturePatchInCache(&gltex->mipmap, blockwidth, blockheight, texture, patch, realpatch);
|
||
}
|
||
|
||
//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
|
||
if (format2bpp(gltex->mipmap.format) == 4)
|
||
{
|
||
for (i = 3; i < blocksize*4; i += 4) // blocksize*4 because blocksize doesn't include the bpp
|
||
{
|
||
if (block[i] != 0)
|
||
continue;
|
||
|
||
gltex->mipmap.flags |= TF_TRANSPARENT;
|
||
break;
|
||
}
|
||
}
|
||
|
||
gltex->scaleX = 1.0f/(texture->width*FRACUNIT);
|
||
gltex->scaleY = 1.0f/(texture->height*FRACUNIT);
|
||
}
|
||
|
||
// patch may be NULL if glMipmap has been initialised already and makebitmap is false
|
||
void HWR_MakePatch(const patch_t *patch, GLPatch_t *glPatch, GLMipmap_t *glMipmap, boolean makebitmap)
|
||
{
|
||
if (glMipmap->width == 0)
|
||
{
|
||
glMipmap->width = glMipmap->height = 1;
|
||
while (glMipmap->width < patch->width) glMipmap->width <<= 1;
|
||
while (glMipmap->height < patch->height) glMipmap->height <<= 1;
|
||
|
||
// no wrap around, no chroma key
|
||
glMipmap->flags = 0;
|
||
|
||
// setup the texture info
|
||
glMipmap->format = static_cast<GLTextureFormat_t>(patchformat);
|
||
|
||
glPatch->max_s = (float)patch->width / (float)glMipmap->width;
|
||
glPatch->max_t = (float)patch->height / (float)glMipmap->height;
|
||
}
|
||
|
||
Z_Free(glMipmap->data);
|
||
glMipmap->data = NULL;
|
||
|
||
if (makebitmap)
|
||
{
|
||
MakeBlock(glMipmap);
|
||
|
||
HWR_DrawPatchInCache(glMipmap,
|
||
glMipmap->width, glMipmap->height,
|
||
patch->width, patch->height,
|
||
patch);
|
||
}
|
||
}
|
||
|
||
// =================================================
|
||
// CACHING HANDLING
|
||
// =================================================
|
||
|
||
typedef struct
|
||
{
|
||
GLMapTexture_t normal;
|
||
#ifdef GLENCORE
|
||
GLMapTexture_t encore;
|
||
#endif
|
||
} GLMapTextureSet_t; // idk i suck at naming things
|
||
|
||
static size_t gl_numtextures = 0; // Texture count
|
||
static GLMapTextureSet_t *gl_textures; // For all textures
|
||
|
||
static void HWR_FreeTextureData(patch_t *patch)
|
||
{
|
||
GLPatch_t *glPatch;
|
||
|
||
if (!patch || !patch->hardware)
|
||
return;
|
||
|
||
glPatch = static_cast<GLPatch_t *>(patch->hardware);
|
||
|
||
if (vid.glstate == VID_GL_LIBRARY_LOADED)
|
||
GL_DeleteTexture(glPatch->mipmap);
|
||
|
||
Z_Free(glPatch->mipmap->data);
|
||
glPatch->mipmap->data = NULL;
|
||
}
|
||
|
||
void HWR_FreeTexture(patch_t *patch)
|
||
{
|
||
if (!patch)
|
||
return;
|
||
|
||
if (patch->hardware)
|
||
{
|
||
GLPatch_t *glPatch = static_cast<GLPatch_t *>(patch->hardware);
|
||
|
||
HWR_FreeTextureColormaps(patch);
|
||
|
||
if (glPatch->mipmap)
|
||
{
|
||
HWR_FreeTextureData(patch);
|
||
Z_Free(glPatch->mipmap);
|
||
glPatch->mipmap = NULL;
|
||
}
|
||
|
||
Z_Free(patch->hardware);
|
||
}
|
||
|
||
patch->hardware = NULL;
|
||
}
|
||
|
||
// Called by HWR_FreePatchCache.
|
||
static void HWR_FreeTextureColormaps(patch_t *patch)
|
||
{
|
||
GLPatch_t *pat;
|
||
|
||
// The patch must be valid, obviously
|
||
if (!patch)
|
||
return;
|
||
|
||
pat = static_cast<GLPatch_t *>(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.
|
||
Z_Free(next->data);
|
||
Z_Free(next->colormap);
|
||
next->data = NULL;
|
||
next->colormap = NULL;
|
||
GL_DeleteTexture(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_PATCH, PU_PATCH, callback);
|
||
Z_IterateTags(PU_SPRITE, PU_HUDGFX, callback);
|
||
}
|
||
|
||
// free all textures after each level
|
||
void HWR_ClearAllTextures(void)
|
||
{
|
||
// free references to the textures
|
||
GL_ClearMipMapCache();
|
||
HWR_FreePatchCache(true);
|
||
}
|
||
|
||
/*static void HWR_FreeColormapCache(void)
|
||
{
|
||
HWR_FreePatchCache(false);
|
||
}*/
|
||
|
||
void HWR_InitMapTextures(void)
|
||
{
|
||
gl_textures = NULL;
|
||
}
|
||
|
||
static void FreeMapTexture(GLMapTexture_t *tex)
|
||
{
|
||
if (!tex)
|
||
return;
|
||
|
||
GL_DeleteTexture(&tex->mipmap);
|
||
Z_Free(tex->mipmap.data);
|
||
tex->mipmap.data = NULL;
|
||
}
|
||
|
||
void HWR_FreeMapTextures(void)
|
||
{
|
||
size_t i;
|
||
|
||
if (!gl_textures)
|
||
{
|
||
gl_numtextures = 0;
|
||
return;
|
||
}
|
||
|
||
for (i = 0; i < gl_numtextures; i++)
|
||
{
|
||
FreeMapTexture(&gl_textures[i].normal);
|
||
#ifdef GLENCORE
|
||
FreeMapTexture(&gl_textures[i].encore);
|
||
#endif
|
||
}
|
||
|
||
// now the heap don't have any 'user' pointing to our
|
||
// texturecache info, we can free it
|
||
free(gl_textures);
|
||
gl_textures = NULL;
|
||
gl_numtextures = 0;
|
||
}
|
||
|
||
static void HWR_PrecacheLevelFlats(void)
|
||
{
|
||
// lookup for flats that may´ve been already loaded
|
||
// too lazy to make a hashtable or smth so unordered set will do :carbuncle:
|
||
std::unordered_set<UINT32> flatpresent;
|
||
size_t i, j;
|
||
INT32 k;
|
||
|
||
auto load_flat = [&](lumpnum_t lump, boolean noencoremap)
|
||
{
|
||
// flat already loaded?
|
||
if (flatpresent.count(lump) == 0)
|
||
{
|
||
HWR_GetFlat(lump, noencoremap);
|
||
flatpresent.insert(lump);
|
||
}
|
||
};
|
||
|
||
// special case for encore
|
||
#ifdef GLENCORE
|
||
if (encoremap)
|
||
{
|
||
// go through all sectors to determine if it should be remapped for encore
|
||
for (i = 0; i < numsectors; i++)
|
||
{
|
||
sector_t *sec = §ors[i];
|
||
|
||
// gotta check sector floor and ceiling
|
||
for (j = 0; j < 2; j++)
|
||
{
|
||
const boolean ceiling = (j == 0);
|
||
const INT32 pic = ceiling ? sec->ceilingpic : sec->floorpic;
|
||
|
||
const levelflat_t *levelflat = &levelflats[pic];
|
||
|
||
load_flat(levelflat->lumpnum, R_NoEncore(sec, ceiling));
|
||
|
||
if (levelflat->speed) // is it an animated flat ?
|
||
{
|
||
// start from 0, occasionally basenumlump != flat->lumpnum
|
||
for (k = 0; k < levelflat->numpics; k++)
|
||
{
|
||
load_flat(levelflat->baselumpnum + k, R_NoEncore(sec, ceiling));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
// on non encore we have it simple
|
||
// just load every flat in the level
|
||
for (i = 0; i < numlevelflats; i++)
|
||
{
|
||
const levelflat_t *levelflat = &levelflats[i];
|
||
|
||
load_flat(levelflat->lumpnum, false);
|
||
|
||
if (levelflat->speed) // is it an animated flat ?
|
||
{
|
||
// start from 0, occasionally basenumlump != flat->lumpnum
|
||
for (k = 0; k < levelflat->numpics; k++)
|
||
{
|
||
load_flat(levelflat->baselumpnum + k, false);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
static void HWR_PrecacheLevelTextures(void)
|
||
{
|
||
char *texturepresent;
|
||
anim_t *anim;
|
||
size_t i, j, f;
|
||
INT32 h;
|
||
|
||
texturepresent = static_cast<char *>(calloc(gl_numtextures, sizeof(*texturepresent)));
|
||
if (texturepresent == NULL)
|
||
I_Error("%s: Out of memory looking up textures", "HWR_PrecacheLevel");
|
||
|
||
// 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;
|
||
HWR_GetTexture(skytexture, false);
|
||
|
||
for (i = 0; i < numlines; i++)
|
||
{
|
||
const line_t *line = &lines[i];
|
||
#ifdef GLENCORE
|
||
const int noencoremap = ((encoremap && (line->flags & ML_TFERLINE)) ? 2 : 1);
|
||
#endif
|
||
// two sides
|
||
for (j = 0; j < 2; j++)
|
||
{
|
||
// check if single-sided linedef
|
||
if (line->sidenum[j] == 0xffff)
|
||
continue;
|
||
|
||
const side_t *side = &sides[line->sidenum[j]];
|
||
const INT32 sidetex[] = {side->toptexture, side->midtexture, side->bottomtexture};
|
||
|
||
// gotta check from top to bottom uwu
|
||
for (f = 0; f < 3; f++)
|
||
{
|
||
const INT32 texnum = sidetex[f];
|
||
|
||
if (texnum < 0 || texnum >= (signed)gl_numtextures)
|
||
continue;
|
||
|
||
if (texturepresent[texnum])
|
||
continue;
|
||
#ifdef GLENCORE
|
||
texturepresent[texnum] = 1|noencoremap;
|
||
#else
|
||
texturepresent[texnum] = 1;
|
||
#endif
|
||
HWR_GetTexture(texnum, false);
|
||
#ifdef GLENCORE
|
||
if (noencoremap & 2)
|
||
HWR_GetTexture(texnum, true);
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
for (anim = anims; anim < lastanim; anim++)
|
||
{
|
||
if (!anim->istexture)
|
||
continue;
|
||
|
||
const char texpresent = texturepresent[anim->basepic];
|
||
|
||
if (!texpresent)
|
||
continue;
|
||
|
||
for (h = 1; h < anim->numpics; h++)
|
||
{
|
||
const INT32 animtexnum = anim->basepic+h;
|
||
|
||
if (texpresent & 1)
|
||
{
|
||
HWR_GetTexture(animtexnum, false);
|
||
}
|
||
#ifdef GLENCORE
|
||
if (texpresent & 2)
|
||
{
|
||
HWR_GetTexture(animtexnum, true);
|
||
}
|
||
#endif
|
||
}
|
||
}
|
||
|
||
free(texturepresent);
|
||
}
|
||
|
||
/*static void HWR_PrecacheLevelSprites(void)
|
||
{
|
||
patch_t *spritepatch;
|
||
char *spritepresent;
|
||
size_t i, j, k;
|
||
|
||
thinker_t *th;
|
||
|
||
spritepresent = calloc(numsprites, sizeof (*spritepresent));
|
||
if (spritepresent == NULL)
|
||
I_Error("%s: Out of memory looking up sprites", "HWR_PrecacheLevel");
|
||
|
||
for (th = thinkercap.next; th != &thinkercap; th = th->next)
|
||
{
|
||
if (th->function != (actionf_p1)P_MobjThinker)
|
||
continue;
|
||
|
||
const mobj_t *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++)
|
||
{
|
||
const spriteframe_t *sf = &sprites[i].spriteframes[j];
|
||
|
||
#define cacheang(a) {\
|
||
const lumpnum_t lump = sf->lumppat[a];\
|
||
spritepatch = (patch_t *)W_CachePatchNum(lump, PU_SPRITE);\
|
||
if (spritepatch != NULL)\
|
||
HWR_GetPatch(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 (gamestate != GS_LEVEL || !cv_precachetextures.value)
|
||
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 = static_cast<GLMapTextureSet_t *>(calloc(gl_numtextures, sizeof(*gl_textures))); // *2 - 1 for encore-remapped texture and another for noencore texture (unused when not in encore), see: GLMapTextureSet_t
|
||
|
||
if (gl_textures == NULL)
|
||
I_Error("HWR_LoadMapTextures: ran out of memory for OpenGL textures. Sad!");
|
||
}
|
||
|
||
// --------------------------------------------------------------------------
|
||
// Make sure texture is downloaded and set it as the source
|
||
// --------------------------------------------------------------------------
|
||
GLMapTexture_t *HWR_GetTexture(INT32 tex, boolean noencore)
|
||
{
|
||
GLMapTexture_t *gltex;
|
||
GLMipmap_t *glMipmap;
|
||
|
||
if (tex < 0 || tex >= (signed)gl_numtextures)
|
||
{
|
||
#ifdef PARANOIA
|
||
I_Error("HWR_GetTexture: Invalid texture ID %d", tex);
|
||
#else
|
||
tex = 0;
|
||
#endif
|
||
}
|
||
|
||
// Every texture in memory, stored in the
|
||
// hardware renderer's bit depth format. Wow!
|
||
#ifdef GLENCORE
|
||
gltex = (encoremap && !noencore) ? &gl_textures[tex].encore : &gl_textures[tex].normal;
|
||
#else
|
||
gltex = &gl_textures[tex];
|
||
#endif
|
||
|
||
glMipmap = &gltex->mipmap;
|
||
|
||
// Generate texture if missing from the cache
|
||
if (!glMipmap->downloaded)
|
||
{
|
||
if (!gltex->mipmap.data)
|
||
HWR_GenerateTexture(tex, gltex, noencore);
|
||
|
||
// If hardware does not have the texture, then call GL_SetTexture to upload it
|
||
GL_SetTexture(glMipmap);
|
||
}
|
||
|
||
HWR_SetCurrentTexture(glMipmap);
|
||
|
||
// The system-memory data can be purged now.
|
||
Z_ChangeTag(glMipmap->data, PU_HWRCACHE_UNLOCKED);
|
||
|
||
return gltex;
|
||
}
|
||
|
||
static void HWR_CacheFlat(GLMipmap_t *glMipmap, lumpnum_t flatlumpnum)
|
||
{
|
||
#ifdef GLENCORE
|
||
UINT8 *flat;
|
||
size_t steppy;
|
||
#endif
|
||
size_t size, pflatsize;
|
||
boolean haschromakey = false;
|
||
|
||
// setup the texture info
|
||
glMipmap->format = GL_TEXFMT_P_8;
|
||
glMipmap->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;
|
||
}
|
||
|
||
glMipmap->width = glMipmap->height = (UINT16)pflatsize;
|
||
|
||
// the flat raw data needn't be converted with palettized textures
|
||
W_ReadLump(flatlumpnum, Z_Malloc(size, PU_HWRCACHE, &glMipmap->data));
|
||
|
||
#ifdef GLENCORE
|
||
flat = static_cast<UINT8 *>(glMipmap->data);
|
||
for (steppy = 0; steppy < size; steppy++)
|
||
{
|
||
if (flat[steppy] == HWR_PATCHES_CHROMAKEY_COLORINDEX)
|
||
{
|
||
haschromakey = true;
|
||
continue;
|
||
}
|
||
|
||
flat[steppy] = glMipmap->colormap->source[flat[steppy]];
|
||
}
|
||
#endif
|
||
|
||
if (haschromakey)
|
||
{
|
||
const char *flatname = W_CheckNameForNum(flatlumpnum);
|
||
|
||
// hack for gba rainbow roads cyan floors
|
||
if (UNLIKELY(memcmp(flatname, "GBA_RRF5", 8) == 0 && flatname[8] == 0))
|
||
{
|
||
glMipmap->flags &= ~TF_CHROMAKEYED;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Download a Doom 'flat' to the hardware cache and make it ready for use
|
||
void HWR_GetFlat(lumpnum_t flatlumpnum, boolean noencoremap)
|
||
{
|
||
GLMipmap_t *glMipmap;
|
||
patch_t *patch;
|
||
UINT8 *colormap = colormaps;
|
||
|
||
if (flatlumpnum == LUMPERROR)
|
||
return;
|
||
|
||
patch = HWR_GetCachedGLPatch(flatlumpnum);
|
||
glMipmap = ((GLPatch_t *)Patch_AllocateHardwarePatch(patch))->mipmap;
|
||
|
||
if (!glMipmap->colormap)
|
||
{
|
||
#ifdef GLENCORE
|
||
if (!noencoremap && encoremap)
|
||
colormap += COLORMAP_REMAPOFFSET;
|
||
#endif
|
||
glMipmap->colormap = static_cast<GLColormap_t *>(Z_Calloc(sizeof(*glMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL));
|
||
glMipmap->colormap->source = colormap;
|
||
memcpy(glMipmap->colormap->data, colormap, 256 * sizeof(UINT8));
|
||
}
|
||
|
||
if (!glMipmap->downloaded)
|
||
{
|
||
if (!glMipmap->data)
|
||
HWR_CacheFlat(glMipmap, flatlumpnum);
|
||
|
||
// If hardware does not have the texture, then call GL_SetTexture to upload it
|
||
GL_SetTexture(glMipmap);
|
||
}
|
||
HWR_SetCurrentTexture(glMipmap);
|
||
|
||
// The system-memory data can be purged now.
|
||
Z_ChangeTag(glMipmap->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 *glMipmap)
|
||
{
|
||
GLPatch_t *glPatch = static_cast<GLPatch_t *>(patch->hardware);
|
||
|
||
if (!glMipmap->downloaded)
|
||
{
|
||
if (!glMipmap->data)
|
||
HWR_MakePatch(patch, glPatch, glMipmap, true);
|
||
|
||
// If hardware does not have the texture, then call GL_SetTexture to upload it
|
||
GL_SetTexture(glMipmap);
|
||
}
|
||
HWR_SetCurrentTexture(glMipmap);
|
||
|
||
// The system-memory data can be purged now.
|
||
Z_ChangeTag(glMipmap->data, PU_HWRCACHE_UNLOCKED);
|
||
}
|
||
|
||
// ----------------------+
|
||
// HWR_UpdatePatchMipmap : Updates a mipmap.
|
||
// ----------------------+
|
||
static void HWR_UpdatePatchMipmap(patch_t *patch, GLMipmap_t *glMipmap)
|
||
{
|
||
GLPatch_t *glPatch = static_cast<GLPatch_t *>(patch->hardware);
|
||
HWR_MakePatch(patch, glPatch, glMipmap, true);
|
||
|
||
// If hardware does not have the texture, then call GL_SetTexture to upload it
|
||
// If it does have the texture, then call GL_UpdateTexture to update it
|
||
if (!glMipmap->downloaded)
|
||
GL_SetTexture(glMipmap);
|
||
else
|
||
GL_UpdateTexture(glMipmap);
|
||
|
||
HWR_SetCurrentTexture(glMipmap);
|
||
|
||
// The system-memory data can be purged now.
|
||
Z_ChangeTag(glMipmap->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 *glPatch;
|
||
GLMipmap_t *glMipmap, *newMipmap;
|
||
|
||
if (!patch->hardware)
|
||
Patch_CreateGL(patch);
|
||
glPatch = static_cast<GLPatch_t *>(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 (glMipmap = glPatch->mipmap; glMipmap->nextcolormap;)
|
||
{
|
||
glMipmap = glMipmap->nextcolormap;
|
||
|
||
if (glMipmap->colormap && glMipmap->colormap->source == colormap)
|
||
{
|
||
if (memcmp(glMipmap->colormap->data, colormap, 256 * sizeof(UINT8)))
|
||
{
|
||
memcpy(glMipmap->colormap->data, colormap, 256 * sizeof(UINT8));
|
||
HWR_UpdatePatchMipmap(patch, glMipmap);
|
||
}
|
||
else
|
||
HWR_LoadPatchMipmap(patch, glMipmap);
|
||
|
||
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 = static_cast<GLMipmap_t *>(calloc(1, sizeof (*newMipmap)));
|
||
if (newMipmap == NULL)
|
||
I_Error("%s: Out of memory", "HWR_GetMappedPatch");
|
||
glMipmap->nextcolormap = newMipmap;
|
||
|
||
newMipmap->colormap = static_cast<GLColormap_t *>(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 *glPatch)
|
||
{
|
||
if (!glPatch)
|
||
return;
|
||
|
||
Z_ChangeTag(glPatch->mipmap->data, PU_HWRCACHE_UNLOCKED);
|
||
}
|
||
|
||
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(static_cast<patch_t *>(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 = static_cast<UINT8 *>(mipmap->data); // places the data directly into here
|
||
UINT8 *flat;
|
||
UINT8 *dest, *src, texel;
|
||
RGBA_t col;
|
||
const 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)*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 *glMipmap, lumpnum_t fademasklumpnum)
|
||
{
|
||
size_t size;
|
||
UINT16 fmheight = 0, fmwidth = 0;
|
||
|
||
// setup the texture info
|
||
glMipmap->format = GL_TEXFMT_ALPHA_8; // put the correct alpha levels straight in so I don't need to convert it later
|
||
glMipmap->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
|
||
glMipmap->width = 0;
|
||
glMipmap->height = 0;
|
||
return;
|
||
}
|
||
|
||
// Thankfully, this will still work for this scenario
|
||
glMipmap->width = fmwidth;
|
||
glMipmap->height = fmheight;
|
||
|
||
MakeBlock(glMipmap);
|
||
|
||
HWR_DrawFadeMaskInCache(glMipmap, 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)
|
||
{
|
||
GLMipmap_t *glMipmap;
|
||
patch_t *patch;
|
||
|
||
patch = HWR_GetCachedGLPatch(fademasklumpnum);
|
||
glMipmap = ((GLPatch_t *)Patch_AllocateHardwarePatch(patch))->mipmap;
|
||
|
||
if (!glMipmap->downloaded && !glMipmap->data)
|
||
HWR_CacheFadeMask(glMipmap, fademasklumpnum);
|
||
|
||
GL_SetTexture(glMipmap);
|
||
|
||
// The system-memory data can be purged now.
|
||
Z_ChangeTag(glMipmap->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];
|
||
|
||
for (int 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;
|
||
}
|
||
GL_SetScreenPalette(crushed_palette);
|
||
}
|
||
else
|
||
{
|
||
GL_SetScreenPalette(palette);
|
||
}
|
||
|
||
// this part is responsible for keeping track of the palette OUTSIDE of a level.
|
||
if (gamestate != GS_LEVEL || gamestate == GS_TITLESCREEN)
|
||
HWR_SetMapPalette();
|
||
}
|
||
else
|
||
{
|
||
// set the palette for the textures
|
||
GL_SetPalette(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 = static_cast<UINT8 *>(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
|
||
GL_SetPaletteLookup(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)
|
||
{
|
||
// outside of a level, pLocalPalette should have PLAYPAL ready for us
|
||
palette = pLocalPalette;
|
||
}
|
||
else
|
||
{
|
||
// in a level pLocalPalette 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 = static_cast<UINT8 *>(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);
|
||
GL_SetPalette(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.
|
||
static UINT32 HWR_CreateLightTable(UINT8 *lighttable)
|
||
{
|
||
UINT32 i, id;
|
||
RGBA_t *palette = HWR_GetTexturePalette();
|
||
RGBA_t *hw_lighttable = static_cast<RGBA_t *>(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 = GL_CreateLightTable(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 = &extra_colormaps[num_extra_colormaps]; // 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)
|
||
{
|
||
GL_ClearLightTables();
|
||
}
|
||
|
||
#endif //HWRENDER
|