// 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 #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(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(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]; 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 } } } static void BlendTab_Subtractive(UINT8 *table, int style, UINT8 blendamt) { INT16 bg, fg; RGBA_t backrgba, frontrgba, result; 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++) { backrgba = pLocalPalette[bg]; // intentionally uses pLocalPalette for (fg = 0; fg < 0x100; fg++) { frontrgba = pLocalPalette[fg]; 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; RGBA_t backrgba, frontrgba, result; if (table == NULL) I_Error("BlendTab_Modulative: input table was NULL!"); for (bg = 0; bg < 0x100; bg++) { backrgba = pGammaCorrectedPalette[bg]; for (fg = 0; fg < 0x100; fg++) { frontrgba = pGammaCorrectedPalette[fg]; 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(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(malloc(sizeof *state)); size_t palsize = 256 * sizeof(RGBA_t); state->LocalPalette = static_cast(memcpy(malloc(palsize), pLocalPalette, palsize)); state->gammaCorrectedPalette = static_cast(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); 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(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(Z_Malloc(NUM_PALETTE_ENTRIES, 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) { return RGetTranslationColormap(K_GetMobjLocalSkinNum(skin, localskin, local), color, flags, (localskin && local)); } patch_t* R_GetSkinFaceRank(player_t* ply) { if (ply->localskin) { if (ply->skinlocal) return localfacerankprefix[ply->localskin - 1]; return facerankprefix[ply->localskin - 1]; } return facerankprefix[ply->skin]; } patch_t* R_GetSkinFaceWant(player_t* ply) { if (ply->localskin) { if (ply->skinlocal) return localfacewantprefix[ply->localskin - 1]; return facewantprefix[ply->localskin - 1]; } return facewantprefix[ply->skin]; } patch_t* R_GetSkinFaceMini(player_t* ply) { if (ply->localskin) { if (ply->skinlocal) return localfacemmapprefix[ply->localskin - 1]; 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 */ /* There seems to be no reason to free cached colormaps tho? Not like they can change :ChonkBunckle: */ /* 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 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(Z_Realloc(negonearray, sizeof(*negonearray) * viewwidth, PU_STATIC, NULL)); screenheightarray = static_cast(Z_Realloc(screenheightarray, sizeof(*screenheightarray) * viewwidth, PU_STATIC, NULL)); floorclip = static_cast(Z_Realloc(floorclip, sizeof(*floorclip) * viewwidth, PU_STATIC, NULL)); ceilingclip = static_cast(Z_Realloc(ceilingclip, sizeof(*ceilingclip) * viewwidth, PU_STATIC, NULL)); frontscale = static_cast(Z_Realloc(frontscale, sizeof(*frontscale) * viewwidth, PU_STATIC, NULL)); xtoviewangle = static_cast(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(aligned_alloc(16, bufsize)); #else temp_dc.buf = static_cast(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"