/* ** r_draw.cpp ** **--------------------------------------------------------------------------- ** Copyright 1998-2016 Randy Heit ** Copyright 2016 Magnus Norddahl ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** 1. Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** 2. Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in the ** documentation and/or other materials provided with the distribution. ** 3. The name of the author may not be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **--------------------------------------------------------------------------- ** */ #include #include "templates.h" #include "doomdef.h" #include "i_system.h" #include "w_wad.h" #include "r_local.h" #include "v_video.h" #include "doomstat.h" #include "st_stuff.h" #include "g_game.h" #include "g_level.h" #include "r_data/r_translate.h" #include "v_palette.h" #include "r_data/colormaps.h" #include "r_plane.h" #include "r_draw.h" #include "r_draw_pal.h" #include "r_thread.h" namespace swrenderer { // Needed by R_DrawFogBoundary (which probably shouldn't be part of this file) extern "C" short spanend[MAXHEIGHT]; extern float rw_light; extern float rw_lightstep; extern int wallshade; double dc_texturemid; int ylookup[MAXHEIGHT]; uint8_t shadetables[NUMCOLORMAPS * 16 * 256]; FDynamicColormap ShadeFakeColormap[16]; uint8_t identitymap[256]; FDynamicColormap identitycolormap; int fuzzoffset[FUZZTABLE + 1]; int fuzzpos; int fuzzviewheight; namespace drawerargs { int dc_pitch; lighttable_t *dc_colormap; int dc_x; int dc_yl; int dc_yh; fixed_t dc_iscale; fixed_t dc_texturefrac; uint32_t dc_textureheight; int dc_color; uint32_t dc_srccolor; uint32_t dc_srccolor_bgra; uint32_t *dc_srcblend; uint32_t *dc_destblend; fixed_t dc_srcalpha; fixed_t dc_destalpha; const uint8_t *dc_source; const uint8_t *dc_source2; uint32_t dc_texturefracx; uint8_t *dc_translation; uint8_t *dc_dest; uint8_t *dc_destorg; int dc_destheight; int dc_count; uint32_t dc_wall_texturefrac[4]; uint32_t dc_wall_iscale[4]; uint8_t *dc_wall_colormap[4]; fixed_t dc_wall_light[4]; const uint8_t *dc_wall_source[4]; const uint8_t *dc_wall_source2[4]; uint32_t dc_wall_texturefracx[4]; uint32_t dc_wall_sourceheight[4]; int dc_wall_fracbits; int ds_y; int ds_x1; int ds_x2; lighttable_t * ds_colormap; dsfixed_t ds_light; dsfixed_t ds_xfrac; dsfixed_t ds_yfrac; dsfixed_t ds_xstep; dsfixed_t ds_ystep; int ds_xbits; int ds_ybits; fixed_t ds_alpha; double ds_lod; const uint8_t *ds_source; int ds_color; unsigned int dc_tspans[4][MAXHEIGHT]; unsigned int *dc_ctspan[4]; unsigned int *horizspan[4]; } void R_InitColumnDrawers() { colfunc = basecolfunc = R_DrawColumn; fuzzcolfunc = R_DrawFuzzColumn; transcolfunc = R_DrawTranslatedColumn; spanfunc = R_DrawSpan; hcolfunc_pre = R_DrawColumnHoriz; hcolfunc_post1 = rt_map1col; hcolfunc_post4 = rt_map4cols; } void R_InitShadeMaps() { int i, j; // set up shading tables for shaded columns // 16 colormap sets, progressing from full alpha to minimum visible alpha uint8_t *table = shadetables; // Full alpha for (i = 0; i < 16; ++i) { ShadeFakeColormap[i].Color = ~0u; ShadeFakeColormap[i].Desaturate = ~0u; ShadeFakeColormap[i].Next = NULL; ShadeFakeColormap[i].Maps = table; for (j = 0; j < NUMCOLORMAPS; ++j) { int a = (NUMCOLORMAPS - j) * 256 / NUMCOLORMAPS * (16 - i); for (int k = 0; k < 256; ++k) { uint8_t v = (((k + 2) * a) + 256) >> 14; table[k] = MIN(v, 64); } table += 256; } } for (i = 0; i < NUMCOLORMAPS * 16 * 256; ++i) { assert(shadetables[i] <= 64); } // Set up a guaranteed identity map for (i = 0; i < 256; ++i) { identitymap[i] = i; } identitycolormap.Maps = identitymap; } void R_InitFuzzTable(int fuzzoff) { /* FUZZOFF,-FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF, FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF, FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF, FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF, FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF,FUZZOFF,-FUZZOFF,FUZZOFF */ static const int8_t fuzzinit[FUZZTABLE] = { 1,-1, 1,-1, 1, 1,-1, 1, 1,-1, 1, 1, 1,-1, 1, 1, 1,-1,-1,-1,-1, 1,-1,-1, 1, 1, 1, 1,-1, 1,-1, 1, 1,-1,-1, 1, 1,-1,-1,-1,-1, 1, 1, 1, 1,-1, 1, 1,-1, 1 }; for (int i = 0; i < FUZZTABLE; i++) { fuzzoffset[i] = fuzzinit[i] * fuzzoff; } } namespace { bool R_SetBlendFunc(int op, fixed_t fglevel, fixed_t bglevel, int flags) { using namespace drawerargs; // r_drawtrans is a seriously bad thing to turn off. I wonder if I should // just remove it completely. if (!r_drawtrans || (op == STYLEOP_Add && fglevel == FRACUNIT && bglevel == 0 && !(flags & STYLEF_InvertSource))) { if (flags & STYLEF_ColorIsFixed) { colfunc = R_FillColumn; hcolfunc_post1 = rt_copy1col; hcolfunc_post4 = rt_copy4cols; } else if (dc_translation == NULL) { colfunc = basecolfunc; hcolfunc_post1 = rt_map1col; hcolfunc_post4 = rt_map4cols; } else { colfunc = transcolfunc; hcolfunc_post1 = rt_tlate1col; hcolfunc_post4 = rt_tlate4cols; } return true; } if (flags & STYLEF_InvertSource) { dc_srcblend = Col2RGB8_Inverse[fglevel >> 10]; dc_destblend = Col2RGB8_LessPrecision[bglevel >> 10]; dc_srcalpha = fglevel; dc_destalpha = bglevel; } else if (op == STYLEOP_Add && fglevel + bglevel <= FRACUNIT) { dc_srcblend = Col2RGB8[fglevel >> 10]; dc_destblend = Col2RGB8[bglevel >> 10]; dc_srcalpha = fglevel; dc_destalpha = bglevel; } else { dc_srcblend = Col2RGB8_LessPrecision[fglevel >> 10]; dc_destblend = Col2RGB8_LessPrecision[bglevel >> 10]; dc_srcalpha = fglevel; dc_destalpha = bglevel; } switch (op) { case STYLEOP_Add: if (fglevel == 0 && bglevel == FRACUNIT) { return false; } if (fglevel + bglevel <= FRACUNIT) { // Colors won't overflow when added if (flags & STYLEF_ColorIsFixed) { colfunc = R_FillAddColumn; hcolfunc_post1 = rt_add1col; hcolfunc_post4 = rt_add4cols; } else if (dc_translation == NULL) { colfunc = R_DrawAddColumn; hcolfunc_post1 = rt_add1col; hcolfunc_post4 = rt_add4cols; } else { colfunc = R_DrawTlatedAddColumn; hcolfunc_post1 = rt_tlateadd1col; hcolfunc_post4 = rt_tlateadd4cols; } } else { // Colors might overflow when added if (flags & STYLEF_ColorIsFixed) { colfunc = R_FillAddClampColumn; hcolfunc_post1 = rt_addclamp1col; hcolfunc_post4 = rt_addclamp4cols; } else if (dc_translation == NULL) { colfunc = R_DrawAddClampColumn; hcolfunc_post1 = rt_addclamp1col; hcolfunc_post4 = rt_addclamp4cols; } else { colfunc = R_DrawAddClampTranslatedColumn; hcolfunc_post1 = rt_tlateaddclamp1col; hcolfunc_post4 = rt_tlateaddclamp4cols; } } return true; case STYLEOP_Sub: if (flags & STYLEF_ColorIsFixed) { colfunc = R_FillSubClampColumn; hcolfunc_post1 = rt_subclamp1col; hcolfunc_post4 = rt_subclamp4cols; } else if (dc_translation == NULL) { colfunc = R_DrawSubClampColumn; hcolfunc_post1 = rt_subclamp1col; hcolfunc_post4 = rt_subclamp4cols; } else { colfunc = R_DrawSubClampTranslatedColumn; hcolfunc_post1 = rt_tlatesubclamp1col; hcolfunc_post4 = rt_tlatesubclamp4cols; } return true; case STYLEOP_RevSub: if (fglevel == 0 && bglevel == FRACUNIT) { return false; } if (flags & STYLEF_ColorIsFixed) { colfunc = R_FillRevSubClampColumn; hcolfunc_post1 = rt_subclamp1col; hcolfunc_post4 = rt_subclamp4cols; } else if (dc_translation == NULL) { colfunc = R_DrawRevSubClampColumn; hcolfunc_post1 = rt_revsubclamp1col; hcolfunc_post4 = rt_revsubclamp4cols; } else { colfunc = R_DrawRevSubClampTranslatedColumn; hcolfunc_post1 = rt_tlaterevsubclamp1col; hcolfunc_post4 = rt_tlaterevsubclamp4cols; } return true; default: return false; } } fixed_t GetAlpha(int type, fixed_t alpha) { switch (type) { case STYLEALPHA_Zero: return 0; case STYLEALPHA_One: return OPAQUE; case STYLEALPHA_Src: return alpha; case STYLEALPHA_InvSrc: return OPAQUE - alpha; default: return 0; } } FDynamicColormap *basecolormapsave; } ESPSResult R_SetPatchStyle(FRenderStyle style, fixed_t alpha, int translation, uint32_t color) { using namespace drawerargs; fixed_t fglevel, bglevel; style.CheckFuzz(); if (style.BlendOp == STYLEOP_Shadow) { style = LegacyRenderStyles[STYLE_TranslucentStencil]; alpha = TRANSLUC33; color = 0; } if (style.Flags & STYLEF_TransSoulsAlpha) { alpha = fixed_t(transsouls * OPAQUE); } else if (style.Flags & STYLEF_Alpha1) { alpha = FRACUNIT; } else { alpha = clamp(alpha, 0, OPAQUE); } if (translation != -1) { dc_translation = NULL; if (translation != 0) { FRemapTable *table = TranslationToTable(translation); if (table != NULL && !table->Inactive) { dc_translation = table->Remap; } } } basecolormapsave = basecolormap; hcolfunc_pre = R_DrawColumnHoriz; // Check for special modes if (style.BlendOp == STYLEOP_Fuzz) { colfunc = fuzzcolfunc; return DoDraw0; } else if (style == LegacyRenderStyles[STYLE_Shaded]) { // Shaded drawer only gets 16 levels of alpha because it saves memory. if ((alpha >>= 12) == 0) return DontDraw; colfunc = R_DrawShadedColumn; hcolfunc_post1 = rt_shaded1col; hcolfunc_post4 = rt_shaded4cols; dc_color = fixedcolormap ? fixedcolormap[APART(color)] : basecolormap->Maps[APART(color)]; dc_colormap = (basecolormap = &ShadeFakeColormap[16 - alpha])->Maps; if (fixedlightlev >= 0 && fixedcolormap == NULL) { dc_colormap += fixedlightlev; } return DoDraw0; } fglevel = GetAlpha(style.SrcAlpha, alpha); bglevel = GetAlpha(style.DestAlpha, alpha); if (style.Flags & STYLEF_ColorIsFixed) { uint32_t x = fglevel >> 10; uint32_t r = RPART(color); uint32_t g = GPART(color); uint32_t b = BPART(color); // dc_color is used by the rt_* routines. It is indexed into dc_srcblend. dc_color = RGB32k.RGB[r >> 3][g >> 3][b >> 3]; if (style.Flags & STYLEF_InvertSource) { r = 255 - r; g = 255 - g; b = 255 - b; } uint32_t alpha = clamp(fglevel >> (FRACBITS - 8), 0, 255); dc_srccolor_bgra = (alpha << 24) | (r << 16) | (g << 8) | b; // dc_srccolor is used by the R_Fill* routines. It is premultiplied // with the alpha. dc_srccolor = ((((r*x) >> 4) << 20) | ((g*x) >> 4) | ((((b)*x) >> 4) << 10)) & 0x3feffbff; hcolfunc_pre = R_FillColumnHoriz; R_SetColorMapLight(identitycolormap.Maps, 0, 0); } if (!R_SetBlendFunc(style.BlendOp, fglevel, bglevel, style.Flags)) { return DontDraw; } return DoDraw0; } ESPSResult R_SetPatchStyle(FRenderStyle style, float alpha, int translation, uint32_t color) { return R_SetPatchStyle(style, FLOAT2FIXED(alpha), translation, color); } void R_FinishSetPatchStyle() { basecolormap = basecolormapsave; } const uint8_t *R_GetColumn(FTexture *tex, int col) { int width; // If the texture's width isn't a power of 2, then we need to make it a // positive offset for proper clamping. if (col < 0 && (width = tex->GetWidth()) != (1 << tex->WidthBits)) { col = width + (col % width); } return tex->GetColumn(col, nullptr); } bool R_GetTransMaskDrawers(void(**drawCol1)(), void(**drawCol4)()) { if (colfunc == R_DrawAddColumn) { *drawCol1 = R_DrawWallAddCol1; *drawCol4 = R_DrawWallAddCol4; return true; } if (colfunc == R_DrawAddClampColumn) { *drawCol1 = R_DrawWallAddClampCol1; *drawCol4 = R_DrawWallAddClampCol4; return true; } if (colfunc == R_DrawSubClampColumn) { *drawCol1 = R_DrawWallSubClampCol1; *drawCol4 = R_DrawWallSubClampCol4; return true; } if (colfunc == R_DrawRevSubClampColumn) { *drawCol1 = R_DrawWallRevSubClampCol1; *drawCol4 = R_DrawWallRevSubClampCol4; return true; } return false; } void R_SetColorMapLight(lighttable_t *base_colormap, float light, int shade) { using namespace drawerargs; dc_colormap = base_colormap + (GETPALOOKUP(light, shade) << COLORMAPSHIFT); } void R_SetColorMapLight(FDynamicColormap *base_colormap, float light, int shade) { R_SetColorMapLight(base_colormap->Maps, light, shade); } void R_SetDSColorMapLight(lighttable_t *base_colormap, float light, int shade) { using namespace drawerargs; ds_colormap = base_colormap + (GETPALOOKUP(light, shade) << COLORMAPSHIFT); } void R_SetDSColorMapLight(FDynamicColormap *base_colormap, float light, int shade) { R_SetDSColorMapLight(base_colormap->Maps, light, shade); } void R_SetTranslationMap(lighttable_t *translation) { using namespace drawerargs; dc_colormap = translation; } void rt_initcols(uint8_t *buffer) { using namespace drawerargs; for (int y = 3; y >= 0; y--) horizspan[y] = dc_ctspan[y] = &dc_tspans[y][0]; DrawerCommandQueue::QueueCommand(buffer); } void rt_span_coverage(int x, int start, int stop) { using namespace drawerargs; unsigned int **tspan = &dc_ctspan[x & 3]; (*tspan)[0] = start; (*tspan)[1] = stop; *tspan += 2; } void rt_flip_posts() { using namespace drawerargs; unsigned int *front = horizspan[dc_x & 3]; unsigned int *back = dc_ctspan[dc_x & 3] - 2; while (front < back) { swapvalues(front[0], back[0]); swapvalues(front[1], back[1]); front += 2; back -= 2; } } void rt_draw4cols(int sx) { using namespace drawerargs; int x, bad; unsigned int maxtop, minbot, minnexttop; // Place a dummy "span" in each column. These don't get // drawn. They're just here to avoid special cases in the // max/min calculations below. for (x = 0; x < 4; ++x) { dc_ctspan[x][0] = screen->GetHeight()+1; dc_ctspan[x][1] = screen->GetHeight(); } for (;;) { // If a column is out of spans, mark it as such bad = 0; minnexttop = 0xffffffff; for (x = 0; x < 4; ++x) { if (horizspan[x] >= dc_ctspan[x]) { bad |= 1 << x; } else if ((horizspan[x]+2)[0] < minnexttop) { minnexttop = (horizspan[x]+2)[0]; } } // Once all columns are out of spans, we're done if (bad == 15) { return; } // Find the largest shared area for the spans in each column maxtop = MAX (MAX (horizspan[0][0], horizspan[1][0]), MAX (horizspan[2][0], horizspan[3][0])); minbot = MIN (MIN (horizspan[0][1], horizspan[1][1]), MIN (horizspan[2][1], horizspan[3][1])); // If there is no shared area with these spans, draw each span // individually and advance to the next spans until we reach a shared area. // However, only draw spans down to the highest span in the next set of // spans. If we allow the entire height of a span to be drawn, it could // prevent any more shared areas from being drawn in these four columns. // // Example: Suppose we have the following arrangement: // A CD // A CD // B D // B D // aB D // aBcD // aBcD // aBc // // If we draw the entire height of the spans, we end up drawing this first: // A CD // A CD // B D // B D // B D // B D // B D // B D // B // // This leaves only the "a" and "c" columns to be drawn, and they are not // part of a shared area, but if we can include B and D with them, we can // get a shared area. So we cut off everything in the first set just // above the "a" column and end up drawing this first: // A CD // A CD // B D // B D // // Then the next time through, we have the following arrangement with an // easily shared area to draw: // aB D // aBcD // aBcD // aBc if (bad != 0 || maxtop > minbot) { int drawcount = 0; for (x = 0; x < 4; ++x) { if (!(bad & 1)) { if (horizspan[x][1] < minnexttop) { hcolfunc_post1 (x, sx+x, horizspan[x][0], horizspan[x][1]); horizspan[x] += 2; drawcount++; } else if (minnexttop > horizspan[x][0]) { hcolfunc_post1 (x, sx+x, horizspan[x][0], minnexttop-1); horizspan[x][0] = minnexttop; drawcount++; } } bad >>= 1; } // Drawcount *should* always be non-zero. The reality is that some situations // can make this not true. Unfortunately, I'm not sure what those situations are. if (drawcount == 0) { return; } continue; } // Draw any span fragments above the shared area. for (x = 0; x < 4; ++x) { if (maxtop > horizspan[x][0]) { hcolfunc_post1 (x, sx+x, horizspan[x][0], maxtop-1); } } // Draw the shared area. hcolfunc_post4 (sx, maxtop, minbot); // For each column, if part of the span is past the shared area, // set its top to just below the shared area. Otherwise, advance // to the next span in that column. for (x = 0; x < 4; ++x) { if (minbot < horizspan[x][1]) { horizspan[x][0] = minbot+1; } else { horizspan[x] += 2; } } } } void R_SetupSpanBits(FTexture *tex) { using namespace drawerargs; tex->GetWidth(); ds_xbits = tex->WidthBits; ds_ybits = tex->HeightBits; if ((1 << ds_xbits) > tex->GetWidth()) { ds_xbits--; } if ((1 << ds_ybits) > tex->GetHeight()) { ds_ybits--; } } void R_SetSpanColormap(lighttable_t *colormap) { using namespace drawerargs; ds_colormap = colormap; } void R_SetSpanSource(FTexture *tex) { using namespace drawerargs; ds_source = tex->GetPixels(); } ///////////////////////////////////////////////////////////////////////// void R_FillColumnHoriz() { using namespace drawerargs; if (dc_count <= 0) return; int x = dc_x & 3; unsigned int **span = &dc_ctspan[x]; (*span)[0] = dc_yl; (*span)[1] = dc_yh; *span += 2; DrawerCommandQueue::QueueCommand(); } void R_DrawColumnHoriz() { using namespace drawerargs; if (dc_count <= 0) return; int x = dc_x & 3; unsigned int **span = &dc_ctspan[x]; (*span)[0] = dc_yl; (*span)[1] = dc_yh; *span += 2; DrawerCommandQueue::QueueCommand(); } // Copies one span at hx to the screen at sx. void rt_copy1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); } // Copies all four spans to the screen starting at sx. void rt_copy4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); } // Maps one span at hx to the screen at sx. void rt_map1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); } // Maps all four spans to the screen starting at sx. void rt_map4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); } // Translates one span at hx to the screen at sx. void rt_tlate1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); rt_map1col(hx, sx, yl, yh); } // Translates all four spans to the screen starting at sx. void rt_tlate4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); rt_map4cols(sx, yl, yh); } // Adds one span at hx to the screen at sx without clamping. void rt_add1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); } // Adds all four spans to the screen starting at sx without clamping. void rt_add4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); } // Translates and adds one span at hx to the screen at sx without clamping. void rt_tlateadd1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); rt_add1col(hx, sx, yl, yh); } // Translates and adds all four spans to the screen starting at sx without clamping. void rt_tlateadd4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); rt_add4cols(sx, yl, yh); } // Shades one span at hx to the screen at sx. void rt_shaded1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); } // Shades all four spans to the screen starting at sx. void rt_shaded4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); } // Adds one span at hx to the screen at sx with clamping. void rt_addclamp1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); } // Adds all four spans to the screen starting at sx with clamping. void rt_addclamp4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); } // Translates and adds one span at hx to the screen at sx with clamping. void rt_tlateaddclamp1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); rt_addclamp1col(hx, sx, yl, yh); } // Translates and adds all four spans to the screen starting at sx with clamping. void rt_tlateaddclamp4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); rt_addclamp4cols(sx, yl, yh); } // Subtracts one span at hx to the screen at sx with clamping. void rt_subclamp1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); } // Subtracts all four spans to the screen starting at sx with clamping. void rt_subclamp4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); } // Translates and subtracts one span at hx to the screen at sx with clamping. void rt_tlatesubclamp1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); rt_subclamp1col(hx, sx, yl, yh); } // Translates and subtracts all four spans to the screen starting at sx with clamping. void rt_tlatesubclamp4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); rt_subclamp4cols(sx, yl, yh); } // Subtracts one span at hx from the screen at sx with clamping. void rt_revsubclamp1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); } // Subtracts all four spans from the screen starting at sx with clamping. void rt_revsubclamp4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); } // Translates and subtracts one span at hx from the screen at sx with clamping. void rt_tlaterevsubclamp1col(int hx, int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(hx, sx, yl, yh); rt_revsubclamp1col(hx, sx, yl, yh); } // Translates and subtracts all four spans from the screen starting at sx with clamping. void rt_tlaterevsubclamp4cols(int sx, int yl, int yh) { DrawerCommandQueue::QueueCommand(0, sx, yl, yh); rt_revsubclamp4cols(sx, yl, yh); } void R_DrawWallCol1() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallCol4() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallMaskedCol1() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallMaskedCol4() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallAddCol1() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallAddCol4() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallAddClampCol1() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallAddClampCol4() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallSubClampCol1() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallSubClampCol4() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallRevSubClampCol1() { DrawerCommandQueue::QueueCommand(); } void R_DrawWallRevSubClampCol4() { DrawerCommandQueue::QueueCommand(); } void R_DrawSingleSkyCol1(uint32_t solid_top, uint32_t solid_bottom) { DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); } void R_DrawSingleSkyCol4(uint32_t solid_top, uint32_t solid_bottom) { DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); } void R_DrawDoubleSkyCol1(uint32_t solid_top, uint32_t solid_bottom) { DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); } void R_DrawDoubleSkyCol4(uint32_t solid_top, uint32_t solid_bottom) { DrawerCommandQueue::QueueCommand(solid_top, solid_bottom); } void R_DrawColumn() { DrawerCommandQueue::QueueCommand(); } void R_FillColumn() { DrawerCommandQueue::QueueCommand(); } void R_FillAddColumn() { DrawerCommandQueue::QueueCommand(); } void R_FillAddClampColumn() { DrawerCommandQueue::QueueCommand(); } void R_FillSubClampColumn() { DrawerCommandQueue::QueueCommand(); } void R_FillRevSubClampColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawFuzzColumn() { using namespace drawerargs; DrawerCommandQueue::QueueCommand(); dc_yl = MAX(dc_yl, 1); dc_yh = MIN(dc_yh, fuzzviewheight); if (dc_yl <= dc_yh) fuzzpos = (fuzzpos + dc_yh - dc_yl + 1) % FUZZTABLE; } void R_DrawAddColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawTranslatedColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawTlatedAddColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawShadedColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawAddClampColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawAddClampTranslatedColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawSubClampColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawSubClampTranslatedColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawRevSubClampColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawRevSubClampTranslatedColumn() { DrawerCommandQueue::QueueCommand(); } void R_DrawSpan() { DrawerCommandQueue::QueueCommand(); } void R_DrawSpanMasked() { DrawerCommandQueue::QueueCommand(); } void R_DrawSpanTranslucent() { DrawerCommandQueue::QueueCommand(); } void R_DrawSpanMaskedTranslucent() { DrawerCommandQueue::QueueCommand(); } void R_DrawSpanAddClamp() { DrawerCommandQueue::QueueCommand(); } void R_DrawSpanMaskedAddClamp() { DrawerCommandQueue::QueueCommand(); } void R_FillSpan() { DrawerCommandQueue::QueueCommand(); } void R_DrawTiltedSpan(int y, int x1, int x2, const FVector3 &plane_sz, const FVector3 &plane_su, const FVector3 &plane_sv, bool plane_shade, int planeshade, float planelightfloat, fixed_t pviewx, fixed_t pviewy) { DrawerCommandQueue::QueueCommand(y, x1, x2, plane_sz, plane_su, plane_sv, plane_shade, planeshade, planelightfloat, pviewx, pviewy); } void R_DrawColoredSpan(int y, int x1, int x2) { DrawerCommandQueue::QueueCommand(y, x1, x2); } namespace { const uint8_t *slab_colormap; } void R_SetupDrawSlab(uint8_t *colormap) { slab_colormap = colormap; } void R_DrawSlab(int dx, fixed_t v, int dy, fixed_t vi, const uint8_t *vptr, uint8_t *p) { DrawerCommandQueue::QueueCommand(dx, v, dy, vi, vptr, p, slab_colormap); } void R_DrawFogBoundarySection(int y, int y2, int x1) { for (; y < y2; ++y) { int x2 = spanend[y]; DrawerCommandQueue::QueueCommand(y, x1, x2); } } void R_DrawFogBoundary(int x1, int x2, short *uclip, short *dclip) { // This is essentially the same as R_MapVisPlane but with an extra step // to create new horizontal spans whenever the light changes enough that // we need to use a new colormap. double lightstep = rw_lightstep; double light = rw_light + rw_lightstep*(x2 - x1 - 1); int x = x2 - 1; int t2 = uclip[x]; int b2 = dclip[x]; int rcolormap = GETPALOOKUP(light, wallshade); int lcolormap; uint8_t *basecolormapdata = basecolormap->Maps; if (b2 > t2) { fillshort(spanend + t2, b2 - t2, x); } R_SetColorMapLight(basecolormap->Maps, (float)light, wallshade); uint8_t *fake_dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); for (--x; x >= x1; --x) { int t1 = uclip[x]; int b1 = dclip[x]; const int xr = x + 1; int stop; light -= rw_lightstep; lcolormap = GETPALOOKUP(light, wallshade); if (lcolormap != rcolormap) { if (t2 < b2 && rcolormap != 0) { // Colormap 0 is always the identity map, so rendering it is // just a waste of time. R_DrawFogBoundarySection(t2, b2, xr); } if (t1 < t2) t2 = t1; if (b1 > b2) b2 = b1; if (t2 < b2) { fillshort(spanend + t2, b2 - t2, x); } rcolormap = lcolormap; R_SetColorMapLight(basecolormap->Maps, (float)light, wallshade); fake_dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); } else { if (fake_dc_colormap != basecolormapdata) { stop = MIN(t1, b2); while (t2 < stop) { int y = t2++; DrawerCommandQueue::QueueCommand(y, xr, spanend[y]); } stop = MAX(b1, t2); while (b2 > stop) { int y = --b2; DrawerCommandQueue::QueueCommand(y, xr, spanend[y]); } } else { t2 = MAX(t2, MIN(t1, b2)); b2 = MIN(b2, MAX(b1, t2)); } stop = MIN(t2, b1); while (t1 < stop) { spanend[t1++] = x; } stop = MAX(b2, t2); while (b1 > stop) { spanend[--b1] = x; } } t2 = uclip[x]; b2 = dclip[x]; } if (t2 < b2 && rcolormap != 0) { R_DrawFogBoundarySection(t2, b2, x1); } } void R_DrawParticle(vissprite_t *sprite) { R_DrawParticle_C(sprite); } }