mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2024-11-19 02:53:20 +00:00
1108 lines
30 KiB
C
1108 lines
30 KiB
C
// Emacs style mode select -*- C++ -*-
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Copyright (C) 1998-2000 by DooM Legacy Team.
|
|
//
|
|
// This program is free software; you can redistribute it and/or
|
|
// modify it under the terms of the GNU General Public License
|
|
// as published by the Free Software Foundation; either version 2
|
|
// of the License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
/// \file
|
|
/// \brief load and convert graphics to the hardware format
|
|
|
|
#include "../doomdef.h"
|
|
|
|
#ifdef HWRENDER
|
|
#include "hw_glob.h"
|
|
#include "hw_drv.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"
|
|
|
|
//Hurdler: 25/04/2000: used for new colormap code in hardware mode
|
|
//static UINT8 *gr_colormap = NULL; // by default it must be NULL ! (because colormap tables are not initialized)
|
|
boolean firetranslucent = false;
|
|
|
|
// Values set after a call to HWR_ResizeBlock()
|
|
static INT32 blocksize, blockwidth, blockheight;
|
|
|
|
INT32 patchformat = GR_TEXFMT_AP_88; // use alpha for holes
|
|
INT32 textureformat = GR_TEXFMT_P_8; // use chromakey for hole
|
|
|
|
// sprite, use alpha and chroma key for hole
|
|
static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
|
|
INT32 pblockwidth, INT32 pblockheight, INT32 blockmodulo,
|
|
INT32 ptexturewidth, INT32 ptextureheight,
|
|
INT32 originx, INT32 originy, // where to draw patch in surface block
|
|
const patch_t *realpatch, INT32 bpp)
|
|
{
|
|
INT32 x, x1, x2;
|
|
INT32 col, ncols;
|
|
fixed_t xfrac, xfracstep;
|
|
fixed_t yfrac, yfracstep, position, count;
|
|
fixed_t scale_y;
|
|
RGBA_t colortemp;
|
|
UINT8 *dest;
|
|
const UINT8 *source;
|
|
const column_t *patchcol;
|
|
UINT8 alpha;
|
|
UINT8 *block = mipmap->grInfo.data;
|
|
UINT8 texel;
|
|
UINT16 texelu16;
|
|
|
|
x1 = originx;
|
|
x2 = x1 + SHORT(realpatch->width);
|
|
|
|
if (x1 < 0)
|
|
x = 0;
|
|
else
|
|
x = x1;
|
|
|
|
if (x2 > ptexturewidth)
|
|
x2 = ptexturewidth;
|
|
|
|
if (!ptexturewidth)
|
|
return;
|
|
|
|
col = x * pblockwidth / ptexturewidth;
|
|
ncols = ((x2 - x) * pblockwidth) / ptexturewidth;
|
|
|
|
/*
|
|
CONS_Debug(DBG_RENDER, "patch %dx%d texture %dx%d block %dx%d\n", SHORT(realpatch->width),
|
|
SHORT(realpatch->height),
|
|
ptexturewidth,
|
|
textureheight,
|
|
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 = (ptexturewidth << FRACBITS) / pblockwidth;
|
|
yfracstep = (ptextureheight<< FRACBITS) / pblockheight;
|
|
if (bpp < 1 || bpp > 4)
|
|
I_Error("HWR_DrawPatchInCache: no drawer defined for this bpp (%d)\n",bpp);
|
|
|
|
for (block += col*bpp; ncols--; block += bpp, xfrac += xfracstep)
|
|
{
|
|
INT32 topdelta, prevdelta = -1;
|
|
patchcol = (const column_t *)((const UINT8 *)realpatch
|
|
+ LONG(realpatch->columnofs[xfrac>>FRACBITS]));
|
|
|
|
scale_y = (pblockheight << FRACBITS) / ptextureheight;
|
|
|
|
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];
|
|
|
|
if (firetranslucent && (transtables[(texel<<8)+0x40000]!=texel))
|
|
alpha = 0x80;
|
|
else
|
|
alpha = 0xff;
|
|
|
|
//Hurdler: not perfect, but better than holes
|
|
if (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX && (mipmap->flags & TF_CHROMAKEYED))
|
|
texel = HWR_CHROMAKEY_EQUIVALENTCOLORINDEX;
|
|
//Hurdler: 25/04/2000: now support colormap in hardware mode
|
|
else if (mipmap->colormap)
|
|
texel = mipmap->colormap[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 : texelu16 = (UINT16)((alpha<<8) | texel);
|
|
memcpy(dest, &texelu16, sizeof(UINT16));
|
|
break;
|
|
case 3 : colortemp = V_GetColor(texel);
|
|
memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
|
|
break;
|
|
case 4 : colortemp = V_GetColor(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// resize the patch to be 3dfx compliant
|
|
// set : blocksize = blockwidth * blockheight (no bpp used)
|
|
// blockwidth
|
|
// blockheight
|
|
//note : 8bit (1 byte per pixel) palettized format
|
|
static void HWR_ResizeBlock(INT32 originalwidth, INT32 originalheight,
|
|
GrTexInfo *grInfo)
|
|
{
|
|
// Build the full textures from patches.
|
|
static const GrLOD_t gr_lods[9] =
|
|
{
|
|
GR_LOD_LOG2_256,
|
|
GR_LOD_LOG2_128,
|
|
GR_LOD_LOG2_64,
|
|
GR_LOD_LOG2_32,
|
|
GR_LOD_LOG2_16,
|
|
GR_LOD_LOG2_8,
|
|
GR_LOD_LOG2_4,
|
|
GR_LOD_LOG2_2,
|
|
GR_LOD_LOG2_1
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
GrAspectRatio_t aspect;
|
|
float max_s;
|
|
float max_t;
|
|
} booring_aspect_t;
|
|
|
|
static const booring_aspect_t gr_aspects[8] =
|
|
{
|
|
{GR_ASPECT_LOG2_1x1, 255, 255},
|
|
{GR_ASPECT_LOG2_2x1, 255, 127},
|
|
{GR_ASPECT_LOG2_4x1, 255, 63},
|
|
{GR_ASPECT_LOG2_8x1, 255, 31},
|
|
|
|
{GR_ASPECT_LOG2_1x1, 255, 255},
|
|
{GR_ASPECT_LOG2_1x2, 127, 255},
|
|
{GR_ASPECT_LOG2_1x4, 63, 255},
|
|
{GR_ASPECT_LOG2_1x8, 31, 255}
|
|
};
|
|
|
|
INT32 j,k;
|
|
INT32 max,min;
|
|
|
|
// find a power of 2 width/height
|
|
if (cv_grrounddown.value)
|
|
{
|
|
blockwidth = 256;
|
|
while (originalwidth < blockwidth)
|
|
blockwidth >>= 1;
|
|
if (blockwidth < 1)
|
|
I_Error("3D GenerateTexture : too small");
|
|
|
|
blockheight = 256;
|
|
while (originalheight < blockheight)
|
|
blockheight >>= 1;
|
|
if (blockheight < 1)
|
|
I_Error("3D GenerateTexture : too small");
|
|
}
|
|
else if (cv_voodoocompatibility.value)
|
|
{
|
|
if (originalwidth > 256 || originalheight > 256)
|
|
{
|
|
blockwidth = 256;
|
|
while (originalwidth < blockwidth)
|
|
blockwidth >>= 1;
|
|
if (blockwidth < 1)
|
|
I_Error("3D GenerateTexture : too small");
|
|
|
|
blockheight = 256;
|
|
while (originalheight < blockheight)
|
|
blockheight >>= 1;
|
|
if (blockheight < 1)
|
|
I_Error("3D GenerateTexture : too small");
|
|
}
|
|
else
|
|
{
|
|
//size up to nearest power of 2
|
|
blockwidth = 1;
|
|
while (blockwidth < originalwidth)
|
|
blockwidth <<= 1;
|
|
// scale down the original graphics to fit in 256
|
|
if (blockwidth > 256)
|
|
blockwidth = 256;
|
|
//I_Error("3D GenerateTexture : too big");
|
|
|
|
//size up to nearest power of 2
|
|
blockheight = 1;
|
|
while (blockheight < originalheight)
|
|
blockheight <<= 1;
|
|
// scale down the original graphics to fit in 256
|
|
if (blockheight > 256)
|
|
blockheight = 255;
|
|
//I_Error("3D GenerateTexture : too big");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//size up to nearest power of 2
|
|
blockwidth = 1;
|
|
while (blockwidth < originalwidth)
|
|
blockwidth <<= 1;
|
|
// scale down the original graphics to fit in 256
|
|
if (blockwidth > 2048)
|
|
blockwidth = 2048;
|
|
//I_Error("3D GenerateTexture : too big");
|
|
|
|
//size up to nearest power of 2
|
|
blockheight = 1;
|
|
while (blockheight < originalheight)
|
|
blockheight <<= 1;
|
|
// scale down the original graphics to fit in 256
|
|
if (blockheight > 2048)
|
|
blockheight = 2048;
|
|
//I_Error("3D GenerateTexture : too big");
|
|
}
|
|
|
|
// do the boring LOD stuff.. blech!
|
|
if (blockwidth >= blockheight)
|
|
{
|
|
max = blockwidth;
|
|
min = blockheight;
|
|
}
|
|
else
|
|
{
|
|
max = blockheight;
|
|
min = blockwidth;
|
|
}
|
|
|
|
for (k = 2048, j = 0; k > max; j++)
|
|
k>>=1;
|
|
grInfo->smallLodLog2 = gr_lods[j];
|
|
grInfo->largeLodLog2 = gr_lods[j];
|
|
|
|
for (k = max, j = 0; k > min && j < 4; j++)
|
|
k>>=1;
|
|
// aspect ratio too small for 3Dfx (eg: 8x128 is 1x16 : use 1x8)
|
|
if (j == 4)
|
|
{
|
|
j = 3;
|
|
//CONS_Debug(DBG_RENDER, "HWR_ResizeBlock : bad aspect ratio %dx%d\n", blockwidth,blockheight);
|
|
if (blockwidth < blockheight)
|
|
blockwidth = max>>3;
|
|
else
|
|
blockheight = max>>3;
|
|
}
|
|
if (blockwidth < blockheight)
|
|
j += 4;
|
|
grInfo->aspectRatioLog2 = gr_aspects[j].aspect;
|
|
|
|
blocksize = blockwidth * blockheight;
|
|
|
|
//CONS_Debug(DBG_RENDER, "Width is %d, Height is %d\n", blockwidth, blockheight);
|
|
}
|
|
|
|
|
|
static const INT32 format2bpp[16] =
|
|
{
|
|
0, //0
|
|
0, //1
|
|
1, //2 GR_TEXFMT_ALPHA_8
|
|
1, //3 GR_TEXFMT_INTENSITY_8
|
|
1, //4 GR_TEXFMT_ALPHA_INTENSITY_44
|
|
1, //5 GR_TEXFMT_P_8
|
|
4, //6 GR_RGBA
|
|
0, //7
|
|
0, //8
|
|
0, //9
|
|
2, //10 GR_TEXFMT_RGB_565
|
|
2, //11 GR_TEXFMT_ARGB_1555
|
|
2, //12 GR_TEXFMT_ARGB_4444
|
|
2, //13 GR_TEXFMT_ALPHA_INTENSITY_88
|
|
2, //14 GR_TEXFMT_AP_88
|
|
};
|
|
|
|
static UINT8 *MakeBlock(GLMipmap_t *grMipmap)
|
|
{
|
|
UINT8 *block;
|
|
INT32 bpp, i;
|
|
UINT16 bu16 = ((0x00 <<8) | HWR_CHROMAKEY_EQUIVALENTCOLORINDEX);
|
|
|
|
bpp = format2bpp[grMipmap->grInfo.format];
|
|
block = Z_Malloc(blocksize*bpp, PU_HWRCACHE, &(grMipmap->grInfo.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++)
|
|
//[segabor]
|
|
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, GLTexture_t *grtex)
|
|
{
|
|
UINT8 *block;
|
|
texture_t *texture;
|
|
texpatch_t *patch;
|
|
patch_t *realpatch;
|
|
|
|
INT32 i;
|
|
boolean skyspecial = false; //poor hack for Legacy large skies..
|
|
|
|
texture = textures[texnum];
|
|
|
|
// hack the Legacy skies..
|
|
if (texture->name[0] == 'S' &&
|
|
texture->name[1] == 'K' &&
|
|
texture->name[2] == 'Y' &&
|
|
(texture->name[4] == 0 ||
|
|
texture->name[5] == 0)
|
|
)
|
|
{
|
|
skyspecial = true;
|
|
grtex->mipmap.flags = TF_WRAPXY; // don't use the chromakey for sky
|
|
}
|
|
else
|
|
grtex->mipmap.flags = TF_CHROMAKEYED | TF_WRAPXY;
|
|
|
|
HWR_ResizeBlock (texture->width, texture->height, &grtex->mipmap.grInfo);
|
|
grtex->mipmap.width = (UINT16)blockwidth;
|
|
grtex->mipmap.height = (UINT16)blockheight;
|
|
grtex->mipmap.grInfo.format = textureformat;
|
|
|
|
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 = V_GetColor(HWR_CHROMAKEY_EQUIVALENTCOLORINDEX);
|
|
for (j = 0; j < blockheight; j++)
|
|
{
|
|
for (i = 0; i < blockwidth; i++)
|
|
{
|
|
block[4*(j*blockwidth+i)+0] = col.s.red;
|
|
block[4*(j*blockwidth+i)+1] = col.s.green;
|
|
block[4*(j*blockwidth+i)+2] = col.s.blue;
|
|
block[4*(j*blockwidth+i)+3] = 0xff;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Composite the columns together.
|
|
for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
|
|
{
|
|
realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
|
|
HWR_DrawPatchInCache(&grtex->mipmap,
|
|
blockwidth, blockheight,
|
|
blockwidth*format2bpp[grtex->mipmap.grInfo.format],
|
|
texture->width, texture->height,
|
|
patch->originx, patch->originy,
|
|
realpatch,
|
|
format2bpp[grtex->mipmap.grInfo.format]);
|
|
Z_Unlock(realpatch);
|
|
}
|
|
//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
|
|
if (format2bpp[grtex->mipmap.grInfo.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)
|
|
{
|
|
INT32 newwidth, newheight;
|
|
|
|
// don't do it twice (like a cache)
|
|
if (grMipmap->width == 0)
|
|
{
|
|
// save the original patch header so that the GLPatch can be casted
|
|
// into a standard patch_t struct and the existing code can get the
|
|
// orginal patch dimensions and offsets.
|
|
grPatch->width = SHORT(patch->width);
|
|
grPatch->height = SHORT(patch->height);
|
|
grPatch->leftoffset = SHORT(patch->leftoffset);
|
|
grPatch->topoffset = SHORT(patch->topoffset);
|
|
|
|
// find the good 3dfx size (boring spec)
|
|
HWR_ResizeBlock (SHORT(patch->width), SHORT(patch->height), &grMipmap->grInfo);
|
|
grMipmap->width = (UINT16)blockwidth;
|
|
grMipmap->height = (UINT16)blockheight;
|
|
|
|
// no wrap around, no chroma key
|
|
grMipmap->flags = 0;
|
|
// setup the texture info
|
|
grMipmap->grInfo.format = patchformat;
|
|
}
|
|
else
|
|
{
|
|
blockwidth = grMipmap->width;
|
|
blockheight = grMipmap->height;
|
|
blocksize = blockwidth * blockheight;
|
|
}
|
|
|
|
Z_Free(grMipmap->grInfo.data);
|
|
grMipmap->grInfo.data = NULL;
|
|
|
|
// if rounddown, rounddown patches as well as textures
|
|
if (cv_grrounddown.value)
|
|
{
|
|
newwidth = blockwidth;
|
|
newheight = blockheight;
|
|
}
|
|
else if (cv_voodoocompatibility.value) // Only scales down textures that exceed 256x256.
|
|
{
|
|
// no rounddown, do not size up patches, so they don't look 'scaled'
|
|
newwidth = min(grPatch->width, blockwidth);
|
|
newheight = min(grPatch->height, blockheight);
|
|
|
|
if (newwidth > 256 || newheight > 256)
|
|
{
|
|
newwidth = blockwidth;
|
|
newheight = blockheight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no rounddown, do not size up patches, so they don't look 'scaled'
|
|
newwidth = min(grPatch->width, blockwidth);
|
|
newheight = min(grPatch->height, blockheight);
|
|
}
|
|
|
|
if (makebitmap)
|
|
{
|
|
MakeBlock(grMipmap);
|
|
|
|
HWR_DrawPatchInCache(grMipmap,
|
|
newwidth, newheight,
|
|
blockwidth*format2bpp[grMipmap->grInfo.format],
|
|
grPatch->width, grPatch->height,
|
|
0, 0,
|
|
patch,
|
|
format2bpp[grMipmap->grInfo.format]);
|
|
}
|
|
|
|
grPatch->max_s = (float)newwidth / (float)blockwidth;
|
|
grPatch->max_t = (float)newheight / (float)blockheight;
|
|
}
|
|
|
|
|
|
// =================================================
|
|
// CACHING HANDLING
|
|
// =================================================
|
|
|
|
static size_t gr_numtextures;
|
|
static GLTexture_t *gr_textures; // for ALL Doom textures
|
|
|
|
void HWR_InitTextureCache(void)
|
|
{
|
|
gr_numtextures = 0;
|
|
gr_textures = NULL;
|
|
}
|
|
|
|
|
|
// Callback function for HWR_FreeTextureCache.
|
|
static void FreeMipmapColormap(INT32 patchnum, void *patch)
|
|
{
|
|
GLPatch_t* const grpatch = patch;
|
|
(void)patchnum; //unused
|
|
while (grpatch->mipmap.nextcolormap)
|
|
{
|
|
GLMipmap_t *grmip = grpatch->mipmap.nextcolormap;
|
|
grpatch->mipmap.nextcolormap = grmip->nextcolormap;
|
|
if (grmip->grInfo.data) Z_Free(grmip->grInfo.data);
|
|
free(grmip);
|
|
}
|
|
}
|
|
|
|
void HWR_FreeTextureCache(void)
|
|
{
|
|
INT32 i;
|
|
// free references to the textures
|
|
HWD.pfnClearMipMapCache();
|
|
|
|
// free all hardware-converted graphics cached in the heap
|
|
// our gool is only the textures since user of the texture is the texture cache
|
|
Z_FreeTags(PU_HWRCACHE, PU_HWRCACHE);
|
|
Z_FreeTags(PU_HWRCACHE_UNLOCKED, PU_HWRCACHE_UNLOCKED);
|
|
|
|
// Alam: free the Z_Blocks before freeing it's users
|
|
|
|
// free all skin after each level: must be done after pfnClearMipMapCache!
|
|
for (i = 0; i < numwadfiles; i++)
|
|
M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap);
|
|
|
|
// now the heap don't have any 'user' pointing to our
|
|
// texturecache info, we can free it
|
|
if (gr_textures)
|
|
free(gr_textures);
|
|
gr_textures = NULL;
|
|
gr_numtextures = 0;
|
|
}
|
|
|
|
void HWR_PrepLevelCache(size_t pnumtextures)
|
|
{
|
|
// problem: the mipmap cache management hold a list of mipmaps.. but they are
|
|
// reallocated on each level..
|
|
//sub-optimal, but 1) just need re-download stuff in hardware cache VERY fast
|
|
// 2) sprite/menu stuff mixed with level textures so can't do anything else
|
|
|
|
// we must free it since numtextures changed
|
|
HWR_FreeTextureCache();
|
|
|
|
gr_numtextures = pnumtextures;
|
|
gr_textures = calloc(pnumtextures, sizeof (*gr_textures));
|
|
if (gr_textures == NULL)
|
|
I_Error("3D can't alloc gr_textures");
|
|
}
|
|
|
|
void HWR_SetPalette(RGBA_t *palette)
|
|
{
|
|
//Hudler: 16/10/99: added for OpenGL gamma correction
|
|
RGBA_t gamma_correction = {0x7F7F7F7F};
|
|
|
|
//Hurdler 16/10/99: added for OpenGL gamma correction
|
|
gamma_correction.s.red = (UINT8)cv_grgammared.value;
|
|
gamma_correction.s.green = (UINT8)cv_grgammagreen.value;
|
|
gamma_correction.s.blue = (UINT8)cv_grgammablue.value;
|
|
HWD.pfnSetPalette(palette, &gamma_correction);
|
|
|
|
// 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 == GR_RGBA || textureformat == GR_RGBA)
|
|
{
|
|
Z_FreeTags(PU_HWRCACHE, PU_HWRCACHE);
|
|
Z_FreeTags(PU_HWRCACHE_UNLOCKED, PU_HWRCACHE_UNLOCKED);
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------
|
|
// Make sure texture is downloaded and set it as the source
|
|
// --------------------------------------------------------------------------
|
|
GLTexture_t *HWR_GetTexture(INT32 tex)
|
|
{
|
|
GLTexture_t *grtex;
|
|
#ifdef PARANOIA
|
|
if ((unsigned)tex >= gr_numtextures)
|
|
I_Error(" HWR_GetTexture: tex >= numtextures\n");
|
|
#endif
|
|
grtex = &gr_textures[tex];
|
|
|
|
if (!grtex->mipmap.grInfo.data && !grtex->mipmap.downloaded)
|
|
HWR_GenerateTexture(tex, grtex);
|
|
|
|
HWD.pfnSetTexture(&grtex->mipmap);
|
|
|
|
// The system-memory data can be purged now.
|
|
Z_ChangeTag(grtex->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED);
|
|
|
|
return grtex;
|
|
}
|
|
|
|
|
|
static void HWR_CacheFlat(GLMipmap_t *grMipmap, lumpnum_t flatlumpnum)
|
|
{
|
|
size_t size, pflatsize;
|
|
|
|
// setup the texture info
|
|
grMipmap->grInfo.smallLodLog2 = GR_LOD_LOG2_64;
|
|
grMipmap->grInfo.largeLodLog2 = GR_LOD_LOG2_64;
|
|
grMipmap->grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
|
|
grMipmap->grInfo.format = GR_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;
|
|
|
|
// the flat raw data needn't be converted with palettized textures
|
|
W_ReadLump(flatlumpnum, Z_Malloc(W_LumpLength(flatlumpnum),
|
|
PU_HWRCACHE, &grMipmap->grInfo.data));
|
|
}
|
|
|
|
|
|
// Download a Doom 'flat' to the hardware cache and make it ready for use
|
|
void HWR_GetFlat(lumpnum_t flatlumpnum)
|
|
{
|
|
GLMipmap_t *grmip;
|
|
|
|
grmip = &HWR_GetCachedGLPatch(flatlumpnum)->mipmap;
|
|
|
|
if (!grmip->downloaded && !grmip->grInfo.data)
|
|
HWR_CacheFlat(grmip, flatlumpnum);
|
|
|
|
HWD.pfnSetTexture(grmip);
|
|
|
|
// The system-memory data can be purged now.
|
|
Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
|
|
}
|
|
|
|
//
|
|
// HWR_LoadMappedPatch(): replace the skin color of the sprite in cache
|
|
// : load it first in doom cache if not already
|
|
//
|
|
static void HWR_LoadMappedPatch(GLMipmap_t *grmip, GLPatch_t *gpatch)
|
|
{
|
|
if (!grmip->downloaded && !grmip->grInfo.data)
|
|
{
|
|
patch_t *patch = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC);
|
|
HWR_MakePatch(patch, gpatch, grmip, true);
|
|
|
|
Z_Free(patch);
|
|
}
|
|
|
|
HWD.pfnSetTexture(grmip);
|
|
|
|
// The system-memory data can be purged now.
|
|
Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
|
|
}
|
|
|
|
// -----------------+
|
|
// HWR_GetPatch : Download a patch to the hardware cache and make it ready for use
|
|
// -----------------+
|
|
void HWR_GetPatch(GLPatch_t *gpatch)
|
|
{
|
|
// is it in hardware cache
|
|
if (!gpatch->mipmap.downloaded && !gpatch->mipmap.grInfo.data)
|
|
{
|
|
// load the software patch, PU_STATIC or the Z_Malloc for hardware patch will
|
|
// flush the software patch before the conversion! oh yeah I suffered
|
|
patch_t *ptr = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC);
|
|
HWR_MakePatch(ptr, gpatch, &gpatch->mipmap, true);
|
|
|
|
// this is inefficient.. but the hardware patch in heap is purgeable so it should
|
|
// not fragment memory, and besides the REAL cache here is the hardware memory
|
|
Z_Free(ptr);
|
|
}
|
|
|
|
HWD.pfnSetTexture(&gpatch->mipmap);
|
|
|
|
// The system-memory patch data can be purged now.
|
|
Z_ChangeTag(gpatch->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED);
|
|
}
|
|
|
|
|
|
// -------------------+
|
|
// HWR_GetMappedPatch : Same as HWR_GetPatch for sprite color
|
|
// -------------------+
|
|
void HWR_GetMappedPatch(GLPatch_t *gpatch, const UINT8 *colormap)
|
|
{
|
|
GLMipmap_t *grmip, *newmip;
|
|
|
|
if (colormap == colormaps || colormap == NULL)
|
|
{
|
|
// Load the default (green) color in doom cache (temporary?) AND hardware cache
|
|
HWR_GetPatch(gpatch);
|
|
return;
|
|
}
|
|
|
|
// search for the mimmap
|
|
// skip the first (no colormap translated)
|
|
for (grmip = &gpatch->mipmap; grmip->nextcolormap; )
|
|
{
|
|
grmip = grmip->nextcolormap;
|
|
if (grmip->colormap == colormap)
|
|
{
|
|
HWR_LoadMappedPatch(grmip, gpatch);
|
|
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_FreeTextureCache
|
|
// (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
|
|
newmip = calloc(1, sizeof (*newmip));
|
|
if (newmip == NULL)
|
|
I_Error("%s: Out of memory", "HWR_GetMappedPatch");
|
|
grmip->nextcolormap = newmip;
|
|
|
|
newmip->colormap = colormap;
|
|
HWR_LoadMappedPatch(newmip, gpatch);
|
|
}
|
|
|
|
void HWR_UnlockCachedPatch(GLPatch_t *gpatch)
|
|
{
|
|
if (!gpatch)
|
|
return;
|
|
|
|
Z_ChangeTag(gpatch->mipmap.grInfo.data, PU_HWRCACHE_UNLOCKED);
|
|
Z_ChangeTag(gpatch, PU_HWRPATCHINFO_UNLOCKED);
|
|
}
|
|
|
|
static const INT32 picmode2GR[] =
|
|
{
|
|
GR_TEXFMT_P_8, // PALETTE
|
|
0, // INTENSITY (unsupported yet)
|
|
GR_TEXFMT_ALPHA_INTENSITY_88, // INTENSITY_ALPHA (corona use this)
|
|
0, // RGB24 (unsupported yet)
|
|
GR_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;
|
|
|
|
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 = V_GetColor(texel);
|
|
memcpy(dest, &col, sizeof(RGBA_t)-sizeof(UINT8));
|
|
dest += sizeof(RGBA_t)-sizeof(UINT8);
|
|
break;
|
|
case 4 :
|
|
memcpy(dest, &V_GetColor(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 :
|
|
// -----------------+
|
|
GLPatch_t *HWR_GetPic(lumpnum_t lumpnum)
|
|
{
|
|
GLPatch_t *grpatch;
|
|
|
|
grpatch = HWR_GetCachedGLPatch(lumpnum);
|
|
|
|
if (!grpatch->mipmap.downloaded && !grpatch->mipmap.grInfo.data)
|
|
{
|
|
pic_t *pic;
|
|
UINT8 *block;
|
|
size_t len;
|
|
INT32 newwidth, newheight;
|
|
|
|
pic = W_CacheLumpNum(lumpnum, PU_CACHE);
|
|
grpatch->width = SHORT(pic->width);
|
|
grpatch->height = SHORT(pic->height);
|
|
len = W_LumpLength(lumpnum) - sizeof (pic_t);
|
|
|
|
grpatch->leftoffset = 0;
|
|
grpatch->topoffset = 0;
|
|
|
|
// find the good 3dfx size (boring spec)
|
|
HWR_ResizeBlock (grpatch->width, grpatch->height, &grpatch->mipmap.grInfo);
|
|
grpatch->mipmap.width = (UINT16)blockwidth;
|
|
grpatch->mipmap.height = (UINT16)blockheight;
|
|
|
|
if (pic->mode == PALETTE)
|
|
grpatch->mipmap.grInfo.format = textureformat; // can be set by driver
|
|
else
|
|
grpatch->mipmap.grInfo.format = picmode2GR[pic->mode];
|
|
|
|
Z_Free(grpatch->mipmap.grInfo.data);
|
|
|
|
// allocate block
|
|
block = MakeBlock(&grpatch->mipmap);
|
|
|
|
// if rounddown, rounddown patches as well as textures
|
|
if (cv_grrounddown.value)
|
|
{
|
|
newwidth = blockwidth;
|
|
newheight = blockheight;
|
|
}
|
|
else if (cv_voodoocompatibility.value) // Only scales down textures that exceed 256x256.
|
|
{
|
|
// no rounddown, do not size up patches, so they don't look 'scaled'
|
|
newwidth = min(SHORT(pic->width),blockwidth);
|
|
newheight = min(SHORT(pic->height),blockheight);
|
|
|
|
if (newwidth > 256 || newheight > 256)
|
|
{
|
|
newwidth = blockwidth;
|
|
newheight = blockheight;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no rounddown, do not size up patches, so they don't look 'scaled'
|
|
newwidth = min(SHORT(pic->width),blockwidth);
|
|
newheight = min(SHORT(pic->height),blockheight);
|
|
}
|
|
|
|
|
|
if (grpatch->width == blockwidth &&
|
|
grpatch->height == blockheight &&
|
|
format2bpp[grpatch->mipmap.grInfo.format] == format2bpp[picmode2GR[pic->mode]])
|
|
{
|
|
// no conversion needed
|
|
M_Memcpy(grpatch->mipmap.grInfo.data, pic->data,len);
|
|
}
|
|
else
|
|
HWR_DrawPicInCache(block, newwidth, newheight,
|
|
blockwidth*format2bpp[grpatch->mipmap.grInfo.format],
|
|
pic,
|
|
format2bpp[grpatch->mipmap.grInfo.format]);
|
|
|
|
Z_Unlock(pic);
|
|
Z_ChangeTag(block, PU_HWRCACHE_UNLOCKED);
|
|
|
|
grpatch->mipmap.flags = 0;
|
|
grpatch->max_s = (float)newwidth / (float)blockwidth;
|
|
grpatch->max_t = (float)newheight / (float)blockheight;
|
|
}
|
|
HWD.pfnSetTexture(&grpatch->mipmap);
|
|
//CONS_Debug(DBG_RENDER, "picloaded at %x as texture %d\n",grpatch->mipmap.grInfo.data, grpatch->mipmap.downloaded);
|
|
|
|
return grpatch;
|
|
}
|
|
|
|
GLPatch_t *HWR_GetCachedGLPatchPwad(UINT16 wadnum, UINT16 lumpnum)
|
|
{
|
|
aatree_t *hwrcache = wadfiles[wadnum]->hwrcache;
|
|
GLPatch_t *grpatch;
|
|
|
|
if (!(grpatch = M_AATreeGet(hwrcache, lumpnum)))
|
|
{
|
|
grpatch = Z_Calloc(sizeof(GLPatch_t), PU_HWRPATCHINFO, NULL);
|
|
grpatch->wadnum = wadnum;
|
|
grpatch->lumpnum = lumpnum;
|
|
M_AATreeSet(hwrcache, lumpnum, grpatch);
|
|
}
|
|
|
|
return grpatch;
|
|
}
|
|
|
|
GLPatch_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->grInfo.data; // places the data directly into here, it already has the space allocated from HWR_ResizeBlock
|
|
UINT8 *flat;
|
|
UINT8 *dest, *src, texel;
|
|
RGBA_t col;
|
|
|
|
// Place the flats data into flat
|
|
W_ReadLump(fademasklumpnum, Z_Malloc(W_LumpLength(fademasklumpnum),
|
|
PU_HWRCACHE, &flat));
|
|
|
|
stepy = ((INT32)SHORT(fmheight)<<FRACBITS)/pblockheight;
|
|
stepx = ((INT32)SHORT(fmwidth)<<FRACBITS)/pblockwidth;
|
|
posy = 0;
|
|
for (j = 0; j < pblockheight; j++)
|
|
{
|
|
posx = 0;
|
|
dest = &block[j*blockwidth]; // 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 = V_GetColor(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->grInfo.format = GR_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
|
|
break;
|
|
}
|
|
|
|
// Thankfully, this will still work for this scenario
|
|
HWR_ResizeBlock(fmwidth, fmheight, &grMipmap->grInfo);
|
|
|
|
grMipmap->width = blockwidth;
|
|
grMipmap->height = blockheight;
|
|
|
|
MakeBlock(grMipmap);
|
|
|
|
HWR_DrawFadeMaskInCache(grMipmap, blockwidth, blockheight, 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 *grmip;
|
|
|
|
grmip = &HWR_GetCachedGLPatch(fademasklumpnum)->mipmap;
|
|
|
|
if (!grmip->downloaded && !grmip->grInfo.data)
|
|
HWR_CacheFadeMask(grmip, fademasklumpnum);
|
|
|
|
HWD.pfnSetTexture(grmip);
|
|
|
|
// The system-memory data can be purged now.
|
|
Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
|
|
}
|
|
|
|
#endif //HWRENDER
|