diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a3c63bb0b..aa67b13d1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -828,6 +828,7 @@ set( FASTMATH_PCH_SOURCES r_segs.cpp r_sky.cpp r_things.cpp + r_walldraw.cpp s_advsound.cpp s_environment.cpp s_playlist.cpp diff --git a/src/p_actionfunctions.cpp b/src/p_actionfunctions.cpp index 12645561b..4b6113768 100644 --- a/src/p_actionfunctions.cpp +++ b/src/p_actionfunctions.cpp @@ -1524,7 +1524,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_CustomMissile) Pitch += missile->Vel.Pitch(); } missilespeed = fabs(Pitch.Cos() * missile->Speed); - missile->Vel.Z = Pitch.Sin() * missile->Speed; + missile->Vel.Z = -Pitch.Sin() * missile->Speed; } else { diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index 230e16dcc..9918e62a9 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -639,12 +639,12 @@ void R_AddLine (seg_t *line) if (rw_frontcz1 > rw_backcz1 || rw_frontcz2 > rw_backcz2) { rw_havehigh = true; - WallMost (wallupper, backsector->ceilingplane, &WallC); + R_CreateWallSegmentYSloped (wallupper, backsector->ceilingplane, &WallC); } if (rw_frontfz1 < rw_backfz1 || rw_frontfz2 < rw_backfz2) { rw_havelow = true; - WallMost (walllower, backsector->floorplane, &WallC); + R_CreateWallSegmentYSloped (walllower, backsector->floorplane, &WallC); } // Portal @@ -745,8 +745,8 @@ void R_AddLine (seg_t *line) } else { - rw_ceilstat = WallMost (walltop, frontsector->ceilingplane, &WallC); - rw_floorstat = WallMost (wallbottom, frontsector->floorplane, &WallC); + rw_ceilstat = R_CreateWallSegmentYSloped (walltop, frontsector->ceilingplane, &WallC); + rw_floorstat = R_CreateWallSegmentYSloped (wallbottom, frontsector->floorplane, &WallC); // [RH] treat off-screen walls as solid #if 0 // Maybe later... diff --git a/src/r_draw.h b/src/r_draw.h index 8c1af58fc..9f8b1d03b 100644 --- a/src/r_draw.h +++ b/src/r_draw.h @@ -133,10 +133,7 @@ namespace swrenderer bool R_GetTransMaskDrawers(fixed_t(**tmvline1)(), void(**tmvline4)()); const uint8_t *R_GetColumn(FTexture *tex, int col); - void wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const uint8_t *(*getcol)(FTexture *tex, int col) = R_GetColumn); - void maskwallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const uint8_t *(*getcol)(FTexture *tex, int col) = R_GetColumn); - void transmaskwallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const uint8_t *(*getcol)(FTexture *tex, int col) = R_GetColumn); - + void rt_initcols(uint8_t *buffer = nullptr); void rt_span_coverage(int x, int start, int stop); void rt_draw4cols(int sx); diff --git a/src/r_main.cpp b/src/r_main.cpp index e79800e49..5242f369f 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -1081,8 +1081,8 @@ CCMD (clearwallcycles) bestwallcycles = HUGE_VAL; } -#if 1 -// To use these, also uncomment the clock/unclock in wallscan +#if 0 +// The replacement code for Build's wallscan doesn't have any timing calls so this does not work anymore. static double bestscancycles = HUGE_VAL; ADD_STAT (scancycles) diff --git a/src/r_plane.cpp b/src/r_plane.cpp index f8893dcd7..ba139b22c 100644 --- a/src/r_plane.cpp +++ b/src/r_plane.cpp @@ -152,6 +152,7 @@ static double xstepscale, ystepscale; static double basexfrac, baseyfrac; void R_DrawSinglePlane (visplane_t *, fixed_t alpha, bool additive, bool masked); +void R_DrawSkySegment(visplane_t *vis, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const uint8_t *(*getcol)(FTexture *tex, int col)); //========================================================================== // @@ -635,7 +636,7 @@ extern FTexture *rw_pic; // Allow for layer skies up to 512 pixels tall. This is overkill, // since the most anyone can ever see of the sky is 500 pixels. -// We need 4 skybufs because wallscan can draw up to 4 columns at a time. +// We need 4 skybufs because R_DrawSkySegment can draw up to 4 columns at a time. // Need two versions - one for true color and one for palette #define MAXSKYBUF 3072 static BYTE skybuf[4][512]; @@ -997,7 +998,7 @@ static void R_DrawSky (visplane_t *pl) lastskycol[x] = 0xffffffff; lastskycol_bgra[x] = 0xffffffff; } - wallscan (pl->left, pl->right, (short *)pl->top, (short *)pl->bottom, swall, lwall, + R_DrawSkySegment (pl, (short *)pl->top, (short *)pl->bottom, swall, lwall, frontyScale, backskytex == NULL ? R_GetOneSkyColumn : R_GetTwoSkyColumns); } else @@ -1035,7 +1036,7 @@ static void R_DrawSkyStriped (visplane_t *pl) lastskycol[x] = 0xffffffff; lastskycol_bgra[x] = 0xffffffff; } - wallscan (pl->left, pl->right, top, bot, swall, lwall, rw_pic->Scale.Y, + R_DrawSkySegment (pl, top, bot, swall, lwall, rw_pic->Scale.Y, backskytex == NULL ? R_GetOneSkyColumn : R_GetTwoSkyColumns); yl = yh; yh += drawheight; diff --git a/src/r_segs.cpp b/src/r_segs.cpp index c24e7f719..38db1a6b2 100644 --- a/src/r_segs.cpp +++ b/src/r_segs.cpp @@ -50,7 +50,7 @@ #define WALLYREPEAT 8 -CVAR(Bool, r_np2, true, 0) + CVAR(Bool, r_fogboundary, true, 0) CVAR(Bool, r_drawmirrors, true, 0) EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); @@ -60,6 +60,9 @@ namespace swrenderer { using namespace drawerargs; + void R_DrawWallSegment(FTexture *rw_pic, int x1, int x2, short *walltop, short *wallbottom, float *swall, fixed_t *lwall, double yscale, double top, double bottom, bool mask); + void R_DrawDrawSeg(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat); + #define HEIGHTBITS 12 #define HEIGHTSHIFT (FRACBITS-HEIGHTBITS) @@ -135,9 +138,6 @@ static fixed_t *maskedtexturecol; static void R_RenderDecal (side_t *wall, DBaseDecal *first, drawseg_t *clipper, int pass); static void WallSpriteColumn (void (*drawfunc)(const BYTE *column, const FTexture::Span *spans)); -void wallscan_np2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask); -static void wallscan_np2_ds(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat); -static void call_wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask); inline bool IsFogBoundary (sector_t *front, sector_t *back) { @@ -186,13 +186,13 @@ void ClipMidtex(int x1, int x2) { short most[MAXWIDTH]; - WallMost(most, curline->frontsector->ceilingplane, &WallC); + R_CreateWallSegmentYSloped(most, curline->frontsector->ceilingplane, &WallC); for (int i = x1; i < x2; ++i) { if (wallupper[i] < most[i]) wallupper[i] = most[i]; } - WallMost(most, curline->frontsector->floorplane, &WallC); + R_CreateWallSegmentYSloped(most, curline->frontsector->floorplane, &WallC); for (int i = x1; i < x2; ++i) { if (walllower[i] > most[i]) @@ -374,19 +374,19 @@ void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) if (fake3D & FAKE3D_CLIPTOP) { - OWallMost(wallupper, textop < sclipTop - ViewPos.Z ? textop : sclipTop - ViewPos.Z, &WallC); + R_CreateWallSegmentY(wallupper, textop < sclipTop - ViewPos.Z ? textop : sclipTop - ViewPos.Z, &WallC); } else { - OWallMost(wallupper, textop, &WallC); + R_CreateWallSegmentY(wallupper, textop, &WallC); } if (fake3D & FAKE3D_CLIPBOTTOM) { - OWallMost(walllower, textop - texheight > sclipBottom - ViewPos.Z ? textop - texheight : sclipBottom - ViewPos.Z, &WallC); + R_CreateWallSegmentY(walllower, textop - texheight > sclipBottom - ViewPos.Z ? textop - texheight : sclipBottom - ViewPos.Z, &WallC); } else { - OWallMost(walllower, textop - texheight, &WallC); + R_CreateWallSegmentY(walllower, textop - texheight, &WallC); } for (i = x1; i < x2; i++) @@ -489,7 +489,7 @@ void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) if (fake3D & FAKE3D_CLIPTOP) { - OWallMost(wallupper, sclipTop - ViewPos.Z, &WallC); + R_CreateWallSegmentY(wallupper, sclipTop - ViewPos.Z, &WallC); for (i = x1; i < x2; i++) { if (wallupper[i] < mceilingclip[i]) @@ -499,7 +499,7 @@ void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) } if (fake3D & FAKE3D_CLIPBOTTOM) { - OWallMost(walllower, sclipBottom - ViewPos.Z, &WallC); + R_CreateWallSegmentY(walllower, sclipBottom - ViewPos.Z, &WallC); for (i = x1; i < x2; i++) { if (walllower[i] > mfloorclip[i]) @@ -510,7 +510,7 @@ void R_RenderMaskedSegRange (drawseg_t *ds, int x1, int x2) rw_offset = 0; rw_pic = tex; - wallscan_np2_ds(ds, x1, x2, mceilingclip, mfloorclip, MaskedSWall, maskedtexturecol, ds->yscale); + R_DrawDrawSeg(ds, x1, x2, mceilingclip, mfloorclip, MaskedSWall, maskedtexturecol, ds->yscale); } clearfog: @@ -623,8 +623,8 @@ void R_RenderFakeWall(drawseg_t *ds, int x1, int x2, F3DFloor *rover) WallC.tright.Y = ds->cy + ds->cdy; WallT = ds->tmapvals; - OWallMost(wallupper, sclipTop - ViewPos.Z, &WallC); - OWallMost(walllower, sclipBottom - ViewPos.Z, &WallC); + R_CreateWallSegmentY(wallupper, sclipTop - ViewPos.Z, &WallC); + R_CreateWallSegmentY(walllower, sclipBottom - ViewPos.Z, &WallC); for (i = x1; i < x2; i++) { @@ -638,7 +638,7 @@ void R_RenderFakeWall(drawseg_t *ds, int x1, int x2, F3DFloor *rover) } PrepLWall (lwall, curline->sidedef->TexelLength*xscale, ds->sx1, ds->sx2); - wallscan_np2_ds(ds, x1, x2, wallupper, walllower, MaskedSWall, lwall, yscale); + R_DrawDrawSeg(ds, x1, x2, wallupper, walllower, MaskedSWall, lwall, yscale); R_FinishSetPatchStyle(); } @@ -1044,719 +1044,12 @@ void R_RenderFakeWallRange (drawseg_t *ds, int x1, int x2) return; } - -struct WallscanSampler -{ - WallscanSampler() { } - WallscanSampler(int y1, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)); - - uint32_t uv_pos; - uint32_t uv_step; - uint32_t uv_max; - - const BYTE *source; - const BYTE *source2; - uint32_t texturefracx; - uint32_t height; -}; - -WallscanSampler::WallscanSampler(int y1, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)) -{ - xoffset += FLOAT2FIXED(xmagnitude * 0.5); - - if (!r_swtruecolor) - { - height = texture->GetHeight(); - - int uv_fracbits = 32 - texture->HeightBits; - if (uv_fracbits != 32) - { - uv_max = height << uv_fracbits; - - // Find start uv in [0-base_height[ range. - // Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range. - double uv_stepd = swal * yrepeat; - double v = (dc_texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; - v = v - floor(v); - v *= height; - v *= (1 << uv_fracbits); - - uv_pos = (uint32_t)v; - uv_step = xs_ToFixed(uv_fracbits, uv_stepd); - if (uv_step == 0) // To prevent divide by zero elsewhere - uv_step = 1; - } - else - { // Hack for one pixel tall textures - uv_pos = 0; - uv_step = 0; - uv_max = 1; - } - - source = getcol(texture, xoffset >> FRACBITS); - source2 = nullptr; - texturefracx = 0; - } - else - { - // Normalize to 0-1 range: - double uv_stepd = swal * yrepeat; - double v = (dc_texturemid + uv_stepd * (y1 - CenterY + 0.5)) / texture->GetHeight(); - v = v - floor(v); - double v_step = uv_stepd / texture->GetHeight(); - - if (isnan(v) || isnan(v_step)) // this should never happen, but it apparently does.. - { - uv_stepd = 0.0; - v = 0.0; - v_step = 0.0; - } - - // Convert to uint32: - uv_pos = (uint32_t)(v * 0x100000000LL); - uv_step = (uint32_t)(v_step * 0x100000000LL); - uv_max = 0; - - // Texture mipmap and filter selection: - if (getcol != R_GetColumn) - { - source = getcol(texture, xoffset >> FRACBITS); - source2 = nullptr; - height = texture->GetHeight(); - texturefracx = 0; - } - else - { - double ymagnitude = fabs(uv_stepd); - double magnitude = MAX(ymagnitude, xmagnitude); - double min_lod = -1000.0; - double lod = MAX(log2(magnitude) + r_lod_bias, min_lod); - bool magnifying = lod < 0.0f; - - int mipmap_offset = 0; - int mip_width = texture->GetWidth(); - int mip_height = texture->GetHeight(); - if (r_mipmap && texture->Mipmapped() && mip_width > 1 && mip_height > 1) - { - uint32_t xpos = (uint32_t)((((uint64_t)xoffset) << FRACBITS) / mip_width); - - int level = (int)lod; - while (level > 0 && mip_width > 1 && mip_height > 1) - { - mipmap_offset += mip_width * mip_height; - level--; - mip_width = MAX(mip_width >> 1, 1); - mip_height = MAX(mip_height >> 1, 1); - } - xoffset = (xpos >> FRACBITS) * mip_width; - } - - const uint32_t *pixels = texture->GetPixelsBgra() + mipmap_offset; - - bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter); - if (filter_nearest) - { - int tx = (xoffset >> FRACBITS) % mip_width; - if (tx < 0) - tx += mip_width; - source = (BYTE*)(pixels + tx * mip_height); - source2 = nullptr; - height = mip_height; - texturefracx = 0; - } - else - { - xoffset -= FRACUNIT / 2; - int tx0 = (xoffset >> FRACBITS) % mip_width; - if (tx0 < 0) - tx0 += mip_width; - int tx1 = (tx0 + 1) % mip_width; - source = (BYTE*)(pixels + tx0 * mip_height); - source2 = (BYTE*)(pixels + tx1 * mip_height); - height = mip_height; - texturefracx = (xoffset >> (FRACBITS - 4)) & 15; - } - } - } -} - -// Draw a column with support for non-power-of-two ranges -void wallscan_drawcol1(int x, int y1, int y2, WallscanSampler &sampler, DWORD(*draw1column)()) -{ - if (r_swtruecolor) - { - int count = y2 - y1; - - dc_source = sampler.source; - dc_source2 = sampler.source2; - dc_texturefracx = sampler.texturefracx; - dc_dest = (ylookup[y1] + x) * 4 + dc_destorg; - dc_count = count; - dc_iscale = sampler.uv_step; - dc_texturefrac = sampler.uv_pos; - dc_textureheight = sampler.height; - draw1column(); - - uint64_t step64 = sampler.uv_step; - uint64_t pos64 = sampler.uv_pos; - sampler.uv_pos = (uint32_t)(pos64 + step64 * count); - } - else - { - if (sampler.uv_max == 0 || sampler.uv_step == 0) // power of two - { - int count = y2 - y1; - - dc_source = sampler.source; - dc_source2 = sampler.source2; - dc_texturefracx = sampler.texturefracx; - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - dc_iscale = sampler.uv_step; - dc_texturefrac = sampler.uv_pos; - draw1column(); - - uint64_t step64 = sampler.uv_step; - uint64_t pos64 = sampler.uv_pos; - sampler.uv_pos = (uint32_t)(pos64 + step64 * count); - } - else - { - uint32_t uv_pos = sampler.uv_pos; - - uint32_t left = y2 - y1; - while (left > 0) - { - uint32_t available = sampler.uv_max - uv_pos; - uint32_t next_uv_wrap = available / sampler.uv_step; - if (available % sampler.uv_step != 0) - next_uv_wrap++; - uint32_t count = MIN(left, next_uv_wrap); - - dc_source = sampler.source; - dc_source2 = sampler.source2; - dc_texturefracx = sampler.texturefracx; - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - dc_iscale = sampler.uv_step; - dc_texturefrac = uv_pos; - draw1column(); - - left -= count; - uv_pos += sampler.uv_step * count; - if (uv_pos >= sampler.uv_max) - uv_pos -= sampler.uv_max; - } - - sampler.uv_pos = uv_pos; - } - } -} - -// Draw four columns with support for non-power-of-two ranges -void wallscan_drawcol4(int x, int y1, int y2, WallscanSampler *sampler, void(*draw4columns)()) -{ - if (r_swtruecolor) - { - int count = y2 - y1; - for (int i = 0; i < 4; i++) - { - bufplce[i] = sampler[i].source; - bufplce2[i] = sampler[i].source2; - buftexturefracx[i] = sampler[i].texturefracx; - bufheight[i] = sampler[i].height; - vplce[i] = sampler[i].uv_pos; - vince[i] = sampler[i].uv_step; - - uint64_t step64 = sampler[i].uv_step; - uint64_t pos64 = sampler[i].uv_pos; - sampler[i].uv_pos = (uint32_t)(pos64 + step64 * count); - } - dc_dest = (ylookup[y1] + x) * 4 + dc_destorg; - dc_count = count; - draw4columns(); - } - else - { - if (sampler[0].uv_max == 0 || sampler[0].uv_step == 0) // power of two, no wrap handling needed - { - int count = y2 - y1; - for (int i = 0; i < 4; i++) - { - bufplce[i] = sampler[i].source; - bufplce2[i] = sampler[i].source2; - buftexturefracx[i] = sampler[i].texturefracx; - vplce[i] = sampler[i].uv_pos; - vince[i] = sampler[i].uv_step; - - uint64_t step64 = sampler[i].uv_step; - uint64_t pos64 = sampler[i].uv_pos; - sampler[i].uv_pos = (uint32_t)(pos64 + step64 * count); - } - dc_dest = (ylookup[y1] + x) + dc_destorg; - dc_count = count; - draw4columns(); - } - else - { - dc_dest = (ylookup[y1] + x) + dc_destorg; - for (int i = 0; i < 4; i++) - { - bufplce[i] = sampler[i].source; - bufplce2[i] = sampler[i].source2; - buftexturefracx[i] = sampler[i].texturefracx; - } - - uint32_t left = y2 - y1; - while (left > 0) - { - // Find which column wraps first - uint32_t count = left; - for (int i = 0; i < 4; i++) - { - uint32_t available = sampler[i].uv_max - sampler[i].uv_pos; - uint32_t next_uv_wrap = available / sampler[i].uv_step; - if (available % sampler[i].uv_step != 0) - next_uv_wrap++; - count = MIN(next_uv_wrap, count); - } - - // Draw until that column wraps - for (int i = 0; i < 4; i++) - { - vplce[i] = sampler[i].uv_pos; - vince[i] = sampler[i].uv_step; - } - dc_count = count; - draw4columns(); - - // Wrap the uv position - for (int i = 0; i < 4; i++) - { - sampler[i].uv_pos += sampler[i].uv_step * count; - if (sampler[i].uv_pos >= sampler[i].uv_max) - sampler[i].uv_pos -= sampler[i].uv_max; - } - - left -= count; - } - } - } -} - -typedef DWORD(*Draw1ColumnFuncPtr)(); -typedef void(*Draw4ColumnsFuncPtr)(); - -void wallscan_any( - int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, - const BYTE *(*getcol)(FTexture *tex, int x), - void(setupwallscan(int bits, Draw1ColumnFuncPtr &draw1, Draw4ColumnsFuncPtr &draw2))) -{ - if (rw_pic->UseType == FTexture::TEX_Null) - return; - - fixed_t xoffset = rw_offset; - - rw_pic->GetHeight(); // To ensure that rw_pic->HeightBits has been set - int fracbits = 32 - rw_pic->HeightBits; - if (fracbits == 32) - { // Hack for one pixel tall textures - fracbits = 0; - yrepeat = 0; - dc_texturemid = 0; - } - - DWORD(*draw1column)(); - void(*draw4columns)(); - setupwallscan(r_swtruecolor ? FRACBITS : fracbits, draw1column, draw4columns); - - bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); - if (fixed) - { - palookupoffse[0] = dc_colormap; - palookupoffse[1] = dc_colormap; - palookupoffse[2] = dc_colormap; - palookupoffse[3] = dc_colormap; - palookuplight[0] = 0; - palookuplight[1] = 0; - palookuplight[2] = 0; - palookuplight[3] = 0; - } - - if (fixedcolormap) - R_SetColorMapLight(fixedcolormap, 0, 0); - else - R_SetColorMapLight(basecolormap, 0, 0); - - float light = rw_light; - - // Calculate where 4 column alignment begins and ends: - int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); - int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); - - double xmagnitude = 1.0; - - // First unaligned columns: - for (int x = x1; x < aligned_x1; x++, light += rw_lightstep) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - if (!fixed) - R_SetColorMapLight(basecolormap, light, wallshade); - - if (x + 1 < x2) xmagnitude = fabs(FIXED2DBL(lwal[x + 1]) - FIXED2DBL(lwal[x])); - - WallscanSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, xmagnitude, rw_pic, getcol); - wallscan_drawcol1(x, y1, y2, sampler, draw1column); - } - - // The aligned columns - for (int x = aligned_x1; x < aligned_x2; x += 4) - { - // Find y1, y2, light and uv values for four columns: - int y1[4] = { uwal[x], uwal[x + 1], uwal[x + 2], uwal[x + 3] }; - int y2[4] = { dwal[x], dwal[x + 1], dwal[x + 2], dwal[x + 3] }; - - float lights[4]; - for (int i = 0; i < 4; i++) - { - lights[i] = light; - light += rw_lightstep; - } - - WallscanSampler sampler[4]; - for (int i = 0; i < 4; i++) - { - if (x + i + 1 < x2) xmagnitude = fabs(FIXED2DBL(lwal[x + i + 1]) - FIXED2DBL(lwal[x + i])); - sampler[i] = WallscanSampler(y1[i], swal[x + i], yrepeat, lwal[x + i] + xoffset, xmagnitude, rw_pic, getcol); - } - - // Figure out where we vertically can start and stop drawing 4 columns in one go - int middle_y1 = y1[0]; - int middle_y2 = y2[0]; - for (int i = 1; i < 4; i++) - { - middle_y1 = MAX(y1[i], middle_y1); - middle_y2 = MIN(y2[i], middle_y2); - } - - // If we got an empty column in our set we cannot draw 4 columns in one go: - bool empty_column_in_set = false; - int bilinear_count = 0; - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - empty_column_in_set = true; - if (sampler[i].source2) - bilinear_count++; - } - - if (empty_column_in_set || middle_y2 <= middle_y1 || (bilinear_count > 0 && bilinear_count < 4)) - { - for (int i = 0; i < 4; i++) - { - if (y2[i] <= y1[i]) - continue; - - if (!fixed) - R_SetColorMapLight(basecolormap, lights[i], wallshade); - wallscan_drawcol1(x + i, y1[i], y2[i], sampler[i], draw1column); - } - continue; - } - - // Draw the first rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (!fixed) - R_SetColorMapLight(basecolormap, lights[i], wallshade); - - if (y1[i] < middle_y1) - wallscan_drawcol1(x + i, y1[i], middle_y1, sampler[i], draw1column); - } - - // Draw the area where all 4 columns are active - if (!fixed) - { - for (int i = 0; i < 4; i++) - { - if (r_swtruecolor) - { - palookupoffse[i] = basecolormap->Maps; - palookuplight[i] = LIGHTSCALE(lights[i], wallshade); - } - else - { - palookupoffse[i] = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); - palookuplight[i] = 0; - } - } - } - wallscan_drawcol4(x, middle_y1, middle_y2, sampler, draw4columns); - - // Draw the last rows where not all 4 columns are active - for (int i = 0; i < 4; i++) - { - if (!fixed) - R_SetColorMapLight(basecolormap, lights[i], wallshade); - - if (middle_y2 < y2[i]) - wallscan_drawcol1(x + i, middle_y2, y2[i], sampler[i], draw1column); - } - } - - // The last unaligned columns: - for (int x = aligned_x2; x < x2; x++, light += rw_lightstep) - { - int y1 = uwal[x]; - int y2 = dwal[x]; - if (y2 <= y1) - continue; - - if (!fixed) - R_SetColorMapLight(basecolormap, light, wallshade); - - if (x + 1 < x2) xmagnitude = fabs(FIXED2DBL(lwal[x + 1]) - FIXED2DBL(lwal[x])); - - WallscanSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, xmagnitude, rw_pic, getcol); - wallscan_drawcol1(x, y1, y2, sampler, draw1column); - } - - NetUpdate (); -} - -void wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - wallscan_any(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) - { - setupvline(bits); - line1 = dovline1; - line4 = dovline4; - }); -} - -void maskwallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - if (!rw_pic->bMasked) // Textures that aren't masked can use the faster wallscan. - { - wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - } - else - { - wallscan_any(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) - { - setupmvline(bits); - line1 = domvline1; - line4 = domvline4; - }); - } -} - -void transmaskwallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) -{ - static fixed_t(*tmvline1)(); - static void(*tmvline4)(); - if (!R_GetTransMaskDrawers(&tmvline1, &tmvline4)) - { - // The current translucency is unsupported, so draw with regular maskwallscan instead. - maskwallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); - } - else - { - wallscan_any(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) - { - setuptmvline(bits); - line1 = reinterpret_cast(tmvline1); - line4 = tmvline4; - }); - } -} - -void wallscan_striped (int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) -{ - FDynamicColormap *startcolormap = basecolormap; - int startshade = wallshade; - bool fogginess = foggy; - - short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; - short *up, *down; - - up = uwal; - down = most1; - - assert(WallC.sx1 <= x1); - assert(WallC.sx2 >= x2); - - // kg3D - fake floors instead of zdoom light list - for (unsigned int i = 0; i < frontsector->e->XFloor.lightlist.Size(); i++) - { - int j = WallMost (most3, frontsector->e->XFloor.lightlist[i].plane, &WallC); - if (j != 3) - { - for (int j = x1; j < x2; ++j) - { - down[j] = clamp (most3[j], up[j], dwal[j]); - } - wallscan (x1, x2, up, down, swal, lwal, yrepeat); - up = down; - down = (down == most1) ? most2 : most1; - } - - lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; - basecolormap = lit->extra_colormap; - wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(fogginess, - *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); - } - - wallscan (x1, x2, up, dwal, swal, lwal, yrepeat); - basecolormap = startcolormap; - wallshade = startshade; -} - -static void call_wallscan(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask) -{ - if (mask) - { - if (colfunc == basecolfunc) - { - maskwallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - else - { - transmaskwallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - } - else - { - if (fixedcolormap != NULL || fixedlightlev >= 0 || !(frontsector->e && frontsector->e->XFloor.lightlist.Size())) - { - wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - else - { - wallscan_striped(x1, x2, uwal, dwal, swal, lwal, yrepeat); - } - } -} - -//============================================================================= -// -// wallscan_np2 -// -// This is a wrapper around wallscan that helps it tile textures whose heights -// are not powers of 2. It divides the wall into texture-sized strips and calls -// wallscan for each of those. Since only one repetition of the texture fits -// in each strip, wallscan will not tile. -// -//============================================================================= - -void wallscan_np2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask) -{ - if (!r_np2) - { - call_wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, mask); - } - else - { - short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; - short *up, *down; - double texheight = rw_pic->GetHeight(); - double partition; - double scaledtexheight = texheight / yrepeat; - - if (yrepeat >= 0) - { // normal orientation: draw strips from top to bottom - partition = top - fmod(top - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); - if (partition == top) - { - partition -= scaledtexheight; - } - up = uwal; - down = most1; - dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; - while (partition > bot) - { - int j = OWallMost(most3, partition - ViewPos.Z, &WallC); - if (j != 3) - { - for (int j = x1; j < x2; ++j) - { - down[j] = clamp(most3[j], up[j], dwal[j]); - } - call_wallscan(x1, x2, up, down, swal, lwal, yrepeat, mask); - up = down; - down = (down == most1) ? most2 : most1; - } - partition -= scaledtexheight; - dc_texturemid -= texheight; - } - call_wallscan(x1, x2, up, dwal, swal, lwal, yrepeat, mask); - } - else - { // upside down: draw strips from bottom to top - partition = bot - fmod(bot - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); - up = most1; - down = dwal; - dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; - while (partition < top) - { - int j = OWallMost(most3, partition - ViewPos.Z, &WallC); - if (j != 12) - { - for (int j = x1; j < x2; ++j) - { - up[j] = clamp(most3[j], uwal[j], down[j]); - } - call_wallscan(x1, x2, up, down, swal, lwal, yrepeat, mask); - down = up; - up = (up == most1) ? most2 : most1; - } - partition -= scaledtexheight; - dc_texturemid -= texheight; - } - call_wallscan(x1, x2, uwal, down, swal, lwal, yrepeat, mask); - } - } -} - -static void wallscan_np2_ds(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) -{ - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - double frontcz1 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v1); - double frontfz1 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v1); - double frontcz2 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v2); - double frontfz2 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v2); - double top = MAX(frontcz1, frontcz2); - double bot = MIN(frontfz1, frontfz2); - if (fake3D & FAKE3D_CLIPTOP) - { - top = MIN(top, sclipTop); - } - if (fake3D & FAKE3D_CLIPBOTTOM) - { - bot = MAX(bot, sclipBottom); - } - wallscan_np2(x1, x2, uwal, dwal, swal, lwal, yrepeat, top, bot, true); - } - else - { - call_wallscan(x1, x2, uwal, dwal, swal, lwal, yrepeat, true); - } -} - // // R_RenderSegLoop // Draws zero, one, or two textures for walls. // Can draw or mark the starting pixel of floor and ceiling textures. // CALLED: CORE LOOPING ROUTINE. // -// [RH] Rewrote this to use Build's wallscan, so it's quite far -// removed from the original Doom routine. -// void R_RenderSegLoop () { @@ -1872,14 +1165,7 @@ void R_RenderSegLoop () { rw_offset = -rw_offset; } - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - wallscan_np2(x1, x2, walltop, wallbottom, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_frontfz1, rw_frontfz2), false); - } - else - { - call_wallscan(x1, x2, walltop, wallbottom, swall, lwall, yscale, false); - } + R_DrawWallSegment(rw_pic, x1, x2, walltop, wallbottom, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_frontfz1, rw_frontfz2), false); } fillshort (ceilingclip+x1, x2-x1, viewheight); fillshort (floorclip+x1, x2-x1, 0xffff); @@ -1915,14 +1201,7 @@ void R_RenderSegLoop () { rw_offset = -rw_offset; } - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - wallscan_np2(x1, x2, walltop, wallupper, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_backcz1, rw_backcz2), false); - } - else - { - call_wallscan(x1, x2, walltop, wallupper, swall, lwall, yscale, false); - } + R_DrawWallSegment(rw_pic, x1, x2, walltop, wallupper, swall, lwall, yscale, MAX(rw_frontcz1, rw_frontcz2), MIN(rw_backcz1, rw_backcz2), false); } memcpy (ceilingclip+x1, wallupper+x1, (x2-x1)*sizeof(short)); } @@ -1961,14 +1240,7 @@ void R_RenderSegLoop () { rw_offset = -rw_offset; } - if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) - { - wallscan_np2(x1, x2, walllower, wallbottom, swall, lwall, yscale, MAX(rw_backfz1, rw_backfz2), MIN(rw_frontfz1, rw_frontfz2), false); - } - else - { - call_wallscan(x1, x2, walllower, wallbottom, swall, lwall, yscale, false); - } + R_DrawWallSegment(rw_pic, x1, x2, walllower, wallbottom, swall, lwall, yscale, MAX(rw_backfz1, rw_backfz2), MIN(rw_frontfz1, rw_frontfz2), false); } memcpy (floorclip+x1, walllower+x1, (x2-x1)*sizeof(short)); } @@ -2079,7 +1351,7 @@ void R_NewWall (bool needlights) // wall but nothing to draw for it. // Recalculate walltop so that the wall is clipped by the back sector's // ceiling instead of the front sector's ceiling. - WallMost (walltop, backsector->ceilingplane, &WallC); + R_CreateWallSegmentYSloped (walltop, backsector->ceilingplane, &WallC); } // Putting sky ceilings on the front and back of a line alters the way unpegged // positioning works. @@ -2654,19 +1926,19 @@ void R_StoreWallRange (int start, int stop) ds_p++; } -int WallMostAny(short *mostbuf, double z1, double z2, const FWallCoords *wallc) +int R_CreateWallSegmentY(short *outbuf, double z1, double z2, const FWallCoords *wallc) { float y1 = (float)(CenterY - z1 * InvZtoScale / wallc->sz1); float y2 = (float)(CenterY - z2 * InvZtoScale / wallc->sz2); if (y1 < 0 && y2 < 0) // entire line is above screen { - memset(&mostbuf[wallc->sx1], 0, (wallc->sx2 - wallc->sx1) * sizeof(mostbuf[0])); + memset(&outbuf[wallc->sx1], 0, (wallc->sx2 - wallc->sx1) * sizeof(outbuf[0])); return 3; } else if (y1 > viewheight && y2 > viewheight) // entire line is below screen { - fillshort(&mostbuf[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); + fillshort(&outbuf[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); return 12; } @@ -2680,7 +1952,7 @@ int WallMostAny(short *mostbuf, double z1, double z2, const FWallCoords *wallc) { float t = (x - wallc->sx1) * rcp_delta; float y = y1 * (1.0f - t) + y2 * t; - mostbuf[x] = (short)xs_RoundToInt(y); + outbuf[x] = (short)xs_RoundToInt(y); } } else @@ -2689,23 +1961,18 @@ int WallMostAny(short *mostbuf, double z1, double z2, const FWallCoords *wallc) { float t = (x - wallc->sx1) * rcp_delta; float y = y1 * (1.0f - t) + y2 * t; - mostbuf[x] = (short)clamp(xs_RoundToInt(y), 0, viewheight); + outbuf[x] = (short)clamp(xs_RoundToInt(y), 0, viewheight); } } return 0; } -int OWallMost(short *mostbuf, double z, const FWallCoords *wallc) -{ - return WallMostAny(mostbuf, z, z, wallc); -} - -int WallMost(short *mostbuf, const secplane_t &plane, const FWallCoords *wallc) +int R_CreateWallSegmentYSloped(short *outbuf, const secplane_t &plane, const FWallCoords *wallc) { if (!plane.isSlope()) { - return OWallMost(mostbuf, plane.Zat0() - ViewPos.Z, wallc); + return R_CreateWallSegmentY(outbuf, plane.Zat0() - ViewPos.Z, wallc); } else { @@ -2770,7 +2037,7 @@ int WallMost(short *mostbuf, const secplane_t &plane, const FWallCoords *wallc) } } - return WallMostAny(mostbuf, z1, z2, wallc); + return R_CreateWallSegmentY(outbuf, z1, z2, wallc); } } diff --git a/src/r_segs.h b/src/r_segs.h index 7d34a78f4..6dc08d52c 100644 --- a/src/r_segs.h +++ b/src/r_segs.h @@ -34,8 +34,13 @@ extern short *openings; extern ptrdiff_t lastopening; extern size_t maxopenings; -int OWallMost (short *mostbuf, double z, const FWallCoords *wallc); -int WallMost (short *mostbuf, const secplane_t &plane, const FWallCoords *wallc); +int R_CreateWallSegmentY (short *outbuf, double z1, double z2, const FWallCoords *wallc); +int R_CreateWallSegmentYSloped (short *outbuf, const secplane_t &plane, const FWallCoords *wallc); +inline int R_CreateWallSegmentY(short *outbuf, double z, const FWallCoords *wallc) +{ + return R_CreateWallSegmentY(outbuf, z, z, wallc); +} + void PrepWall (float *swall, fixed_t *lwall, double walxrepeat, int x1, int x2); void PrepLWall (fixed_t *lwall, double walxrepeat, int x1, int x2); diff --git a/src/r_walldraw.cpp b/src/r_walldraw.cpp new file mode 100644 index 000000000..0f2802b01 --- /dev/null +++ b/src/r_walldraw.cpp @@ -0,0 +1,1137 @@ +/* +** Wall drawing stuff free of Build pollution +** Copyright (c) 2016 Magnus Norddahl +** +** This software is provided 'as-is', without any express or implied +** warranty. In no event will the authors be held liable for any damages +** arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, +** including commercial applications, and to alter it and redistribute it +** freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not +** claim that you wrote the original software. If you use this software +** in a product, an acknowledgment in the product documentation would be +** appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be +** misrepresented as being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include + +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" + +#include "r_local.h" +#include "r_sky.h" +#include "v_video.h" + +#include "m_swap.h" +#include "a_sharedglobal.h" +#include "d_net.h" +#include "g_level.h" +#include "r_draw.h" +#include "r_bsp.h" +#include "r_plane.h" +#include "r_segs.h" +#include "r_3dfloors.h" +#include "v_palette.h" +#include "r_data/colormaps.h" + +namespace swrenderer +{ + using namespace drawerargs; + + extern FTexture *rw_pic; + extern int wallshade; + +#if 0 // ZDoom version + +struct WallSampler +{ + WallSampler() { } + WallSampler(int y1, float swal, double yrepeat, fixed_t xoffset, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)); + + uint32_t uv_pos; + uint32_t uv_step; + uint32_t uv_max; + + const BYTE *source; + uint32_t height; +}; + +WallSampler::WallSampler(int y1, float swal, double yrepeat, fixed_t xoffset, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)) +{ + height = texture->GetHeight(); + + int uv_fracbits = 32 - texture->HeightBits; + if (uv_fracbits != 32) + { + uv_max = height << uv_fracbits; + + // Find start uv in [0-base_height[ range. + // Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range. + double uv_stepd = swal * yrepeat; + double v = (dc_texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; + v = v - floor(v); + v *= height; + v *= (1 << uv_fracbits); + + uv_pos = (uint32_t)v; + uv_step = xs_ToFixed(uv_fracbits, uv_stepd); + if (uv_step == 0) // To prevent divide by zero elsewhere + uv_step = 1; + } + else + { // Hack for one pixel tall textures + uv_pos = 0; + uv_step = 0; + uv_max = 1; + } + + source = getcol(texture, xoffset >> FRACBITS); +} + +// Draw a column with support for non-power-of-two ranges +static void Draw1Column(int x, int y1, int y2, WallSampler &sampler, DWORD(*draw1column)()) +{ + if (sampler.uv_max == 0 || sampler.uv_step == 0) // power of two + { + int count = y2 - y1; + + dc_source = sampler.source; + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + dc_iscale = sampler.uv_step; + dc_texturefrac = sampler.uv_pos; + draw1column(); + + uint64_t step64 = sampler.uv_step; + uint64_t pos64 = sampler.uv_pos; + sampler.uv_pos = (uint32_t)(pos64 + step64 * count); + } + else + { + uint32_t uv_pos = sampler.uv_pos; + + uint32_t left = y2 - y1; + while (left > 0) + { + uint32_t available = sampler.uv_max - uv_pos; + uint32_t next_uv_wrap = available / sampler.uv_step; + if (available % sampler.uv_step != 0) + next_uv_wrap++; + uint32_t count = MIN(left, next_uv_wrap); + + dc_source = sampler.source; + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + dc_iscale = sampler.uv_step; + dc_texturefrac = uv_pos; + draw1column(); + + left -= count; + uv_pos += sampler.uv_step * count; + if (uv_pos >= sampler.uv_max) + uv_pos -= sampler.uv_max; + } + + sampler.uv_pos = uv_pos; + } +} + +// Draw four columns with support for non-power-of-two ranges +static void Draw4Columns(int x, int y1, int y2, WallSampler *sampler, void(*draw4columns)()) +{ + if (sampler[0].uv_max == 0 || sampler[0].uv_step == 0) // power of two, no wrap handling needed + { + int count = y2 - y1; + for (int i = 0; i < 4; i++) + { + bufplce[i] = sampler[i].source; + vplce[i] = sampler[i].uv_pos; + vince[i] = sampler[i].uv_step; + + uint64_t step64 = sampler[i].uv_step; + uint64_t pos64 = sampler[i].uv_pos; + sampler[i].uv_pos = (uint32_t)(pos64 + step64 * count); + } + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + draw4columns(); + } + else + { + dc_dest = (ylookup[y1] + x) + dc_destorg; + for (int i = 0; i < 4; i++) + { + bufplce[i] = sampler[i].source; + } + + uint32_t left = y2 - y1; + while (left > 0) + { + // Find which column wraps first + uint32_t count = left; + for (int i = 0; i < 4; i++) + { + uint32_t available = sampler[i].uv_max - sampler[i].uv_pos; + uint32_t next_uv_wrap = available / sampler[i].uv_step; + if (available % sampler[i].uv_step != 0) + next_uv_wrap++; + count = MIN(next_uv_wrap, count); + } + + // Draw until that column wraps + for (int i = 0; i < 4; i++) + { + vplce[i] = sampler[i].uv_pos; + vince[i] = sampler[i].uv_step; + } + dc_count = count; + draw4columns(); + + // Wrap the uv position + for (int i = 0; i < 4; i++) + { + sampler[i].uv_pos += sampler[i].uv_step * count; + if (sampler[i].uv_pos >= sampler[i].uv_max) + sampler[i].uv_pos -= sampler[i].uv_max; + } + + left -= count; + } + } +} + +typedef DWORD(*Draw1ColumnFuncPtr)(); +typedef void(*Draw4ColumnsFuncPtr)(); + +static void ProcessWallWorker( + int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, + const BYTE *(*getcol)(FTexture *tex, int x), + void(setupProcessNormalWall(int bits, Draw1ColumnFuncPtr &draw1, Draw4ColumnsFuncPtr &draw2))) +{ + if (rw_pic->UseType == FTexture::TEX_Null) + return; + + fixed_t xoffset = rw_offset; + + int fracbits = 32 - rw_pic->HeightBits; + if (fracbits == 32) + { // Hack for one pixel tall textures + fracbits = 0; + yrepeat = 0; + dc_texturemid = 0; + } + + DWORD(*draw1column)(); + void(*draw4columns)(); + setupProcessNormalWall(fracbits, draw1column, draw4columns); + + bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); + if (fixed) + { + palookupoffse[0] = dc_colormap; + palookupoffse[1] = dc_colormap; + palookupoffse[2] = dc_colormap; + palookupoffse[3] = dc_colormap; + } + + if (fixedcolormap) + dc_colormap = fixedcolormap; + else + dc_colormap = basecolormap->Maps; + + float light = rw_light; + + // Calculate where 4 column alignment begins and ends: + int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); + int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); + + // First unaligned columns: + for (int x = x1; x < aligned_x1; x++, light += rw_lightstep) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + if (!fixed) + dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); + + WallSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, rw_pic, getcol); + Draw1Column(x, y1, y2, sampler, draw1column); + } + + // The aligned columns + for (int x = aligned_x1; x < aligned_x2; x += 4) + { + // Find y1, y2, light and uv values for four columns: + int y1[4] = { uwal[x], uwal[x + 1], uwal[x + 2], uwal[x + 3] }; + int y2[4] = { dwal[x], dwal[x + 1], dwal[x + 2], dwal[x + 3] }; + + float lights[4]; + for (int i = 0; i < 4; i++) + { + lights[i] = light; + light += rw_lightstep; + } + + WallSampler sampler[4]; + for (int i = 0; i < 4; i++) + sampler[i] = WallSampler(y1[i], swal[x + i], yrepeat, lwal[x + i] + xoffset, rw_pic, getcol); + + // Figure out where we vertically can start and stop drawing 4 columns in one go + int middle_y1 = y1[0]; + int middle_y2 = y2[0]; + for (int i = 1; i < 4; i++) + { + middle_y1 = MAX(y1[i], middle_y1); + middle_y2 = MIN(y2[i], middle_y2); + } + + // If we got an empty column in our set we cannot draw 4 columns in one go: + bool empty_column_in_set = false; + for (int i = 0; i < 4; i++) + { + if (y2[i] <= y1[i]) + empty_column_in_set = true; + } + + if (empty_column_in_set || middle_y2 <= middle_y1) + { + for (int i = 0; i < 4; i++) + { + if (y2[i] <= y1[i]) + continue; + + if (!fixed) + dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); + Draw1Column(x + i, y1[i], y2[i], sampler[i], draw1column); + } + continue; + } + + // Draw the first rows where not all 4 columns are active + for (int i = 0; i < 4; i++) + { + if (!fixed) + dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); + + if (y1[i] < middle_y1) + Draw1Column(x + i, y1[i], middle_y1, sampler[i], draw1column); + } + + // Draw the area where all 4 columns are active + if (!fixed) + { + for (int i = 0; i < 4; i++) + { + palookupoffse[i] = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); + } + } + Draw4Columns(x, middle_y1, middle_y2, sampler, draw4columns); + + // Draw the last rows where not all 4 columns are active + for (int i = 0; i < 4; i++) + { + if (!fixed) + dc_colormap = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); + + if (middle_y2 < y2[i]) + Draw1Column(x + i, middle_y2, y2[i], sampler[i], draw1column); + } + } + + // The last unaligned columns: + for (int x = aligned_x2; x < x2; x++, light += rw_lightstep) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + if (!fixed) + dc_colormap = basecolormap->Maps + (GETPALOOKUP(light, wallshade) << COLORMAPSHIFT); + + WallSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, rw_pic, getcol); + Draw1Column(x, y1, y2, sampler, draw1column); + } + + NetUpdate(); +} + +static void ProcessNormalWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) +{ + ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setupvline(bits); + line1 = dovline1; + line4 = dovline4; + }); +} + +static void ProcessMaskedWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) +{ + if (!rw_pic->bMasked) // Textures that aren't masked can use the faster ProcessNormalWall. + { + ProcessNormalWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); + } + else + { + ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setupmvline(bits); + line1 = domvline1; + line4 = domvline4; + }); + } +} + +static void ProcessTranslucentWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) +{ + static fixed_t(*tmvline1)(); + static void(*tmvline4)(); + if (!R_GetTransMaskDrawers(&tmvline1, &tmvline4)) + { + // The current translucency is unsupported, so draw with regular ProcessMaskedWall instead. + ProcessMaskedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); + } + else + { + ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setuptmvline(bits); + line1 = reinterpret_cast(tmvline1); + line4 = tmvline4; + }); + } +} + +#else // QZDoom version + +struct WallSampler +{ + WallSampler() { } + WallSampler(int y1, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)); + + uint32_t uv_pos; + uint32_t uv_step; + uint32_t uv_max; + + const BYTE *source; + const BYTE *source2; + uint32_t texturefracx; + uint32_t height; +}; + +WallSampler::WallSampler(int y1, float swal, double yrepeat, fixed_t xoffset, double xmagnitude, FTexture *texture, const BYTE*(*getcol)(FTexture *texture, int x)) +{ + xoffset += FLOAT2FIXED(xmagnitude * 0.5); + + if (!r_swtruecolor) + { + height = texture->GetHeight(); + + int uv_fracbits = 32 - texture->HeightBits; + if (uv_fracbits != 32) + { + uv_max = height << uv_fracbits; + + // Find start uv in [0-base_height[ range. + // Not using xs_ToFixed because it rounds the result and we need something that always rounds down to stay within the range. + double uv_stepd = swal * yrepeat; + double v = (dc_texturemid + uv_stepd * (y1 - CenterY + 0.5)) / height; + v = v - floor(v); + v *= height; + v *= (1 << uv_fracbits); + + uv_pos = (uint32_t)v; + uv_step = xs_ToFixed(uv_fracbits, uv_stepd); + if (uv_step == 0) // To prevent divide by zero elsewhere + uv_step = 1; + } + else + { // Hack for one pixel tall textures + uv_pos = 0; + uv_step = 0; + uv_max = 1; + } + + source = getcol(texture, xoffset >> FRACBITS); + source2 = nullptr; + texturefracx = 0; + } + else + { + // Normalize to 0-1 range: + double uv_stepd = swal * yrepeat; + double v = (dc_texturemid + uv_stepd * (y1 - CenterY + 0.5)) / texture->GetHeight(); + v = v - floor(v); + double v_step = uv_stepd / texture->GetHeight(); + + if (isnan(v) || isnan(v_step)) // this should never happen, but it apparently does.. + { + uv_stepd = 0.0; + v = 0.0; + v_step = 0.0; + } + + // Convert to uint32: + uv_pos = (uint32_t)(v * 0x100000000LL); + uv_step = (uint32_t)(v_step * 0x100000000LL); + uv_max = 0; + + // Texture mipmap and filter selection: + if (getcol != R_GetColumn) + { + source = getcol(texture, xoffset >> FRACBITS); + source2 = nullptr; + height = texture->GetHeight(); + texturefracx = 0; + } + else + { + double ymagnitude = fabs(uv_stepd); + double magnitude = MAX(ymagnitude, xmagnitude); + double min_lod = -1000.0; + double lod = MAX(log2(magnitude) + r_lod_bias, min_lod); + bool magnifying = lod < 0.0f; + + int mipmap_offset = 0; + int mip_width = texture->GetWidth(); + int mip_height = texture->GetHeight(); + if (r_mipmap && texture->Mipmapped() && mip_width > 1 && mip_height > 1) + { + uint32_t xpos = (uint32_t)((((uint64_t)xoffset) << FRACBITS) / mip_width); + + int level = (int)lod; + while (level > 0 && mip_width > 1 && mip_height > 1) + { + mipmap_offset += mip_width * mip_height; + level--; + mip_width = MAX(mip_width >> 1, 1); + mip_height = MAX(mip_height >> 1, 1); + } + xoffset = (xpos >> FRACBITS) * mip_width; + } + + const uint32_t *pixels = texture->GetPixelsBgra() + mipmap_offset; + + bool filter_nearest = (magnifying && !r_magfilter) || (!magnifying && !r_minfilter); + if (filter_nearest) + { + int tx = (xoffset >> FRACBITS) % mip_width; + if (tx < 0) + tx += mip_width; + source = (BYTE*)(pixels + tx * mip_height); + source2 = nullptr; + height = mip_height; + texturefracx = 0; + } + else + { + xoffset -= FRACUNIT / 2; + int tx0 = (xoffset >> FRACBITS) % mip_width; + if (tx0 < 0) + tx0 += mip_width; + int tx1 = (tx0 + 1) % mip_width; + source = (BYTE*)(pixels + tx0 * mip_height); + source2 = (BYTE*)(pixels + tx1 * mip_height); + height = mip_height; + texturefracx = (xoffset >> (FRACBITS - 4)) & 15; + } + } + } +} + +// Draw a column with support for non-power-of-two ranges +void Draw1Column(int x, int y1, int y2, WallSampler &sampler, DWORD(*draw1column)()) +{ + if (r_swtruecolor) + { + int count = y2 - y1; + + dc_source = sampler.source; + dc_source2 = sampler.source2; + dc_texturefracx = sampler.texturefracx; + dc_dest = (ylookup[y1] + x) * 4 + dc_destorg; + dc_count = count; + dc_iscale = sampler.uv_step; + dc_texturefrac = sampler.uv_pos; + dc_textureheight = sampler.height; + draw1column(); + + uint64_t step64 = sampler.uv_step; + uint64_t pos64 = sampler.uv_pos; + sampler.uv_pos = (uint32_t)(pos64 + step64 * count); + } + else + { + if (sampler.uv_max == 0 || sampler.uv_step == 0) // power of two + { + int count = y2 - y1; + + dc_source = sampler.source; + dc_source2 = sampler.source2; + dc_texturefracx = sampler.texturefracx; + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + dc_iscale = sampler.uv_step; + dc_texturefrac = sampler.uv_pos; + draw1column(); + + uint64_t step64 = sampler.uv_step; + uint64_t pos64 = sampler.uv_pos; + sampler.uv_pos = (uint32_t)(pos64 + step64 * count); + } + else + { + uint32_t uv_pos = sampler.uv_pos; + + uint32_t left = y2 - y1; + while (left > 0) + { + uint32_t available = sampler.uv_max - uv_pos; + uint32_t next_uv_wrap = available / sampler.uv_step; + if (available % sampler.uv_step != 0) + next_uv_wrap++; + uint32_t count = MIN(left, next_uv_wrap); + + dc_source = sampler.source; + dc_source2 = sampler.source2; + dc_texturefracx = sampler.texturefracx; + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + dc_iscale = sampler.uv_step; + dc_texturefrac = uv_pos; + draw1column(); + + left -= count; + uv_pos += sampler.uv_step * count; + if (uv_pos >= sampler.uv_max) + uv_pos -= sampler.uv_max; + } + + sampler.uv_pos = uv_pos; + } + } +} + +// Draw four columns with support for non-power-of-two ranges +void Draw4Columns(int x, int y1, int y2, WallSampler *sampler, void(*draw4columns)()) +{ + if (r_swtruecolor) + { + int count = y2 - y1; + for (int i = 0; i < 4; i++) + { + bufplce[i] = sampler[i].source; + bufplce2[i] = sampler[i].source2; + buftexturefracx[i] = sampler[i].texturefracx; + bufheight[i] = sampler[i].height; + vplce[i] = sampler[i].uv_pos; + vince[i] = sampler[i].uv_step; + + uint64_t step64 = sampler[i].uv_step; + uint64_t pos64 = sampler[i].uv_pos; + sampler[i].uv_pos = (uint32_t)(pos64 + step64 * count); + } + dc_dest = (ylookup[y1] + x) * 4 + dc_destorg; + dc_count = count; + draw4columns(); + } + else + { + if (sampler[0].uv_max == 0 || sampler[0].uv_step == 0) // power of two, no wrap handling needed + { + int count = y2 - y1; + for (int i = 0; i < 4; i++) + { + bufplce[i] = sampler[i].source; + bufplce2[i] = sampler[i].source2; + buftexturefracx[i] = sampler[i].texturefracx; + vplce[i] = sampler[i].uv_pos; + vince[i] = sampler[i].uv_step; + + uint64_t step64 = sampler[i].uv_step; + uint64_t pos64 = sampler[i].uv_pos; + sampler[i].uv_pos = (uint32_t)(pos64 + step64 * count); + } + dc_dest = (ylookup[y1] + x) + dc_destorg; + dc_count = count; + draw4columns(); + } + else + { + dc_dest = (ylookup[y1] + x) + dc_destorg; + for (int i = 0; i < 4; i++) + { + bufplce[i] = sampler[i].source; + bufplce2[i] = sampler[i].source2; + buftexturefracx[i] = sampler[i].texturefracx; + } + + uint32_t left = y2 - y1; + while (left > 0) + { + // Find which column wraps first + uint32_t count = left; + for (int i = 0; i < 4; i++) + { + uint32_t available = sampler[i].uv_max - sampler[i].uv_pos; + uint32_t next_uv_wrap = available / sampler[i].uv_step; + if (available % sampler[i].uv_step != 0) + next_uv_wrap++; + count = MIN(next_uv_wrap, count); + } + + // Draw until that column wraps + for (int i = 0; i < 4; i++) + { + vplce[i] = sampler[i].uv_pos; + vince[i] = sampler[i].uv_step; + } + dc_count = count; + draw4columns(); + + // Wrap the uv position + for (int i = 0; i < 4; i++) + { + sampler[i].uv_pos += sampler[i].uv_step * count; + if (sampler[i].uv_pos >= sampler[i].uv_max) + sampler[i].uv_pos -= sampler[i].uv_max; + } + + left -= count; + } + } + } +} + +typedef DWORD(*Draw1ColumnFuncPtr)(); +typedef void(*Draw4ColumnsFuncPtr)(); + +void ProcessWallWorker( + int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, + const BYTE *(*getcol)(FTexture *tex, int x), + void(setupProcessNormalWall(int bits, Draw1ColumnFuncPtr &draw1, Draw4ColumnsFuncPtr &draw2))) +{ + if (rw_pic->UseType == FTexture::TEX_Null) + return; + + fixed_t xoffset = rw_offset; + + rw_pic->GetHeight(); // To ensure that rw_pic->HeightBits has been set + int fracbits = 32 - rw_pic->HeightBits; + if (fracbits == 32) + { // Hack for one pixel tall textures + fracbits = 0; + yrepeat = 0; + dc_texturemid = 0; + } + + DWORD(*draw1column)(); + void(*draw4columns)(); + setupProcessNormalWall(r_swtruecolor ? FRACBITS : fracbits, draw1column, draw4columns); + + bool fixed = (fixedcolormap != NULL || fixedlightlev >= 0); + if (fixed) + { + palookupoffse[0] = dc_colormap; + palookupoffse[1] = dc_colormap; + palookupoffse[2] = dc_colormap; + palookupoffse[3] = dc_colormap; + palookuplight[0] = 0; + palookuplight[1] = 0; + palookuplight[2] = 0; + palookuplight[3] = 0; + } + + if (fixedcolormap) + R_SetColorMapLight(fixedcolormap, 0, 0); + else + R_SetColorMapLight(basecolormap, 0, 0); + + float light = rw_light; + + // Calculate where 4 column alignment begins and ends: + int aligned_x1 = clamp((x1 + 3) / 4 * 4, x1, x2); + int aligned_x2 = clamp(x2 / 4 * 4, x1, x2); + + double xmagnitude = 1.0; + + // First unaligned columns: + for (int x = x1; x < aligned_x1; x++, light += rw_lightstep) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + if (!fixed) + R_SetColorMapLight(basecolormap, light, wallshade); + + if (x + 1 < x2) xmagnitude = fabs(FIXED2DBL(lwal[x + 1]) - FIXED2DBL(lwal[x])); + + WallSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, xmagnitude, rw_pic, getcol); + Draw1Column(x, y1, y2, sampler, draw1column); + } + + // The aligned columns + for (int x = aligned_x1; x < aligned_x2; x += 4) + { + // Find y1, y2, light and uv values for four columns: + int y1[4] = { uwal[x], uwal[x + 1], uwal[x + 2], uwal[x + 3] }; + int y2[4] = { dwal[x], dwal[x + 1], dwal[x + 2], dwal[x + 3] }; + + float lights[4]; + for (int i = 0; i < 4; i++) + { + lights[i] = light; + light += rw_lightstep; + } + + WallSampler sampler[4]; + for (int i = 0; i < 4; i++) + { + if (x + i + 1 < x2) xmagnitude = fabs(FIXED2DBL(lwal[x + i + 1]) - FIXED2DBL(lwal[x + i])); + sampler[i] = WallSampler(y1[i], swal[x + i], yrepeat, lwal[x + i] + xoffset, xmagnitude, rw_pic, getcol); + } + + // Figure out where we vertically can start and stop drawing 4 columns in one go + int middle_y1 = y1[0]; + int middle_y2 = y2[0]; + for (int i = 1; i < 4; i++) + { + middle_y1 = MAX(y1[i], middle_y1); + middle_y2 = MIN(y2[i], middle_y2); + } + + // If we got an empty column in our set we cannot draw 4 columns in one go: + bool empty_column_in_set = false; + int bilinear_count = 0; + for (int i = 0; i < 4; i++) + { + if (y2[i] <= y1[i]) + empty_column_in_set = true; + if (sampler[i].source2) + bilinear_count++; + } + + if (empty_column_in_set || middle_y2 <= middle_y1 || (bilinear_count > 0 && bilinear_count < 4)) + { + for (int i = 0; i < 4; i++) + { + if (y2[i] <= y1[i]) + continue; + + if (!fixed) + R_SetColorMapLight(basecolormap, lights[i], wallshade); + Draw1Column(x + i, y1[i], y2[i], sampler[i], draw1column); + } + continue; + } + + // Draw the first rows where not all 4 columns are active + for (int i = 0; i < 4; i++) + { + if (!fixed) + R_SetColorMapLight(basecolormap, lights[i], wallshade); + + if (y1[i] < middle_y1) + Draw1Column(x + i, y1[i], middle_y1, sampler[i], draw1column); + } + + // Draw the area where all 4 columns are active + if (!fixed) + { + for (int i = 0; i < 4; i++) + { + if (r_swtruecolor) + { + palookupoffse[i] = basecolormap->Maps; + palookuplight[i] = LIGHTSCALE(lights[i], wallshade); + } + else + { + palookupoffse[i] = basecolormap->Maps + (GETPALOOKUP(lights[i], wallshade) << COLORMAPSHIFT); + palookuplight[i] = 0; + } + } + } + Draw4Columns(x, middle_y1, middle_y2, sampler, draw4columns); + + // Draw the last rows where not all 4 columns are active + for (int i = 0; i < 4; i++) + { + if (!fixed) + R_SetColorMapLight(basecolormap, lights[i], wallshade); + + if (middle_y2 < y2[i]) + Draw1Column(x + i, middle_y2, y2[i], sampler[i], draw1column); + } + } + + // The last unaligned columns: + for (int x = aligned_x2; x < x2; x++, light += rw_lightstep) + { + int y1 = uwal[x]; + int y2 = dwal[x]; + if (y2 <= y1) + continue; + + if (!fixed) + R_SetColorMapLight(basecolormap, light, wallshade); + + if (x + 1 < x2) xmagnitude = fabs(FIXED2DBL(lwal[x + 1]) - FIXED2DBL(lwal[x])); + + WallSampler sampler(y1, swal[x], yrepeat, lwal[x] + xoffset, xmagnitude, rw_pic, getcol); + Draw1Column(x, y1, y2, sampler, draw1column); + } + + NetUpdate(); +} + +static void ProcessNormalWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) +{ + ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setupvline(bits); + line1 = dovline1; + line4 = dovline4; + }); +} + +static void ProcessMaskedWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) +{ + if (!rw_pic->bMasked) // Textures that aren't masked can use the faster ProcessNormalWall. + { + ProcessNormalWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); + } + else + { + ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setupmvline(bits); + line1 = domvline1; + line4 = domvline4; + }); + } +} + +static void ProcessTranslucentWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x) = R_GetColumn) +{ + static fixed_t(*tmvline1)(); + static void(*tmvline4)(); + if (!R_GetTransMaskDrawers(&tmvline1, &tmvline4)) + { + // The current translucency is unsupported, so draw with regular ProcessMaskedWall instead. + ProcessMaskedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol); + } + else + { + ProcessWallWorker(x1, x2, uwal, dwal, swal, lwal, yrepeat, getcol, [](int bits, Draw1ColumnFuncPtr &line1, Draw4ColumnsFuncPtr &line4) + { + setuptmvline(bits); + line1 = reinterpret_cast(tmvline1); + line4 = tmvline4; + }); + } +} + +#endif + +static void ProcessStripedWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) +{ + FDynamicColormap *startcolormap = basecolormap; + int startshade = wallshade; + bool fogginess = foggy; + + short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; + short *up, *down; + + up = uwal; + down = most1; + + assert(WallC.sx1 <= x1); + assert(WallC.sx2 >= x2); + + // kg3D - fake floors instead of zdoom light list + for (unsigned int i = 0; i < frontsector->e->XFloor.lightlist.Size(); i++) + { + int j = R_CreateWallSegmentYSloped (most3, frontsector->e->XFloor.lightlist[i].plane, &WallC); + if (j != 3) + { + for (int j = x1; j < x2; ++j) + { + down[j] = clamp (most3[j], up[j], dwal[j]); + } + ProcessNormalWall (x1, x2, up, down, swal, lwal, yrepeat); + up = down; + down = (down == most1) ? most2 : most1; + } + + lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; + basecolormap = lit->extra_colormap; + wallshade = LIGHT2SHADE(curline->sidedef->GetLightLevel(fogginess, + *lit->p_lightlevel, lit->lightsource != NULL) + r_actualextralight); + } + + ProcessNormalWall (x1, x2, up, dwal, swal, lwal, yrepeat); + basecolormap = startcolormap; + wallshade = startshade; +} + +static void ProcessWall(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, bool mask) +{ + if (mask) + { + if (colfunc == basecolfunc) + { + ProcessMaskedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); + } + else + { + ProcessTranslucentWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); + } + } + else + { + if (fixedcolormap != NULL || fixedlightlev >= 0 || !(frontsector->e && frontsector->e->XFloor.lightlist.Size())) + { + ProcessNormalWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); + } + else + { + ProcessStripedWall(x1, x2, uwal, dwal, swal, lwal, yrepeat); + } + } +} + +//============================================================================= +// +// ProcessWallNP2 +// +// This is a wrapper around ProcessWall that helps it tile textures whose heights +// are not powers of 2. It divides the wall into texture-sized strips and calls +// ProcessNormalWall for each of those. Since only one repetition of the texture fits +// in each strip, ProcessWall will not tile. +// +//============================================================================= + +static void ProcessWallNP2(int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, double top, double bot, bool mask) +{ + short most1[MAXWIDTH], most2[MAXWIDTH], most3[MAXWIDTH]; + short *up, *down; + double texheight = rw_pic->GetHeight(); + double partition; + double scaledtexheight = texheight / yrepeat; + + if (yrepeat >= 0) + { // normal orientation: draw strips from top to bottom + partition = top - fmod(top - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); + if (partition == top) + { + partition -= scaledtexheight; + } + up = uwal; + down = most1; + dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; + while (partition > bot) + { + int j = R_CreateWallSegmentY(most3, partition - ViewPos.Z, &WallC); + if (j != 3) + { + for (int j = x1; j < x2; ++j) + { + down[j] = clamp(most3[j], up[j], dwal[j]); + } + ProcessWall(x1, x2, up, down, swal, lwal, yrepeat, mask); + up = down; + down = (down == most1) ? most2 : most1; + } + partition -= scaledtexheight; + dc_texturemid -= texheight; + } + ProcessWall(x1, x2, up, dwal, swal, lwal, yrepeat, mask); + } + else + { // upside down: draw strips from bottom to top + partition = bot - fmod(bot - dc_texturemid / yrepeat - ViewPos.Z, scaledtexheight); + up = most1; + down = dwal; + dc_texturemid = (partition - ViewPos.Z) * yrepeat + texheight; + while (partition < top) + { + int j = R_CreateWallSegmentY(most3, partition - ViewPos.Z, &WallC); + if (j != 12) + { + for (int j = x1; j < x2; ++j) + { + up[j] = clamp(most3[j], uwal[j], down[j]); + } + ProcessWall(x1, x2, up, down, swal, lwal, yrepeat, mask); + down = up; + up = (up == most1) ? most2 : most1; + } + partition -= scaledtexheight; + dc_texturemid -= texheight; + } + ProcessWall(x1, x2, uwal, down, swal, lwal, yrepeat, mask); + } +} + +void R_DrawDrawSeg(drawseg_t *ds, int x1, int x2, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat) +{ + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) + { + double frontcz1 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v1); + double frontfz1 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v1); + double frontcz2 = ds->curline->frontsector->ceilingplane.ZatPoint(ds->curline->v2); + double frontfz2 = ds->curline->frontsector->floorplane.ZatPoint(ds->curline->v2); + double top = MAX(frontcz1, frontcz2); + double bot = MIN(frontfz1, frontfz2); + if (fake3D & FAKE3D_CLIPTOP) + { + top = MIN(top, sclipTop); + } + if (fake3D & FAKE3D_CLIPBOTTOM) + { + bot = MAX(bot, sclipBottom); + } + ProcessWallNP2(x1, x2, uwal, dwal, swal, lwal, yrepeat, top, bot, true); + } + else + { + ProcessWall(x1, x2, uwal, dwal, swal, lwal, yrepeat, true); + } +} + + +void R_DrawWallSegment(FTexture *rw_pic, int x1, int x2, short *walltop, short *wallbottom, float *swall, fixed_t *lwall, double yscale, double top, double bottom, bool mask) +{ + if (rw_pic->GetHeight() != 1 << rw_pic->HeightBits) + { + ProcessWallNP2(x1, x2, walltop, wallbottom, swall, lwall, yscale, top, bottom, false); + } + else + { + ProcessWall(x1, x2, walltop, wallbottom, swall, lwall, yscale, false); + } +} + +void R_DrawSkySegment(visplane_t *pl, short *uwal, short *dwal, float *swal, fixed_t *lwal, double yrepeat, const BYTE *(*getcol)(FTexture *tex, int x)) +{ + ProcessNormalWall(pl->left, pl->right, uwal, dwal, swal, lwal, yrepeat, getcol); +} + + + +} \ No newline at end of file diff --git a/src/scripting/zscript/zcc_compile.cpp b/src/scripting/zscript/zcc_compile.cpp index 856802a97..2b4974bda 100644 --- a/src/scripting/zscript/zcc_compile.cpp +++ b/src/scripting/zscript/zcc_compile.cpp @@ -1583,12 +1583,19 @@ PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt) PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym) { - // The duplicate Simplify call is necessary because if the head node gets replaced there is no way to detect the end of the list otherwise. - arraysize = Simplify(arraysize, sym, true); - ZCC_Expression *val; + TArray indices; + + // Simplify is too broken to resolve this inside the ring list so unravel the list into an array before starting to simplify its components. + auto node = arraysize; do { - val = Simplify(arraysize, sym, true); + indices.Push(node); + node = static_cast(node->SiblingNext); + } while (node != arraysize); + + for (auto node : indices) + { + auto val = Simplify(node, sym, true); if (val->Operation != PEX_ConstValue || !val->Type->IsA(RUNTIME_CLASS(PInt))) { Error(arraysize, "Array index must be an integer constant"); @@ -1601,8 +1608,7 @@ PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, return TypeError; } baseType = NewArray(baseType, size); - val = static_cast(val->SiblingNext); - } while (val != arraysize); + } return baseType; }