Files
SRB2Kart-Saturn/src/r_draw.cpp
T
Alug c977c4fc45 initialize most of our global variables
just to be safe, not really trusting the game at this point, maybe this helps with some obscure bugs or smth lel
2025-11-11 14:19:02 +01:00

678 lines
20 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-2018 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 r_draw.c
/// \brief span / column drawer functions, for 8bpp and 16bpp
/// All drawing to the view buffer is accomplished in this file.
/// The other refresh files only know about ccordinates,
/// not the architecture of the frame buffer.
/// The frame buffer is a linear one, and we need only the base address.
#include <algorithm>
#include "doomdef.h"
#include "doomstat.h"
#include "r_local.h"
#include "r_things.h"
#include "st_stuff.h" // need ST_HEIGHT
#include "i_video.h"
#include "v_video.h"
#include "m_misc.h"
#include "w_wad.h"
#include "z_zone.h"
#include "console.h" // Until buffering gets finished
#include "k_kart.h" // SRB2kart
#include "i_threads.h"
#ifdef HWRENDER
#include "hardware/hw_main.h"
#endif
// ==========================================================================
// COMMON DATA FOR 8bpp AND 16bpp
// ==========================================================================
/** \brief view info
*/
INT32 viewwidth = 0, viewheight = 0, viewwindowx = 0, viewwindowy = 0;
// =========================================================================
// COLUMN DRAWING CODE STUFF
// =========================================================================
drawcolumndata_t g_dc = {};
// -----------------------
// translucency stuff here
// -----------------------
#define NUMTRANSTABLES 9 // how many translucency tables are used
UINT8 *transtables = NULL; // translucency tables
UINT8 *blendtables[NUMBLENDMAPS] = {};
// --------------------------------------------
// c drawer routines
// --------------------------------------------
coldrawfunc_t *colfunc = NULL;
coldrawfunc_t *colfuncs[COLDRAWFUNC_MAX] = {};
int colfunctype = 0;
// =========================================================================
// SPAN DRAWING CODE STUFF
// =========================================================================
drawspandata_t g_ds = {};
// Vectors for Software's tilted slope drawers
floatv3_t *ds_su = NULL, *ds_sv = NULL, *ds_sz = NULL;
float focallengthf = 0.0f;
// For, uh, tilted lighting, duh.
//static INT32 *tiltlighting;
// --------------------------------------------
// c drawer routines
// --------------------------------------------
spandrawfunc_t *spanfunc = NULL;
spandrawfunc_t *spanfuncs[SPANDRAWFUNC_MAX] = {};
// ==========================================================================
// OLD DOOM FUZZY EFFECT
// ==========================================================================
// =========================================================================
// TRANSLATION COLORMAP CODE
// =========================================================================
#define DEFAULT_TT_CACHE_INDEX MAXSKINS
#define BOSS_TT_CACHE_INDEX (MAXSKINS + 1)
#define METALSONIC_TT_CACHE_INDEX (MAXSKINS + 2)
#define ALLWHITE_TT_CACHE_INDEX (MAXSKINS + 3)
#define RAINBOW_TT_CACHE_INDEX (MAXSKINS + 4)
#define BLINK_TT_CACHE_INDEX (MAXSKINS + 5)
#define TT_CACHE_SIZE (MAXSKINS + 6)
#define SKIN_RAMP_LENGTH 16
#define DEFAULT_STARTTRANSCOLOR 160
#define NUM_PALETTE_ENTRIES 256
static UINT8 **translationtablecache[TT_CACHE_SIZE] = {};
static UINT8 **localtranslationtablecache[MAXLOCALSKINS] = {};
CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1] = {};
struct GenerateBlendTables_State
{
RGBA_t *LocalPalette;
RGBA_t *gammaCorrectedPalette;
};
static void R_GenerateBlendTables_Core(struct GenerateBlendTables_State *state);
static void R_AllocateBlendTables(void);
#ifdef HAVE_THREADS
static void R_GenerateBlendTables_Thread(void *userdata)
{
struct GenerateBlendTables_State *state = static_cast<struct GenerateBlendTables_State *>(userdata);
R_GenerateBlendTables_Core(state);
free(state->LocalPalette);
free(state->gammaCorrectedPalette);
free(state);
}
#endif
/** \brief Initializes the translucency tables used by the Software renderer.
*/
void R_InitTranslucencyTables(void)
{
// Load here the transparency lookup tables 'TRANSx0'
// NOTE: the TRANSx0 resources MUST BE aligned on 64k for the asm
// optimised code (in other words, transtables pointer low word is 0)
transtables = static_cast<UINT8*>(Z_Malloc(NUMTRANSTABLES*0x10000, PU_STATIC, NULL));
W_ReadLump(W_GetNumForName("TRANS10"), transtables);
W_ReadLump(W_GetNumForName("TRANS20"), transtables+0x10000);
W_ReadLump(W_GetNumForName("TRANS30"), transtables+0x20000);
W_ReadLump(W_GetNumForName("TRANS40"), transtables+0x30000);
W_ReadLump(W_GetNumForName("TRANS50"), transtables+0x40000);
W_ReadLump(W_GetNumForName("TRANS60"), transtables+0x50000);
W_ReadLump(W_GetNumForName("TRANS70"), transtables+0x60000);
W_ReadLump(W_GetNumForName("TRANS80"), transtables+0x70000);
W_ReadLump(W_GetNumForName("TRANS90"), transtables+0x80000);
R_AllocateBlendTables();
R_GenerateBlendTables();
}
static colorlookup_t transtab_lut;
static colorlookup_t transtab_lut_corrected;
static void BlendTab_Translucent(UINT8 *table, int style, UINT8 blendamt)
{
INT16 bg, fg;
RGBA_t backrgba, frontrgba, result;
if (table == NULL)
I_Error("BlendTab_Translucent: input table was NULL!");
for (bg = 0; bg < 0x100; bg++)
{
backrgba = pGammaCorrectedPalette[bg];
for (fg = 0; fg < 0x100; fg++)
{
frontrgba = pGammaCorrectedPalette[fg];
#if 0 // perfect implementation
result.rgba = ASTBlendPixel(backrgba, frontrgba, style, 0xFF);
result.rgba = ASTBlendPixel(result, frontrgba, AST_TRANSLUCENT, blendamt);
table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue);
#else // performance scrabbler
result.rgba = ASTBlendPixel(backrgba, frontrgba, style, 0xFF);
result.rgba = ASTBlendPixel(result, frontrgba, AST_TRANSLUCENT, blendamt);
table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut_corrected, result.s.red, result.s.green, result.s.blue); // pGammaCorrectedPalette
#endif
}
}
}
static void BlendTab_Subtractive(UINT8 *table, int style, UINT8 blendamt)
{
INT16 bg, fg;
if (table == NULL)
I_Error("BlendTab_Subtractive: input table was NULL!");
if (blendamt == 0xFF)
{
memset(table, GetColorLUT(&transtab_lut, 0, 0, 0), 0x10000);
return;
}
for (bg = 0; bg < 0x100; bg++)
{
for (fg = 0; fg < 0x100; fg++)
{
RGBA_t backrgba = V_GetColor(bg);
RGBA_t frontrgba = V_GetColor(fg);
RGBA_t result;
result.rgba = ASTBlendPixel(backrgba, frontrgba, style, 0xFF);
result.s.red = std::max(0, result.s.red - blendamt);
result.s.green = std::max(0, result.s.green - blendamt);
result.s.blue = std::max(0, result.s.blue - blendamt);
table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue);
}
}
}
static void BlendTab_Modulative(UINT8 *table)
{
INT16 bg, fg;
if (table == NULL)
I_Error("BlendTab_Modulative: input table was NULL!");
for (bg = 0; bg < 0x100; bg++)
{
for (fg = 0; fg < 0x100; fg++)
{
RGBA_t backrgba = V_GetColor(bg);
RGBA_t frontrgba = V_GetColor(fg);
RGBA_t result;
result.rgba = ASTBlendPixel(backrgba, frontrgba, AST_MODULATE, 0);
table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut_corrected, result.s.red, result.s.green, result.s.blue);
}
}
}
static INT32 BlendTab_Count[NUMBLENDMAPS] =
{
NUMTRANSTABLES+1, // blendtab_add
NUMTRANSTABLES+1, // blendtab_subtract
NUMTRANSTABLES+1, // blendtab_reversesubtract
1 // blendtab_modulate
};
static INT32 BlendTab_FromStyle[] =
{
0, // AST_COPY
0, // AST_TRANSLUCENT
blendtab_add, // AST_ADD
blendtab_subtract, // AST_SUBTRACT
blendtab_reversesubtract, // AST_REVERSESUBTRACT
blendtab_modulate, // AST_MODULATE
0 // AST_OVERLAY
};
static void BlendTab_GenerateMaps(INT32 tab, INT32 style, void (*genfunc)(UINT8 *, int, UINT8))
{
INT32 i = 0, num = BlendTab_Count[tab];
const float amtmul = (256.0f / (float)(NUMTRANSTABLES + 1));
for (; i < num; i++)
{
const size_t offs = (0x10000 * i);
const UINT16 alpha = std::fmin(amtmul * i, 0xFF);
genfunc(blendtables[tab] + offs, style, alpha);
}
}
static void R_AllocateBlendTables(void)
{
INT32 i;
for (i = 0; i < NUMBLENDMAPS; i++)
{
blendtables[i] = static_cast<UINT8*>(Z_Malloc(BlendTab_Count[i] * 0x10000, PU_STATIC, NULL));
}
}
void R_GenerateBlendTables(void)
{
#ifdef HAVE_THREADS
// Allocate copies for the worker thread since the originals can be freed in the main thread.
struct GenerateBlendTables_State *state = static_cast<struct GenerateBlendTables_State *>(malloc(sizeof *state));
size_t palsize = 256 * sizeof(RGBA_t);
state->LocalPalette = static_cast<RGBA_t *>(memcpy(malloc(palsize), pLocalPalette, palsize));
state->gammaCorrectedPalette = static_cast<RGBA_t *>(memcpy(malloc(palsize), pGammaCorrectedPalette, palsize));
I_spawn_thread("blend-tables",
R_GenerateBlendTables_Thread, state);
#else
struct GenerateBlendTables_State state = {pLocalPalette, pGammaCorrectedPalette};
R_GenerateBlendTables_Core(&state);
#endif
}
static void R_GenerateBlendTables_Core(struct GenerateBlendTables_State *state)
{
InitColorLUT(&transtab_lut, state->LocalPalette, false);
InitColorLUT(&transtab_lut_corrected, state->gammaCorrectedPalette, false);
// Additive
BlendTab_GenerateMaps(blendtab_add, AST_ADD, BlendTab_Translucent);
// Subtractive
#if 1
BlendTab_GenerateMaps(blendtab_subtract, AST_SUBTRACT, BlendTab_Subtractive);
#else
BlendTab_GenerateMaps(blendtab_subtract, AST_SUBTRACT, BlendTab_Translucent);
#endif
// Reverse subtractive
BlendTab_GenerateMaps(blendtab_reversesubtract, AST_REVERSESUBTRACT, BlendTab_Translucent);
// Modulative blending only requires a single table
BlendTab_Modulative(blendtables[blendtab_modulate]);
}
#define ClipBlendLevel(style, trans) std::max(std::min((trans), BlendTab_Count[BlendTab_FromStyle[style]]-1), 0)
#define ClipTransLevel(trans) std::max(std::min((trans), NUMTRANSMAPS-2), 0)
UINT8 *R_GetTranslucencyTable(INT32 alphalevel)
{
return transtables + (ClipTransLevel(alphalevel-1) << FF_TRANSSHIFT);
}
UINT8 *R_GetBlendTable(int style, INT32 alphalevel)
{
size_t offs;
if (style <= AST_COPY || style >= AST_OVERLAY)
return transtables + (ClipTransLevel(alphalevel) << FF_TRANSSHIFT);
offs = (ClipBlendLevel(style, alphalevel) << FF_TRANSSHIFT);
// Lactozilla: Returns the equivalent to AST_TRANSLUCENT
// if no alpha style matches any of the blend tables.
switch (style)
{
case AST_ADD:
return blendtables[blendtab_add] + offs;
case AST_SUBTRACT:
return blendtables[blendtab_subtract] + offs;
case AST_REVERSESUBTRACT:
return blendtables[blendtab_reversesubtract] + offs;
case AST_MODULATE:
return blendtables[blendtab_modulate];
default:
break;
}
// Return a normal translucency table
if (--alphalevel >= 0)
return transtables + (ClipTransLevel(alphalevel) << FF_TRANSSHIFT);
else
return NULL;
}
boolean R_BlendLevelVisible(INT32 blendmode, INT32 alphalevel)
{
if (blendmode <= AST_COPY || blendmode == AST_SUBTRACT || blendmode == AST_MODULATE || blendmode >= AST_OVERLAY)
return true;
return (alphalevel < BlendTab_Count[BlendTab_FromStyle[blendmode]]);
}
/** \brief Retrieves a translation colormap from the cache.
\param skinnum skin number, or a translation mode
\param color translation color
\param flags set GTC_CACHE to use the cache
\return Colormap. If not cached, caller should Z_Free.
*/
static UINT8* RGetTranslationColormap(INT32 skinnum, skincolors_t color, UINT8 flags, boolean local)
{
UINT8 ***tt;
UINT8* ret;
INT32 skintableindex;
if (local)
{
tt = localtranslationtablecache;
skintableindex = skinnum;
}
else
{
tt = translationtablecache;
// Adjust if we want the default colormap
switch (skinnum)
{
case TC_DEFAULT: skintableindex = DEFAULT_TT_CACHE_INDEX; break;
case TC_BOSS: skintableindex = BOSS_TT_CACHE_INDEX; break;
case TC_METALSONIC: skintableindex = METALSONIC_TT_CACHE_INDEX; break;
case TC_ALLWHITE: skintableindex = ALLWHITE_TT_CACHE_INDEX; break;
case TC_RAINBOW: skintableindex = RAINBOW_TT_CACHE_INDEX; break;
case TC_BLINK: skintableindex = BLINK_TT_CACHE_INDEX; break;
default: skintableindex = skinnum; break;
}
}
if (flags & GTC_CACHE)
{
// Allocate table for skin if necessary
if (!tt[skintableindex])
tt[skintableindex] = static_cast<UINT8**>(Z_Calloc(MAXTRANSLATIONS * sizeof(UINT8**), PU_STATIC, NULL));
// Get colormap
ret = tt[skintableindex][color];
}
else ret = NULL;
// Generate the colormap if necessary
if (!ret)
{
ret = static_cast<UINT8*>(Z_Malloc(NUM_PALETTE_ENTRIES, (flags & GTC_CACHE) ? PU_LEVEL : PU_STATIC, NULL));
K_GenerateKartColormap(ret, skinnum, color, local); //R_GenerateTranslationColormap(ret, skinnum, color); // SRB2kart
// Cache the colormap if desired
if (flags & GTC_CACHE)
tt[skintableindex][color] = ret;
}
return ret;
}
UINT8* R_GetTranslationColormap(INT32 skinnum, skincolors_t color, UINT8 flags)
{
return RGetTranslationColormap(skinnum, color, flags, false);
}
UINT8* R_GetLocalTranslationColormap(skin_t *skin, skin_t *localskin, skincolors_t color, UINT8 flags, boolean local)
{
if (localskin)
return RGetTranslationColormap(localskin - K_GetSkinArray(local), color, flags, local);
else
return RGetTranslationColormap((skin - skins), color, flags, false);
}
patch_t* R_GetSkinFaceRank(player_t* ply)
{
if (ply->skinlocal && ply->localskin)
return localfacerankprefix[ply->localskin - 1];
else if (ply->localskin)
return facerankprefix[ply->localskin - 1];
return facerankprefix[ply->skin];
}
patch_t* R_GetSkinFaceWant(player_t* ply)
{
if (ply->skinlocal && ply->localskin)
return localfacewantprefix[ply->localskin - 1];
else if (ply->localskin)
return facewantprefix[ply->localskin - 1];
return facewantprefix[ply->skin];
}
patch_t* R_GetSkinFaceMini(player_t* ply)
{
if (ply->skinlocal && ply->localskin)
return localfacemmapprefix[ply->localskin - 1];
else if (ply->localskin)
return facemmapprefix[ply->localskin - 1];
return facemmapprefix[ply->skin];
}
/** \brief Flushes cache of translation colormaps.
Flushes cache of translation colormaps, but doesn't actually free the
colormaps themselves. These are freed when PU_LEVEL blocks are purged,
at or before which point, this function should be called.
\return void
*/
void R_FlushTranslationColormapCache(void)
{
INT32 i;
for (i = 0; i < (INT32)(sizeof(translationtablecache) / sizeof(translationtablecache[0])); i++)
if (translationtablecache[i])
memset(translationtablecache[i], 0, MAXTRANSLATIONS * sizeof(UINT8**));
for (i = 0; i < (INT32)(sizeof(localtranslationtablecache) / sizeof(localtranslationtablecache[0])); i++)
if (localtranslationtablecache[i])
memset(localtranslationtablecache[i], 0, MAXTRANSLATIONS * sizeof(UINT8**));
}
// ==========================================================================
// COMMON DRAWER FOR 8 AND 16 BIT COLOR MODES
// ==========================================================================
// in a perfect world, all routines would be compatible for either mode,
// and optimised enough
//
// in reality, the few routines that can work for either mode, are
// put here
enum columncontext_e columncontext = COLUMNCONTEXT_DIRECT;
enum ColumnFlushType
{
FLUSH_NONE = 0x0000,
FLUSH_OPAQUE = 0x0001,
FLUSH_TRANS = 0x0002,
FLUSH_COLORMAP = 0x0004,
FLUSH_COLORMAP_TRANS = 0x0008,
};
typedef struct drawcolumndata_temp_s
{
INT32 x;
INT32 yl[8], yh[8];
// e6y: resolution limitation is removed
UINT8 *buf;
INT32 startx;
ColumnFlushType type;
INT32 commontop, commonbot;
UINT8 *transmap;
// SoM 7-28-04: Fix the fuzz problem.
UINT8 *translation;
} drawcolumndata_temp_t;
drawcolumndata_temp_t temp_dc = {};
//
// Error functions that will abort if R_FlushColumns tries to flush
// columns without a column type.
//
FUNCNORETURN static ATTRNORETURN void R_FlushWholeError(void)
{
I_Error("R_FlushWholeColumns called without being initialized.\n");
}
FUNCNORETURN static ATTRNORETURN void R_FlushHTError(void)
{
I_Error("R_FlushHTColumns called without being initialized.\n");
}
FUNCNORETURN static ATTRNORETURN void R_QuadFlushError(void)
{
I_Error("R_FlushQuadColumn called without being initialized.\n");
}
static void (*R_FlushWholeColumns)(void) = R_FlushWholeError;
static void (*R_FlushHTColumns)(void) = R_FlushHTError;
static void (*R_FlushQuadColumn)(void) = R_QuadFlushError;
static void R_FlushColumns(void)
{
if (temp_dc.x != 8 || temp_dc.commontop >= temp_dc.commonbot)
R_FlushWholeColumns();
else
{
R_FlushHTColumns();
R_FlushQuadColumn();
}
temp_dc.x = 0;
}
//
// R_ResetColumnBuffer
//
// haleyjd 09/13/04: new function to call from main rendering loop
// which gets rid of the unnecessary reset of various variables during
// column drawing.
//
void R_ResetColumnBuffer(void)
{
// haleyjd 10/06/05: this must not be done if x == 0!
if (temp_dc.x)
{
R_FlushColumns();
}
temp_dc.type = FLUSH_NONE;
R_FlushWholeColumns = R_FlushWholeError;
R_FlushHTColumns = R_FlushHTError;
R_FlushQuadColumn = R_QuadFlushError;
}
/** \brief The R_InitViewBuffer function
Creates lookup tables for getting the framebuffer address
of a pixel to draw.
\param width witdh of buffer
\param height hieght of buffer
\return void
*/
static void R_AllocViewMemory(void)
{
negonearray = static_cast<INT16*>(Z_Realloc(negonearray, sizeof(*negonearray) * viewwidth, PU_STATIC, NULL));
screenheightarray = static_cast<INT16*>(Z_Realloc(screenheightarray, sizeof(*screenheightarray) * viewwidth, PU_STATIC, NULL));
floorclip = static_cast<INT16*>(Z_Realloc(floorclip, sizeof(*floorclip) * viewwidth, PU_STATIC, NULL));
ceilingclip = static_cast<INT16*>(Z_Realloc(ceilingclip, sizeof(*ceilingclip) * viewwidth, PU_STATIC, NULL));
frontscale = static_cast<fixed_t*>(Z_Realloc(frontscale, sizeof(*frontscale) * viewwidth, PU_STATIC, NULL));
xtoviewangle = static_cast<angle_t*>(Z_Realloc(xtoviewangle, sizeof(*xtoviewangle) * (viewwidth + 1), PU_STATIC, NULL));
R_AllocSegMemory();
R_AllocClipSegMemory();
R_AllocPlaneMemory();
#ifdef FLOORSPLATS
R_AllocFloorSpriteTables();
#endif
R_AllocVisSpriteMemory();
}
void R_InitViewBuffer(void)
{
R_AllocViewMemory();
viewwindowx = 0;
viewwindowy = 0;
INT32 bufsize = (vid.width * 8) * sizeof(*temp_dc.buf);
if (temp_dc.buf)
{
#if defined(__SSE__)
aligned_free(temp_dc.buf);
#else
Z_Free(temp_dc.buf);
#endif
}
memset(&temp_dc, 0, sizeof(temp_dc));
#if defined(__SSE__)
while (bufsize & 15)
bufsize++;
temp_dc.buf = static_cast<UINT8*>(aligned_alloc(16, bufsize));
#else
temp_dc.buf = static_cast<UINT8*>(Z_Malloc(bufsize, PU_STATIC, NULL));
#endif
memset(temp_dc.buf, 0, bufsize);
}
/** \brief The R_VideoErase function
Copy a screen buffer.
\param ofs offest from buffer
\param count bytes to erase
\return void
*/
void R_VideoErase(size_t ofs, INT32 count)
{
// LFB copy.
// This might not be a good idea if memcpy
// is not optimal, e.g. byte by byte on
// a 32bit CPU, as GNU GCC/Linux libc did
// at one point.
memcpy(vid.screens[0] + ofs, vid.screens[1] + ofs, count);
}
// ==========================================================================
// INCLUDE DRAWING CODE HERE
// ==========================================================================
#include "r_draw_column.cpp"
#include "r_draw_span.cpp"