RingRacers/src/r_data.c
Sally Coolatta 87a223d5e2 New ice slope physics
Make them stronger by the same factor as acceleration is weakened. This feels a lot more natural, since your top speed tends to be higher on ice, which was making them feel strangely easier to climb ... even though driving on ice is hard mode (even in real life)

Also fixes an oversight where P_SpawnFriction was using the outdated movefactor constants from SRB2 instead of the ones from SRB2Kart that the rest of the codebase was using, by unifying it into one function.
2024-08-24 22:19:08 -04:00

1259 lines
34 KiB
C

// DR. ROBOTNIK'S RING RACERS
//-----------------------------------------------------------------------------
// Copyright (C) 2024 by Kart Krew.
// Copyright (C) 2020 by Sonic Team Junior.
// Copyright (C) 2000 by DooM Legacy Team.
// Copyright (C) 1996 by id Software, Inc.
//
// 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 r_data.c
/// \brief Preparation of data for rendering, generation of lookups, caching, retrieval by name
#include "doomdef.h"
#include "g_game.h"
#include "i_video.h"
#include "r_local.h"
#include "r_sky.h"
#include "p_local.h"
#include "m_misc.h"
#include "r_data.h"
#include "r_textures.h"
#include "r_patch.h"
#include "r_picformats.h"
#include "w_wad.h"
#include "z_zone.h"
#include "p_setup.h" // levelflats
#include "v_video.h" // pMasterPalette
#include "f_finale.h" // wipes
#include "byteptr.h"
#include "dehacked.h"
// DRRR
#include "k_brightmap.h"
//
// Graphics.
// SRB2 graphics for walls and sprites
// is stored in vertical runs of opaque pixels (posts).
// A column is composed of zero or more posts,
// a patch or sprite is composed of zero or more columns.
//
size_t numspritelumps, max_spritelumps;
// needed for pre rendering
sprcache_t *spritecachedinfo;
lighttable_t *colormaps;
UINT8 *encoremap;
// for debugging/info purposes
size_t flatmemory, spritememory, texturememory;
// highcolor stuff
INT16 color8to16[256]; // remap color index to highcolor rgb value
INT16 *hicolormaps; // test a 32k colormap remaps high -> high
// Blends two pixels together, using the equation
// that matches the specified alpha style.
UINT32 ASTBlendPixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha)
{
RGBA_t output;
INT16 fullalpha = (alpha - (0xFF - foreground.s.alpha));
if (style == AST_TRANSLUCENT)
{
if (fullalpha <= 0)
output.rgba = background.rgba;
else
{
// don't go too high
if (fullalpha >= 0xFF)
fullalpha = 0xFF;
alpha = (UINT8)fullalpha;
// if the background pixel is empty,
// match software and don't blend anything
if (!background.s.alpha)
{
// ...unless the foreground pixel ISN'T actually translucent.
if (alpha == 0xFF)
output.rgba = foreground.rgba;
else
output.rgba = 0;
}
else
{
UINT8 beta = (0xFF - alpha);
output.s.red = ((background.s.red * beta) + (foreground.s.red * alpha)) / 0xFF;
output.s.green = ((background.s.green * beta) + (foreground.s.green * alpha)) / 0xFF;
output.s.blue = ((background.s.blue * beta) + (foreground.s.blue * alpha)) / 0xFF;
output.s.alpha = 0xFF;
}
}
return output.rgba;
}
#define clamp_rgb(c) max(min(c, 0xFF), 0x00);
else
{
float falpha = ((float)alpha / 255.0f);
float fr = ((float)foreground.s.red * falpha);
float fg = ((float)foreground.s.green * falpha);
float fb = ((float)foreground.s.blue * falpha);
if (style == AST_ADD)
{
output.s.red = clamp_rgb((int)(background.s.red + fr));
output.s.green = clamp_rgb((int)(background.s.green + fg));
output.s.blue = clamp_rgb((int)(background.s.blue + fb));
}
else if (style == AST_SUBTRACT)
{
output.s.red = clamp_rgb((int)(background.s.red - fr));
output.s.green = clamp_rgb((int)(background.s.green - fg));
output.s.blue = clamp_rgb((int)(background.s.blue - fb));
}
else if (style == AST_REVERSESUBTRACT)
{
output.s.red = clamp_rgb((int)((-background.s.red) + fr));
output.s.green = clamp_rgb((int)((-background.s.green) + fg));
output.s.blue = clamp_rgb((int)((-background.s.blue) + fb));
}
else if (style == AST_MODULATE)
{
fr = ((float)foreground.s.red / 256.0f);
fg = ((float)foreground.s.green / 256.0f);
fb = ((float)foreground.s.blue / 256.0f);
output.s.red = clamp_rgb((int)(background.s.red * fr));
output.s.green = clamp_rgb((int)(background.s.green * fg));
output.s.blue = clamp_rgb((int)(background.s.blue * fb));
}
// just copy the pixel
else if (style == AST_COPY)
output.rgba = foreground.rgba;
output.s.alpha = 0xFF;
return output.rgba;
}
#undef clamp_rgb
return 0;
}
INT32 ASTTextureBlendingThreshold[2] = {255/11, (10*255/11)};
// Blends a pixel for a texture patch.
UINT32 ASTBlendTexturePixel(RGBA_t background, RGBA_t foreground, int style, UINT8 alpha)
{
// Alpha style set to translucent?
if (style == AST_TRANSLUCENT)
{
// Is the alpha small enough for translucency?
if (alpha <= ASTTextureBlendingThreshold[1])
{
// Is the patch way too translucent? Don't blend then.
if (alpha < ASTTextureBlendingThreshold[0])
return background.rgba;
return ASTBlendPixel(background, foreground, style, alpha);
}
else // just copy the pixel
return foreground.rgba;
}
else
return ASTBlendPixel(background, foreground, style, alpha);
}
// Blends two palette indexes for a texture patch, then
// finds the nearest palette index from the blended output.
UINT8 ASTBlendPaletteIndexes(UINT8 background, UINT8 foreground, int style, UINT8 alpha)
{
// Alpha style set to translucent?
if (style == AST_TRANSLUCENT)
{
// Is the alpha small enough for translucency?
if (alpha <= ASTTextureBlendingThreshold[1])
{
UINT8 *mytransmap;
INT32 trans;
// Is the patch way too translucent? Don't blend then.
if (alpha < ASTTextureBlendingThreshold[0])
return background;
// The equation's not exact but it works as intended. I'll call it a day for now.
trans = (8*(alpha) + 255/8)/(255 - 255/11);
mytransmap = R_GetTranslucencyTable(trans + 1);
if (background != 0xFF)
return *(mytransmap + (background<<8) + foreground);
}
else // just copy the pixel
return foreground;
}
// just copy the pixel
else if (style == AST_COPY)
return foreground;
// use ASTBlendPixel for all other blend modes
// and find the nearest colour in the palette
else if (style != AST_TRANSLUCENT)
{
RGBA_t texel;
RGBA_t bg = V_GetMasterColor(background);
RGBA_t fg = V_GetMasterColor(foreground);
texel.rgba = ASTBlendPixel(bg, fg, style, alpha);
return NearestColor(texel.s.red, texel.s.green, texel.s.blue);
}
// fallback if all above fails, somehow
// return the background pixel
return background;
}
#ifdef EXTRACOLORMAPLUMPS
static lumplist_t *colormaplumps = NULL; ///\todo free leak
static size_t numcolormaplumps = 0;
static inline lumpnum_t R_CheckNumForNameList(const char *name, lumplist_t *list, size_t listsize)
{
size_t i;
UINT16 lump;
for (i = listsize - 1; i < INT16_MAX; i--)
{
lump = W_CheckNumForNamePwad(name, list[i].wadfile, list[i].firstlump);
if (lump == INT16_MAX || lump > (list[i].firstlump + list[i].numlumps))
continue;
else
return (list[i].wadfile<<16)+lump;
}
return LUMPERROR;
}
static void R_InitExtraColormaps(void)
{
lumpnum_t startnum, endnum;
UINT16 cfile, clump;
static size_t maxcolormaplumps = 16;
for (cfile = clump = 0; cfile < numwadfiles; cfile++, clump = 0)
{
startnum = W_CheckNumForNamePwad("C_START", cfile, clump);
if (startnum == INT16_MAX)
continue;
endnum = W_CheckNumForNamePwad("C_END", cfile, clump);
if (endnum == INT16_MAX)
I_Error("R_InitExtraColormaps: C_START without C_END\n");
// This shouldn't be possible when you use the Pwad function, silly
//if (WADFILENUM(startnum) != WADFILENUM(endnum))
//I_Error("R_InitExtraColormaps: C_START and C_END in different wad files!\n");
if (numcolormaplumps >= maxcolormaplumps)
maxcolormaplumps *= 2;
colormaplumps = Z_Realloc(colormaplumps,
sizeof (*colormaplumps) * maxcolormaplumps, PU_STATIC, NULL);
colormaplumps[numcolormaplumps].wadfile = cfile;
colormaplumps[numcolormaplumps].firstlump = startnum+1;
colormaplumps[numcolormaplumps].numlumps = endnum - (startnum + 1);
numcolormaplumps++;
}
CONS_Printf(M_GetText("Number of Extra Colormaps: %s\n"), sizeu1(numcolormaplumps));
}
#endif
//
// R_InitColormaps
//
void R_InitColormaps(void)
{
size_t len;
lumpnum_t lump;
// Load in the light tables
lump = W_GetNumForName("COLORMAP");
len = W_LumpLength(lump);
if (len < COLORMAP_SIZE*2) // accomodate encore mode later
len = COLORMAP_SIZE*2;
colormaps = Z_MallocAlign(len, PU_STATIC, NULL, 8);
W_ReadLump(lump, colormaps);
// no need to init encoremap at this stage
// Init Boom colormaps.
R_ClearColormaps();
#ifdef EXTRACOLORMAPLUMPS
R_InitExtraColormaps();
#endif
}
void R_ReInitColormaps(UINT16 num, void *newencoremap, size_t encoremapsize)
{
char colormap[9] = "COLORMAP";
lumpnum_t lump;
const lumpnum_t basecolormaplump = W_GetNumForName(colormap);
if (num > 0 && num <= 10000)
snprintf(colormap, 8, "CLM%04u", num-1);
// Load in the light tables, now 64k aligned for smokie...
lump = W_GetNumForName(colormap);
if (lump == LUMPERROR)
lump = basecolormaplump;
else
{
if (W_LumpLength(lump) != W_LumpLength(basecolormaplump))
{
CONS_Alert(CONS_WARNING, "%s lump size does not match COLORMAP, results may be unexpected.\n", colormap);
}
}
W_ReadLumpHeader(lump, colormaps, W_LumpLength(basecolormaplump), 0U);
// Encore mode.
if (newencoremap)
{
lighttable_t *colormap_p, *colormap_p2;
size_t p, i;
I_Assert(encoremapsize == 256);
encoremap = Z_MallocAlign(256 + 10, PU_LEVEL, NULL, 8);
M_Memcpy(encoremap, newencoremap, encoremapsize);
colormap_p = colormap_p2 = colormaps;
colormap_p += COLORMAP_REMAPOFFSET;
for (p = 0; p < LIGHTLEVELS; p++)
{
for (i = 0; i < 256; i++)
{
*colormap_p = colormap_p2[encoremap[i]];
colormap_p++;
}
colormap_p2 += 256;
}
}
else
encoremap = NULL;
// Init Boom colormaps.
R_ClearColormaps();
}
//
// R_ClearColormaps
//
// Clears out extra colormaps between levels.
//
void R_ClearColormaps(void)
{
// Purged by PU_LEVEL, just overwrite the pointer
extra_colormaps = R_CreateDefaultColormap(true);
}
//
// R_CreateDefaultColormap()
// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
//
extracolormap_t *R_CreateDefaultColormap(boolean lighttable)
{
extracolormap_t *exc = Z_Calloc(sizeof (*exc), PU_LEVEL, NULL);
exc->fadestart = 0;
exc->fadeend = 31;
exc->flags = 0;
exc->rgba = 0;
exc->fadergba = 0x19000000;
exc->colormap = lighttable ? R_CreateLightTable(exc) : NULL;
#ifdef EXTRACOLORMAPLUMPS
exc->lump = LUMPERROR;
exc->lumpname[0] = 0;
#endif
exc->next = exc->prev = NULL;
return exc;
}
//
// R_GetDefaultColormap()
//
extracolormap_t *R_GetDefaultColormap(void)
{
#ifdef COLORMAPREVERSELIST
extracolormap_t *exc;
#endif
if (!extra_colormaps)
return (extra_colormaps = R_CreateDefaultColormap(true));
#ifdef COLORMAPREVERSELIST
for (exc = extra_colormaps; exc->next; exc = exc->next);
return exc;
#else
return extra_colormaps;
#endif
}
//
// R_CopyColormap()
// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
//
extracolormap_t *R_CopyColormap(extracolormap_t *extra_colormap, boolean lighttable)
{
extracolormap_t *exc = Z_Calloc(sizeof (*exc), PU_LEVEL, NULL);
if (!extra_colormap)
extra_colormap = R_GetDefaultColormap();
*exc = *extra_colormap;
exc->next = exc->prev = NULL;
#ifdef EXTRACOLORMAPLUMPS
strncpy(exc->lumpname, extra_colormap->lumpname, 9);
if (exc->lump != LUMPERROR && lighttable)
{
// aligned on 8 bit for asm code
exc->colormap = Z_MallocAlign(W_LumpLength(lump), PU_LEVEL, NULL, 16);
W_ReadLump(lump, exc->colormap);
}
else
#endif
if (lighttable)
exc->colormap = R_CreateLightTable(exc);
else
exc->colormap = NULL;
return exc;
}
//
// R_AddColormapToList
//
// Sets prev/next chain for extra_colormaps var
// Copypasta from P_AddFFloorToList
//
void R_AddColormapToList(extracolormap_t *extra_colormap)
{
#ifndef COLORMAPREVERSELIST
extracolormap_t *exc;
#endif
if (!extra_colormaps)
{
extra_colormaps = extra_colormap;
extra_colormap->next = 0;
extra_colormap->prev = 0;
return;
}
#ifdef COLORMAPREVERSELIST
extra_colormaps->prev = extra_colormap;
extra_colormap->next = extra_colormaps;
extra_colormaps = extra_colormap;
extra_colormap->prev = 0;
#else
for (exc = extra_colormaps; exc->next; exc = exc->next);
exc->next = extra_colormap;
extra_colormap->prev = exc;
extra_colormap->next = 0;
#endif
}
//
// R_CheckDefaultColormapByValues()
//
#ifdef EXTRACOLORMAPLUMPS
boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams,
INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags, lumpnum_t lump)
#else
boolean R_CheckDefaultColormapByValues(boolean checkrgba, boolean checkfadergba, boolean checkparams,
INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags)
#endif
{
return (
(!checkparams ? true :
(fadestart == 0
&& fadeend == 31
&& !flags)
)
&& (!checkrgba ? true : rgba == 0)
&& (!checkfadergba ? true : fadergba == 0x19000000)
#ifdef EXTRACOLORMAPLUMPS
&& lump == LUMPERROR
&& extra_colormap->lumpname[0] == 0
#endif
);
}
boolean R_CheckDefaultColormap(extracolormap_t *extra_colormap, boolean checkrgba, boolean checkfadergba, boolean checkparams)
{
if (!extra_colormap)
return true;
#ifdef EXTRACOLORMAPLUMPS
return R_CheckDefaultColormapByValues(checkrgba, checkfadergba, checkparams, extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->flags, extra_colormap->lump);
#else
return R_CheckDefaultColormapByValues(checkrgba, checkfadergba, checkparams, extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->flags);
#endif
}
boolean R_CheckEqualColormaps(extracolormap_t *exc_a, extracolormap_t *exc_b, boolean checkrgba, boolean checkfadergba, boolean checkparams)
{
// Treat NULL as default colormap
// We need this because what if one exc is a default colormap, and the other is NULL? They're really both equal.
if (!exc_a)
exc_a = R_GetDefaultColormap();
if (!exc_b)
exc_b = R_GetDefaultColormap();
if (exc_a == exc_b)
return true;
return (
(!checkparams ? true :
(exc_a->fadestart == exc_b->fadestart
&& exc_a->fadeend == exc_b->fadeend
&& exc_a->flags == exc_b->flags)
)
&& (!checkrgba ? true : exc_a->rgba == exc_b->rgba)
&& (!checkfadergba ? true : exc_a->fadergba == exc_b->fadergba)
#ifdef EXTRACOLORMAPLUMPS
&& exc_a->lump == exc_b->lump
&& !strncmp(exc_a->lumpname, exc_b->lumpname, 9)
#endif
);
}
//
// R_GetColormapFromListByValues()
// NOTE: Returns NULL if no match is found
//
#ifdef EXTRACOLORMAPLUMPS
extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags, lumpnum_t lump)
#else
extracolormap_t *R_GetColormapFromListByValues(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags)
#endif
{
extracolormap_t *exc;
UINT32 dbg_i = 0;
for (exc = extra_colormaps; exc; exc = exc->next)
{
if (rgba == exc->rgba
&& fadergba == exc->fadergba
&& fadestart == exc->fadestart
&& fadeend == exc->fadeend
&& flags == exc->flags
#ifdef EXTRACOLORMAPLUMPS
&& (lump != LUMPERROR && lump == exc->lump)
#endif
)
{
CONS_Debug(DBG_RENDER, "Found Colormap %d: rgba(%d,%d,%d,%d) fadergba(%d,%d,%d,%d)\n",
dbg_i, R_GetRgbaR(rgba), R_GetRgbaG(rgba), R_GetRgbaB(rgba), R_GetRgbaA(rgba),
R_GetRgbaR(fadergba), R_GetRgbaG(fadergba), R_GetRgbaB(fadergba), R_GetRgbaA(fadergba));
return exc;
}
dbg_i++;
}
return NULL;
}
extracolormap_t *R_GetColormapFromList(extracolormap_t *extra_colormap)
{
#ifdef EXTRACOLORMAPLUMPS
return R_GetColormapFromListByValues(extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->flags, extra_colormap->lump);
#else
return R_GetColormapFromListByValues(extra_colormap->rgba, extra_colormap->fadergba, extra_colormap->fadestart, extra_colormap->fadeend, extra_colormap->flags);
#endif
}
#ifdef EXTRACOLORMAPLUMPS
extracolormap_t *R_ColormapForName(char *name)
{
lumpnum_t lump;
extracolormap_t *exc;
lump = R_CheckNumForNameList(name, colormaplumps, numcolormaplumps);
if (lump == LUMPERROR)
I_Error("R_ColormapForName: Cannot find colormap lump %.8s\n", name);
exc = R_GetColormapFromListByValues(0, 0x19000000, 0, 31, 0, lump);
if (exc)
return exc;
exc = Z_Calloc(sizeof (*exc), PU_LEVEL, NULL);
exc->lump = lump;
strncpy(exc->lumpname, name, 9);
exc->lumpname[8] = 0;
// aligned on 8 bit for asm code
exc->colormap = Z_MallocAlign(W_LumpLength(lump), PU_LEVEL, NULL, 16);
W_ReadLump(lump, exc->colormap);
// We set all params of the colormap to normal because there
// is no real way to tell how GL should handle a colormap lump anyway..
exc->fadestart = 0;
exc->fadeend = 31;
exc->flags = 0;
exc->rgba = 0;
exc->fadergba = 0x19000000;
R_AddColormapToList(exc);
return exc;
}
#endif
//
// R_CreateColormapFromLinedef
//
// This is a more GL friendly way of doing colormaps: Specify colormap
// data in a special linedef's texture areas and use that to generate
// custom colormaps at runtime. NOTE: For GL mode, we only need to color
// data and not the colormap data.
//
static double brightChange[256], map[256][3];
static int RoundUp(double number);
lighttable_t *R_CreateLightTable(extracolormap_t *extra_colormap)
{
double cmaskr, cmaskg, cmaskb, cdestr, cdestg, cdestb, cdestbright;
double maskamt = 0, othermask = 0;
double fmaskamt = 0, fothermask = 0;
UINT8 cr = R_GetRgbaR(extra_colormap->rgba),
cg = R_GetRgbaG(extra_colormap->rgba),
cb = R_GetRgbaB(extra_colormap->rgba),
ca = R_GetRgbaA(extra_colormap->rgba),
cfr = R_GetRgbaR(extra_colormap->fadergba),
cfg = R_GetRgbaG(extra_colormap->fadergba),
cfb = R_GetRgbaB(extra_colormap->fadergba),
cfa = R_GetRgbaA(extra_colormap->fadergba);
UINT8 fadestart = extra_colormap->fadestart,
fadedist = extra_colormap->fadeend - extra_colormap->fadestart;
lighttable_t *lighttable = NULL;
size_t i;
/////////////////////
// Calc the RGBA mask
/////////////////////
cmaskr = cr;
cmaskg = cg;
cmaskb = cb;
maskamt = (double)(ca/24.0l);
othermask = 1 - maskamt;
maskamt /= 0xff;
cmaskr *= maskamt;
cmaskg *= maskamt;
cmaskb *= maskamt;
/////////////////////
// Calc the RGBA fade mask
/////////////////////
cdestr = cfr;
cdestg = cfg;
cdestb = cfb;
cdestbright = sqrt((cfr*cfr) + (cfg*cfg) + (cfb*cfb));
fmaskamt = (double)(cfa/24.0l);
fothermask = 1 - fmaskamt;
//fmaskamt /= 0xff;
(void)fothermask; // unused, but don't feel like commenting it out
/////////////////////
// This code creates the colormap array used by software renderer
/////////////////////
{
double r, g, b, cbrightness, cbest, cdist;
int p;
lighttable_t *colormap_p;
// Initialise the map and delta arrays
// map[i] stores an RGB color (as double) for index i,
// which is then converted to SRB2's palette later
// brightChange[i] is the value added/subtracted every step for the fade;
// map[i]'s values are in/decremented by it after each use
for (i = 0; i < 256; i++)
{
r = pMasterPalette[i].s.red;
g = pMasterPalette[i].s.green;
b = pMasterPalette[i].s.blue;
cbrightness = sqrt((r*r) + (g*g) + (b*b));
map[i][0] = (cbrightness * cmaskr) + (r * othermask);
if (map[i][0] > 255.0l)
map[i][0] = 255.0l;
map[i][1] = (cbrightness * cmaskg) + (g * othermask);
if (map[i][1] > 255.0l)
map[i][1] = 255.0l;
map[i][2] = (cbrightness * cmaskb) + (b * othermask);
if (map[i][2] > 255.0l)
map[i][2] = 255.0l;
// Get the "best" color.
// Our brightest color's value, if we're fading to a darker color,
// or our (inverted) darkest color's value, if we're fading to a brighter color.
if (cbrightness < cdestbright)
{
cbest = 255.0l - min(r, min(g, b));
cdist = 255.0l - max(cdestr, max(cdestg, cdestb));
}
else
{
cbest = max(r, max(g, b));
cdist = min(cdestr, min(cdestg, cdestb));
}
// Add/subtract this value during fading.
brightChange[i] = (fabs(cbest - cdist) / (double)fadedist) * fmaskamt;
}
// Now allocate memory for the actual colormap array itself!
// aligned on 8 bit for asm code
colormap_p = Z_MallocAlign((COLORMAP_SIZE * (encoremap ? 2 : 1)) + 10, PU_LEVEL, NULL, 8);
lighttable = (UINT8 *)colormap_p;
// Calculate the palette index for each palette index, for each light level
// (as well as the two unused colormap lines we inherited from Doom)
for (p = 0; p < LIGHTLEVELS; p++)
{
for (i = 0; i < 256; i++)
{
*colormap_p = NearestColor((UINT8)RoundUp(map[i][0]),
(UINT8)RoundUp(map[i][1]),
(UINT8)RoundUp(map[i][2]));
colormap_p++;
if ((UINT32)p < fadestart)
continue;
// Add/subtract towards the destination color.
if (fabs(map[i][0] - cdestr) <= brightChange[i])
map[i][0] = cdestr;
else if (map[i][0] > cdestr)
map[i][0] -= brightChange[i];
else
map[i][0] += brightChange[i];
if (fabs(map[i][1] - cdestg) <= brightChange[i])
map[i][1] = cdestg;
else if (map[i][1] > cdestg)
map[i][1] -= brightChange[i];
else
map[i][1] += brightChange[i];
if (fabs(map[i][2] - cdestb) <= brightChange[i])
map[i][2] = cdestb;
else if (map[i][2] > cdestb)
map[i][2] -= brightChange[i];
else
map[i][2] += brightChange[i];
}
}
if (encoremap)
{
lighttable_t *colormap_p2 = lighttable;
for (p = 0; p < LIGHTLEVELS; p++)
{
for (i = 0; i < 256; i++)
{
*colormap_p = colormap_p2[encoremap[i]];
colormap_p++;
}
colormap_p2 += 256;
}
}
}
return lighttable;
}
extracolormap_t *R_CreateColormapFromLinedef(char *p1, char *p2, char *p3)
{
// default values
UINT8 cr = 0, cg = 0, cb = 0, ca = 0, cfr = 0, cfg = 0, cfb = 0, cfa = 25;
UINT32 fadestart = 0, fadeend = 31;
UINT8 flags = 0;
INT32 rgba = 0, fadergba = 0x19000000;
#define HEX2INT(x) (UINT32)(x >= '0' && x <= '9' ? x - '0' : x >= 'a' && x <= 'f' ? x - 'a' + 10 : x >= 'A' && x <= 'F' ? x - 'A' + 10 : 0)
#define ALPHA2INT(x) (x >= 'a' && x <= 'z' ? x - 'a' : x >= 'A' && x <= 'Z' ? x - 'A' : x >= '0' && x <= '9' ? 25 : 0)
// Get base colormap value
// First alpha-only, then full value
if (p1[0] >= 'a' && p1[0] <= 'z' && !p1[1])
ca = (p1[0] - 'a');
else if (p1[0] == '#' && p1[1] >= 'a' && p1[1] <= 'z' && !p1[2])
ca = (p1[1] - 'a');
else if (p1[0] >= 'A' && p1[0] <= 'Z' && !p1[1])
ca = (p1[0] - 'A');
else if (p1[0] == '#' && p1[1] >= 'A' && p1[1] <= 'Z' && !p1[2])
ca = (p1[1] - 'A');
else if (p1[0] == '#')
{
// For each subsequent value, the value before it must exist
// If we don't get every value, then set alpha to max
if (p1[1] && p1[2])
{
cr = ((HEX2INT(p1[1]) * 16) + HEX2INT(p1[2]));
if (p1[3] && p1[4])
{
cg = ((HEX2INT(p1[3]) * 16) + HEX2INT(p1[4]));
if (p1[5] && p1[6])
{
cb = ((HEX2INT(p1[5]) * 16) + HEX2INT(p1[6]));
if (p1[7] >= 'a' && p1[7] <= 'z')
ca = (p1[7] - 'a');
else if (p1[7] >= 'A' && p1[7] <= 'Z')
ca = (p1[7] - 'A');
else
ca = 25;
}
else
ca = 25;
}
else
ca = 25;
}
else
ca = 25;
}
#define NUMFROMCHAR(c) (c >= '0' && c <= '9' ? c - '0' : 0)
// Get parameters like fadestart, fadeend, and flags
if (p2[0] == '#')
{
if (p2[1])
{
flags = NUMFROMCHAR(p2[1]);
if (p2[2] && p2[3])
{
fadestart = NUMFROMCHAR(p2[3]) + (NUMFROMCHAR(p2[2]) * 10);
if (p2[4] && p2[5])
fadeend = NUMFROMCHAR(p2[5]) + (NUMFROMCHAR(p2[4]) * 10);
}
}
if (fadestart > 30)
fadestart = 0;
if (fadeend > 31 || fadeend < 1)
fadeend = 31;
}
#undef NUMFROMCHAR
// Get fade (dark) colormap value
// First alpha-only, then full value
if (p3[0] >= 'a' && p3[0] <= 'z' && !p3[1])
cfa = (p3[0] - 'a');
else if (p3[0] == '#' && p3[1] >= 'a' && p3[1] <= 'z' && !p3[2])
cfa = (p3[1] - 'a');
else if (p3[0] >= 'A' && p3[0] <= 'Z' && !p3[1])
cfa = (p3[0] - 'A');
else if (p3[0] == '#' && p3[1] >= 'A' && p3[1] <= 'Z' && !p3[2])
cfa = (p3[1] - 'A');
else if (p3[0] == '#')
{
// For each subsequent value, the value before it must exist
// If we don't get every value, then set alpha to max
if (p3[1] && p3[2])
{
cfr = ((HEX2INT(p3[1]) * 16) + HEX2INT(p3[2]));
if (p3[3] && p3[4])
{
cfg = ((HEX2INT(p3[3]) * 16) + HEX2INT(p3[4]));
if (p3[5] && p3[6])
{
cfb = ((HEX2INT(p3[5]) * 16) + HEX2INT(p3[6]));
if (p3[7] >= 'a' && p3[7] <= 'z')
cfa = (p3[7] - 'a');
else if (p3[7] >= 'A' && p3[7] <= 'Z')
cfa = (p3[7] - 'A');
else
cfa = 25;
}
else
cfa = 25;
}
else
cfa = 25;
}
else
cfa = 25;
}
#undef ALPHA2INT
#undef HEX2INT
if (encoremap)
{
UINT8 j = encoremap[NearestColor((UINT8)cr, (UINT8)cg, (UINT8)cb)];
//CONS_Printf("R_CreateColormap: encoremap[%d] = %d\n", j, encoremap[j]);
cr = pLocalPalette[j].s.red;
cg = pLocalPalette[j].s.green;
cb = pLocalPalette[j].s.blue;
}
// Pack rgba values into combined var
// OpenGL also uses this instead of lighttables for rendering
rgba = R_PutRgbaRGBA(cr, cg, cb, ca);
fadergba = R_PutRgbaRGBA(cfr, cfg, cfb, cfa);
return R_CreateColormap(rgba, fadergba, fadestart, fadeend, flags);
}
extracolormap_t *R_CreateColormap(INT32 rgba, INT32 fadergba, UINT8 fadestart, UINT8 fadeend, UINT8 flags)
{
extracolormap_t *extra_colormap;
// Did we just make a default colormap?
#ifdef EXTRACOLORMAPLUMPS
if (R_CheckDefaultColormapByValues(true, true, true, rgba, fadergba, fadestart, fadeend, flags, LUMPERROR))
return NULL;
#else
if (R_CheckDefaultColormapByValues(true, true, true, rgba, fadergba, fadestart, fadeend, flags))
return NULL;
#endif
// Look for existing colormaps
#ifdef EXTRACOLORMAPLUMPS
extra_colormap = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, flags, LUMPERROR);
#else
extra_colormap = R_GetColormapFromListByValues(rgba, fadergba, fadestart, fadeend, flags);
#endif
if (extra_colormap)
return extra_colormap;
CONS_Debug(DBG_RENDER, "Creating Colormap: rgba(%x) fadergba(%x)\n", rgba, fadergba);
extra_colormap = Z_Calloc(sizeof(*extra_colormap), PU_LEVEL, NULL);
extra_colormap->fadestart = (UINT16)fadestart;
extra_colormap->fadeend = (UINT16)fadeend;
extra_colormap->flags = flags;
extra_colormap->rgba = rgba;
extra_colormap->fadergba = fadergba;
#ifdef EXTRACOLORMAPLUMPS
extra_colormap->lump = LUMPERROR;
extra_colormap->lumpname[0] = 0;
#endif
// Having lighttables for alpha-only entries is kind of pointless,
// but if there happens to be a matching rgba entry that is NOT alpha-only (but has same rgb values),
// then it needs this lighttable because we share matching entries.
extra_colormap->colormap = R_CreateLightTable(extra_colormap);
R_AddColormapToList(extra_colormap);
return extra_colormap;
}
//
// R_AddColormaps()
// NOTE: The result colormap is not added to the extra_colormaps chain. You must do that yourself!
//
extracolormap_t *R_AddColormaps(extracolormap_t *exc_augend, extracolormap_t *exc_addend,
boolean subR, boolean subG, boolean subB, boolean subA,
boolean subFadeR, boolean subFadeG, boolean subFadeB, boolean subFadeA,
boolean subFadeStart, boolean subFadeEnd, boolean ignoreFlags,
boolean lighttable)
{
INT16 red, green, blue, alpha;
// exc_augend is added (or subtracted) onto by exc_addend
// In Rennaisance times, the first number was considered the augend, the second number the addend
// But since the commutative property was discovered, today they're both called addends!
// So let's be Olde English for a hot second.
exc_augend = R_CopyColormap(exc_augend, false);
if(!exc_addend)
exc_addend = R_GetDefaultColormap();
///////////////////
// base rgba
///////////////////
red = max(min(
R_GetRgbaR(exc_augend->rgba)
+ (subR ? -1 : 1) // subtract R
* R_GetRgbaR(exc_addend->rgba)
, 255), 0);
green = max(min(
R_GetRgbaG(exc_augend->rgba)
+ (subG ? -1 : 1) // subtract G
* R_GetRgbaG(exc_addend->rgba)
, 255), 0);
blue = max(min(
R_GetRgbaB(exc_augend->rgba)
+ (subB ? -1 : 1) // subtract B
* R_GetRgbaB(exc_addend->rgba)
, 255), 0);
alpha = R_GetRgbaA(exc_addend->rgba);
alpha = max(min(R_GetRgbaA(exc_augend->rgba) + (subA ? -1 : 1) * alpha, 25), 0);
exc_augend->rgba = R_PutRgbaRGBA(red, green, blue, alpha);
///////////////////
// fade/dark rgba
///////////////////
red = max(min(
R_GetRgbaR(exc_augend->fadergba)
+ (subFadeR ? -1 : 1) // subtract R
* R_GetRgbaR(exc_addend->fadergba)
, 255), 0);
green = max(min(
R_GetRgbaG(exc_augend->fadergba)
+ (subFadeG ? -1 : 1) // subtract G
* R_GetRgbaG(exc_addend->fadergba)
, 255), 0);
blue = max(min(
R_GetRgbaB(exc_augend->fadergba)
+ (subFadeB ? -1 : 1) // subtract B
* R_GetRgbaB(exc_addend->fadergba)
, 255), 0);
alpha = R_GetRgbaA(exc_addend->fadergba);
if (alpha == 25 && !R_GetRgbaRGB(exc_addend->fadergba))
alpha = 0; // HACK: fadergba A defaults at 25, so don't add anything in this case
alpha = max(min(R_GetRgbaA(exc_augend->fadergba) + (subFadeA ? -1 : 1) * alpha, 25), 0);
exc_augend->fadergba = R_PutRgbaRGBA(red, green, blue, alpha);
///////////////////
// parameters
///////////////////
exc_augend->fadestart = max(min(
exc_augend->fadestart
+ (subFadeStart ? -1 : 1) // subtract fadestart
* exc_addend->fadestart
, 31), 0);
exc_augend->fadeend = max(min(
exc_augend->fadeend
+ (subFadeEnd ? -1 : 1) // subtract fadeend
* (exc_addend->fadeend == 31 && !exc_addend->fadestart ? 0 : exc_addend->fadeend)
// HACK: fadeend defaults to 31, so don't add anything in this case
, 31), 0);
if (!ignoreFlags) // overwrite flags with new value
exc_augend->flags = exc_addend->flags;
///////////////////
// put it together
///////////////////
exc_augend->colormap = lighttable ? R_CreateLightTable(exc_augend) : NULL;
exc_augend->next = exc_augend->prev = NULL;
return exc_augend;
}
// Thanks to quake2 source!
// utils3/qdata/images.c
UINT8 NearestPaletteColor(UINT8 r, UINT8 g, UINT8 b, RGBA_t *palette)
{
int dr, dg, db;
int distortion, bestdistortion = 256 * 256 * 4, bestcolor = 0, i;
// Use master palette if none specified
if (palette == NULL)
palette = pMasterPalette;
for (i = 0; i < 256; i++)
{
dr = r - palette[i].s.red;
dg = g - palette[i].s.green;
db = b - palette[i].s.blue;
distortion = dr*dr + dg*dg + db*db;
if (distortion < bestdistortion)
{
if (!distortion)
return (UINT8)i;
bestdistortion = distortion;
bestcolor = i;
}
}
return (UINT8)bestcolor;
}
// Rounds off floating numbers and checks for 0 - 255 bounds
static int RoundUp(double number)
{
if (number > 255.0l)
return 255;
if (number < 0.0l)
return 0;
if ((int)number <= (int)(number - 0.5f))
return (int)number + 1;
return (int)number;
}
#ifdef EXTRACOLORMAPLUMPS
const char *R_NameForColormap(extracolormap_t *extra_colormap)
{
if (!extra_colormap)
return "NONE";
if (extra_colormap->lump == LUMPERROR)
return "INLEVEL";
return extra_colormap->lumpname;
}
#endif
//
// R_InitTextureData
//
// Locates all the lumps that will be used by all views
// Must be called after W_Init.
//
void R_InitTextureData(void)
{
CONS_Printf("R_LoadTextures()...\n");
R_LoadTextures();
CONS_Printf("P_InitPicAnims()...\n");
P_InitPicAnims();
CONS_Printf("K_InitBrightmaps()...\n");
K_InitBrightmaps();
}
//
// R_PrecacheLevel
//
// Preloads all relevant graphics for the level.
//
void R_PrecacheLevel(void)
{
char *texturepresent, *spritepresent;
size_t i, j, k;
lumpnum_t lump;
thinker_t *th;
spriteframe_t *sf;
if (demo.playback)
return;
// do not flush the memory, Z_Malloc twice with same user will cause error in Z_CheckHeap()
if (rendermode != render_soft)
return;
// Precache flats.
flatmemory = P_PrecacheLevelFlats();
//
// Precache textures.
//
// no need to precache all software textures in 3D mode
// (note they are still used with the reference software view)
texturepresent = calloc(numtextures, sizeof (*texturepresent));
if (texturepresent == NULL) I_Error("%s: Out of memory looking up textures", "R_PrecacheLevel");
for (j = 0; j < numsides; j++)
{
// huh, a potential bug here????
if (sides[j].toptexture >= 0 && sides[j].toptexture < numtextures)
texturepresent[sides[j].toptexture] = 1;
if (sides[j].midtexture >= 0 && sides[j].midtexture < numtextures)
texturepresent[sides[j].midtexture] = 1;
if (sides[j].bottomtexture >= 0 && sides[j].bottomtexture < numtextures)
texturepresent[sides[j].bottomtexture] = 1;
}
// 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 texture name set by the map.
texturepresent[skytexture] = 1;
texturememory = 0;
for (j = 0; j < (unsigned)numtextures; j++)
{
if (!texturepresent[j])
continue;
if (!texturecache[j])
R_GenerateTexture(j);
// pre-caching individual patches that compose textures became obsolete,
// since we cache entire composite textures
}
free(texturepresent);
//
// Precache sprites.
//
spritepresent = calloc(numsprites, sizeof (*spritepresent));
if (spritepresent == NULL) I_Error("%s: Out of memory looking up sprites", "R_PrecacheLevel");
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
if (th->function.acp1 != (actionf_p1)P_RemoveThinkerDelayed)
spritepresent[((mobj_t *)th)->sprite] = 1;
spritememory = 0;
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];\
if (devparm)\
spritememory += W_LumpLength(lump);\
W_CachePatchNum(lump, PU_SPRITE);\
}
// 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 = (sf->rotate & SRF_3DGE ? 16 : 8);
while (k--)
cacheang(k);
break;
}
#undef cacheang
}
}
free(spritepresent);
// FIXME: this is no longer correct with OpenGL render mode
CONS_Debug(DBG_SETUP, "Precache level done:\n"
"flatmemory: %s k\n"
"texturememory: %s k\n"
"spritememory: %s k\n", sizeu1(flatmemory>>10), sizeu2(texturememory>>10), sizeu3(spritememory>>10));
}