//----------------------------------------------------------------------------- // // Copyright 1993-1996 id Software // Copyright 1999-2016 Randy Heit // Copyright 2016 Magnus Norddahl // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see http://www.gnu.org/licenses/ // //----------------------------------------------------------------------------- // #include #include "templates.h" #include "doomdef.h" #include "m_bbox.h" #include "i_system.h" #include "p_lnspec.h" #include "p_setup.h" #include "a_sharedglobal.h" #include "g_level.h" #include "g_levellocals.h" #include "p_effect.h" #include "doomstat.h" #include "r_state.h" #include "v_palette.h" #include "r_sky.h" #include "po_man.h" #include "r_data/colormaps.h" #include "d_net.h" #include "swrenderer/r_memory.h" #include "swrenderer/r_renderthread.h" #include "swrenderer/drawers/r_draw.h" #include "swrenderer/scene/r_3dfloors.h" #include "swrenderer/scene/r_opaque_pass.h" #include "swrenderer/scene/r_portal.h" #include "swrenderer/line/r_wallsetup.h" #include "swrenderer/line/r_walldraw.h" #include "swrenderer/line/r_fogboundary.h" #include "swrenderer/line/r_renderdrawsegment.h" #include "swrenderer/segments/r_drawsegment.h" #include "swrenderer/things/r_visiblesprite.h" #include "swrenderer/scene/r_light.h" #include "swrenderer/viewport/r_viewport.h" #include "swrenderer/viewport/r_spritedrawer.h" EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); namespace swrenderer { RenderDrawSegment::RenderDrawSegment(RenderThread *thread) { Thread = thread; } void RenderDrawSegment::Render(DrawSegment *ds, int x1, int x2) { auto viewport = Thread->Viewport.get(); RenderFogBoundary renderfog; float *MaskedSWall = nullptr, MaskedScaleY = 0, rw_scalestep = 0; fixed_t *maskedtexturecol = nullptr; FTexture *tex; int i; sector_t tempsec; // killough 4/13/98 double texheight, texheightscale; bool notrelevant = false; double rowoffset; bool wrap = false; const sector_t *sec; bool sprflipvert = false; curline = ds->curline; float alpha = (float)MIN(curline->linedef->alpha, 1.); bool additive = (curline->linedef->flags & ML_ADDTRANS) != 0; WallDrawerArgs walldrawerargs; walldrawerargs.SetStyle(true, additive, FLOAT2FIXED(alpha)); SpriteDrawerArgs columndrawerargs; FDynamicColormap *patchstylecolormap = nullptr; bool visible = columndrawerargs.SetStyle(viewport, LegacyRenderStyles[additive ? STYLE_Add : STYLE_Translucent], alpha, 0, 0, patchstylecolormap); if (!visible && !ds->bFogBoundary && !ds->bFakeBoundary) { return; } if (Thread->MainThread) NetUpdate(); frontsector = curline->frontsector; backsector = curline->backsector; tex = TexMan(curline->sidedef->GetTexture(side_t::mid), true); if (i_compatflags & COMPATF_MASKEDMIDTEX) { tex = tex->GetRawTexture(); } // killough 4/13/98: get correct lightlevel for 2s normal textures sec = Thread->OpaquePass->FakeFlat(frontsector, &tempsec, nullptr, nullptr, nullptr, 0, 0, 0, 0); FDynamicColormap *basecolormap = GetColorTable(sec->Colormap, sec->SpecialColors[sector_t::walltop]); // [RH] Set basecolormap int wallshade = ds->shade; rw_lightstep = ds->lightstep; rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; Clip3DFloors *clip3d = Thread->Clip3D.get(); CameraLight *cameraLight = CameraLight::Instance(); if (cameraLight->FixedLightLevel() < 0) { if (!(clip3d->fake3D & FAKE3D_CLIPTOP)) { clip3d->sclipTop = sec->ceilingplane.ZatPoint(Thread->Viewport->viewpoint.Pos); } for (i = frontsector->e->XFloor.lightlist.Size() - 1; i >= 0; i--) { if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[i].plane.Zat0()) { lightlist_t *lit = &frontsector->e->XFloor.lightlist[i]; basecolormap = GetColorTable(lit->extra_colormap, frontsector->SpecialColors[sector_t::walltop]); bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, viewport), foggy); break; } } } short *mfloorclip = ds->sprbottomclip - ds->x1; short *mceilingclip = ds->sprtopclip - ds->x1; double spryscale; // [RH] Draw fog partition if (ds->bFogBoundary) { renderfog.Render(Thread, x1, x2, mceilingclip, mfloorclip, wallshade, rw_light, rw_lightstep, basecolormap); if (ds->maskedtexturecol == nullptr) { goto clearfog; } } if ((ds->bFakeBoundary && !(ds->bFakeBoundary & 4)) || !visible) { goto clearfog; } MaskedSWall = ds->swall - ds->x1; MaskedScaleY = ds->yscale; maskedtexturecol = ds->maskedtexturecol - ds->x1; spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); rw_scalestep = ds->iscalestep; if (cameraLight->FixedLightLevel() >= 0) { walldrawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); columndrawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); } else if (cameraLight->FixedColormap() != nullptr) { walldrawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); columndrawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); } // find positioning texheight = tex->GetScaledHeightDouble(); texheightscale = fabs(curline->sidedef->GetTextureYScale(side_t::mid)); if (texheightscale != 1) { texheight = texheight / texheightscale; } double texturemid; if (curline->linedef->flags & ML_DONTPEGBOTTOM) { texturemid = MAX(frontsector->GetPlaneTexZ(sector_t::floor), backsector->GetPlaneTexZ(sector_t::floor)) + texheight; } else { texturemid = MIN(frontsector->GetPlaneTexZ(sector_t::ceiling), backsector->GetPlaneTexZ(sector_t::ceiling)); } rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid); wrap = (curline->linedef->flags & ML_WRAP_MIDTEX) || (curline->sidedef->Flags & WALLF_WRAP_MIDTEX); if (!wrap) { // Texture does not wrap vertically. double textop; if (MaskedScaleY < 0) { MaskedScaleY = -MaskedScaleY; sprflipvert = true; } if (tex->bWorldPanning) { // rowoffset is added before the multiply so that the masked texture will // still be positioned in world units rather than texels. texturemid += rowoffset - Thread->Viewport->viewpoint.Pos.Z; textop = texturemid; texturemid *= MaskedScaleY; } else { // rowoffset is added outside the multiply so that it positions the texture // by texels instead of world units. textop = texturemid + rowoffset / MaskedScaleY - Thread->Viewport->viewpoint.Pos.Z; texturemid = (texturemid - Thread->Viewport->viewpoint.Pos.Z) * MaskedScaleY + rowoffset; } if (sprflipvert) { MaskedScaleY = -MaskedScaleY; texturemid -= tex->GetHeight() << FRACBITS; } // [RH] Don't bother drawing segs that are completely offscreen if (viewport->globaldclip * ds->sz1 < -textop && viewport->globaldclip * ds->sz2 < -textop) { // Texture top is below the bottom of the screen goto clearfog; } if (viewport->globaluclip * ds->sz1 > texheight - textop && viewport->globaluclip * ds->sz2 > texheight - textop) { // Texture bottom is above the top of the screen goto clearfog; } if ((clip3d->fake3D & FAKE3D_CLIPBOTTOM) && textop < clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z) { notrelevant = true; goto clearfog; } if ((clip3d->fake3D & FAKE3D_CLIPTOP) && textop - texheight > clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z) { notrelevant = true; goto clearfog; } WallC.sz1 = ds->sz1; WallC.sz2 = ds->sz2; WallC.sx1 = ds->sx1; WallC.sx2 = ds->sx2; if (clip3d->fake3D & FAKE3D_CLIPTOP) { wallupper.Project(Thread->Viewport.get(), textop < clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z ? textop : clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z, &WallC); } else { wallupper.Project(Thread->Viewport.get(), textop, &WallC); } if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) { walllower.Project(Thread->Viewport.get(), textop - texheight > clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z ? textop - texheight : clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z, &WallC); } else { walllower.Project(Thread->Viewport.get(), textop - texheight, &WallC); } for (i = x1; i < x2; i++) { if (wallupper.ScreenY[i] < mceilingclip[i]) wallupper.ScreenY[i] = mceilingclip[i]; } for (i = x1; i < x2; i++) { if (walllower.ScreenY[i] > mfloorclip[i]) walllower.ScreenY[i] = mfloorclip[i]; } if (clip3d->CurrentSkybox) { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor // or above the ceiling, so the appropriate end won't be clipped automatically when adding // this drawseg. if ((curline->linedef->flags & ML_CLIP_MIDTEX) || (curline->sidedef->Flags & WALLF_CLIP_MIDTEX) || (ib_compatflags & BCOMPATF_CLIPMIDTEX)) { ClipMidtex(x1, x2); } } mfloorclip = walllower.ScreenY; mceilingclip = wallupper.ScreenY; // draw the columns one at a time if (visible) { Thread->PrepareTexture(tex); for (int x = x1; x < x2; ++x) { if (cameraLight->FixedColormap() == nullptr && cameraLight->FixedLightLevel() < 0) { columndrawerargs.SetLight(basecolormap, rw_light, wallshade); } fixed_t iscale = xs_Fix<16>::ToFix(MaskedSWall[x] * MaskedScaleY); double sprtopscreen; if (sprflipvert) sprtopscreen = viewport->CenterY + texturemid * spryscale; else sprtopscreen = viewport->CenterY - texturemid * spryscale; columndrawerargs.DrawMaskedColumn(Thread, x, iscale, tex, maskedtexturecol[x], spryscale, sprtopscreen, sprflipvert, mfloorclip, mceilingclip); rw_light += rw_lightstep; spryscale += rw_scalestep; } } } else { // Texture does wrap vertically. if (tex->bWorldPanning) { // rowoffset is added before the multiply so that the masked texture will // still be positioned in world units rather than texels. texturemid = (texturemid - Thread->Viewport->viewpoint.Pos.Z + rowoffset) * MaskedScaleY; } else { // rowoffset is added outside the multiply so that it positions the texture // by texels instead of world units. texturemid = (texturemid - Thread->Viewport->viewpoint.Pos.Z) * MaskedScaleY + rowoffset; } WallC.sz1 = ds->sz1; WallC.sz2 = ds->sz2; WallC.sx1 = ds->sx1; WallC.sx2 = ds->sx2; if (clip3d->CurrentSkybox) { // Midtex clipping doesn't work properly with skyboxes, since you're normally below the floor // or above the ceiling, so the appropriate end won't be clipped automatically when adding // this drawseg. if ((curline->linedef->flags & ML_CLIP_MIDTEX) || (curline->sidedef->Flags & WALLF_CLIP_MIDTEX) || (ib_compatflags & BCOMPATF_CLIPMIDTEX)) { ClipMidtex(x1, x2); } } if (clip3d->fake3D & FAKE3D_CLIPTOP) { wallupper.Project(Thread->Viewport.get(), clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z, &WallC); for (i = x1; i < x2; i++) { if (wallupper.ScreenY[i] < mceilingclip[i]) wallupper.ScreenY[i] = mceilingclip[i]; } mceilingclip = wallupper.ScreenY; } if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) { walllower.Project(Thread->Viewport.get(), clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z, &WallC); for (i = x1; i < x2; i++) { if (walllower.ScreenY[i] > mfloorclip[i]) walllower.ScreenY[i] = mfloorclip[i]; } mfloorclip = walllower.ScreenY; } rw_offset = 0; rw_pic = tex; double top, bot; GetMaskedWallTopBottom(ds, top, bot); RenderWallPart renderWallpart(Thread); renderWallpart.Render(walldrawerargs, frontsector, curline, WallC, rw_pic, x1, x2, mceilingclip, mfloorclip, texturemid, MaskedSWall, maskedtexturecol, ds->yscale, top, bot, true, wallshade, rw_offset, rw_light, rw_lightstep, nullptr, ds->foggy, basecolormap); } clearfog: if (ds->bFakeBoundary & 3) { RenderFakeWallRange(ds, x1, x2, wallshade); } if (!notrelevant) { if (clip3d->fake3D & FAKE3D_REFRESHCLIP) { if (!wrap) { assert(ds->bkup != nullptr); memcpy(ds->sprtopclip, ds->bkup, (ds->x2 - ds->x1) * 2); } } else { fillshort(ds->sprtopclip - ds->x1 + x1, x2 - x1, viewheight); } } return; } // kg3D - render one fake wall void RenderDrawSegment::RenderFakeWall(DrawSegment *ds, int x1, int x2, F3DFloor *rover, int wallshade, FDynamicColormap *basecolormap) { int i; double xscale; double yscale; fixed_t Alpha = Scale(rover->alpha, OPAQUE, 255); if (Alpha <= 0) return; WallDrawerArgs drawerargs; drawerargs.SetStyle(true, (rover->flags & FF_ADDITIVETRANS) != 0, Alpha); rw_lightstep = ds->lightstep; rw_light = ds->light + (x1 - ds->x1) * rw_lightstep; short *mfloorclip = ds->sprbottomclip - ds->x1; short *mceilingclip = ds->sprtopclip - ds->x1; //double spryscale = ds->iscale + ds->iscalestep * (x1 - ds->x1); float *MaskedSWall = ds->swall - ds->x1; // find positioning side_t *scaledside; side_t::ETexpart scaledpart; if (rover->flags & FF_UPPERTEXTURE) { scaledside = curline->sidedef; scaledpart = side_t::top; } else if (rover->flags & FF_LOWERTEXTURE) { scaledside = curline->sidedef; scaledpart = side_t::bottom; } else { scaledside = rover->master->sidedef[0]; scaledpart = side_t::mid; } xscale = rw_pic->Scale.X * scaledside->GetTextureXScale(scaledpart); yscale = rw_pic->Scale.Y * scaledside->GetTextureYScale(scaledpart); double rowoffset = curline->sidedef->GetTextureYOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureYOffset(side_t::mid); double planez = rover->model->GetPlaneTexZ(sector_t::ceiling); rw_offset = FLOAT2FIXED(curline->sidedef->GetTextureXOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureXOffset(side_t::mid)); if (rowoffset < 0) { rowoffset += rw_pic->GetHeight(); } double texturemid = (planez - Thread->Viewport->viewpoint.Pos.Z) * yscale; if (rw_pic->bWorldPanning) { // rowoffset is added before the multiply so that the masked texture will // still be positioned in world units rather than texels. texturemid = texturemid + rowoffset * yscale; rw_offset = xs_RoundToInt(rw_offset * xscale); } else { // rowoffset is added outside the multiply so that it positions the texture // by texels instead of world units. texturemid += rowoffset; } CameraLight *cameraLight = CameraLight::Instance(); if (cameraLight->FixedLightLevel() >= 0) drawerargs.SetLight((r_fullbrightignoresectorcolor) ? &FullNormalLight : basecolormap, 0, cameraLight->FixedLightLevelShade()); else if (cameraLight->FixedColormap() != nullptr) drawerargs.SetLight(cameraLight->FixedColormap(), 0, 0); WallC.sz1 = ds->sz1; WallC.sz2 = ds->sz2; WallC.sx1 = ds->sx1; WallC.sx2 = ds->sx2; WallC.tleft.X = ds->cx; WallC.tleft.Y = ds->cy; WallC.tright.X = ds->cx + ds->cdx; WallC.tright.Y = ds->cy + ds->cdy; WallT = ds->tmapvals; Clip3DFloors *clip3d = Thread->Clip3D.get(); wallupper.Project(Thread->Viewport.get(), clip3d->sclipTop - Thread->Viewport->viewpoint.Pos.Z, &WallC); walllower.Project(Thread->Viewport.get(), clip3d->sclipBottom - Thread->Viewport->viewpoint.Pos.Z, &WallC); for (i = x1; i < x2; i++) { if (wallupper.ScreenY[i] < mceilingclip[i]) wallupper.ScreenY[i] = mceilingclip[i]; } for (i = x1; i < x2; i++) { if (walllower.ScreenY[i] > mfloorclip[i]) walllower.ScreenY[i] = mfloorclip[i]; } ProjectedWallTexcoords walltexcoords; walltexcoords.ProjectPos(Thread->Viewport.get(), curline->sidedef->TexelLength*xscale, ds->sx1, ds->sx2, WallT); double top, bot; GetMaskedWallTopBottom(ds, top, bot); RenderWallPart renderWallpart(Thread); renderWallpart.Render(drawerargs, frontsector, curline, WallC, rw_pic, x1, x2, wallupper.ScreenY, walllower.ScreenY, texturemid, MaskedSWall, walltexcoords.UPos, yscale, top, bot, true, wallshade, rw_offset, rw_light, rw_lightstep, nullptr, ds->foggy, basecolormap); } // kg3D - walls of fake floors void RenderDrawSegment::RenderFakeWallRange(DrawSegment *ds, int x1, int x2, int wallshade) { FTexture *const DONT_DRAW = ((FTexture*)(intptr_t)-1); int i, j; F3DFloor *rover, *fover = nullptr; int passed, last; double floorHeight; double ceilingHeight; curline = ds->curline; frontsector = curline->frontsector; backsector = curline->backsector; if (backsector == nullptr) { return; } if ((ds->bFakeBoundary & 3) == 2) { sector_t *sec = backsector; backsector = frontsector; frontsector = sec; } floorHeight = backsector->CenterFloor(); ceilingHeight = backsector->CenterCeiling(); Clip3DFloors *clip3d = Thread->Clip3D.get(); // maybe fix clipheights if (!(clip3d->fake3D & FAKE3D_CLIPBOTTOM)) clip3d->sclipBottom = floorHeight; if (!(clip3d->fake3D & FAKE3D_CLIPTOP)) clip3d->sclipTop = ceilingHeight; // maybe not visible if (clip3d->sclipBottom >= frontsector->CenterCeiling()) return; if (clip3d->sclipTop <= frontsector->CenterFloor()) return; if (clip3d->fake3D & FAKE3D_DOWN2UP) { // bottom to viewz last = 0; for (i = backsector->e->XFloor.ffloors.Size() - 1; i >= 0; i--) { rover = backsector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_EXISTS)) continue; // visible? passed = 0; if (!(rover->flags & FF_RENDERSIDES) || rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || rover->top.plane->Zat0() <= clip3d->sclipBottom || rover->bottom.plane->Zat0() >= ceilingHeight || rover->top.plane->Zat0() <= floorHeight) { if (!i) { passed = 1; } else { continue; } } rw_pic = nullptr; if (rover->bottom.plane->Zat0() >= clip3d->sclipTop || passed) { if (last) { break; } // maybe wall from inside rendering? fover = nullptr; for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) { fover = frontsector->e->XFloor.ffloors[j]; if (fover->model == rover->model) { // never fover = nullptr; break; } if (!(fover->flags & FF_EXISTS)) continue; if (!(fover->flags & FF_RENDERSIDES)) continue; // no sloped walls, it's bugged if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; // visible? if (fover->top.plane->Zat0() <= clip3d->sclipBottom) continue; // no if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) { // no, last possible fover = nullptr; break; } // it is, render inside? if (!(fover->flags & (FF_BOTHPLANES | FF_INVERTPLANES))) { // no fover = nullptr; } break; } // nothing if (!fover || j == -1) { break; } // correct texture if (fover->flags & rover->flags & FF_SWIMMABLE) { // don't ever draw (but treat as something has been found) rw_pic = DONT_DRAW; } else if (fover->flags & FF_UPPERTEXTURE) { rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); } else if (fover->flags & FF_LOWERTEXTURE) { rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); } else { rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); } } else if (frontsector->e->XFloor.ffloors.Size()) { // maybe not visible? fover = nullptr; for (j = frontsector->e->XFloor.ffloors.Size() - 1; j >= 0; j--) { fover = frontsector->e->XFloor.ffloors[j]; if (fover->model == rover->model) // never { break; } if (!(fover->flags & FF_EXISTS)) continue; if (!(fover->flags & FF_RENDERSIDES)) continue; // no sloped walls, it's bugged if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; // visible? if (fover->top.plane->Zat0() <= clip3d->sclipBottom) continue; // no if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) { // visible, last possible fover = nullptr; break; } if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && !(!(fover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) ) { break; } if (fover->flags & rover->flags & FF_SWIMMABLE) { // don't ever draw (but treat as something has been found) rw_pic = DONT_DRAW; } fover = nullptr; // visible break; } if (fover && j != -1) { fover = nullptr; last = 1; continue; // not visible } } if (!rw_pic) { fover = nullptr; if (rover->flags & FF_UPPERTEXTURE) { rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); } else if (rover->flags & FF_LOWERTEXTURE) { rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); } else { rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); } } // correct colors now FDynamicColormap *basecolormap = nullptr; wallshade = ds->shade; CameraLight *cameraLight = CameraLight::Instance(); if (cameraLight->FixedLightLevel() < 0) { if ((ds->bFakeBoundary & 3) == 2) { for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) { if (clip3d->sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) { lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; basecolormap = GetColorTable(lit->extra_colormap, frontsector->SpecialColors[sector_t::walltop]); bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, Thread->Viewport.get()), foggy); break; } } } else { for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) { if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) { lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; basecolormap = GetColorTable(lit->extra_colormap, frontsector->SpecialColors[sector_t::walltop]); bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, Thread->Viewport.get()), foggy); break; } } } } if (basecolormap == nullptr) basecolormap = GetColorTable(frontsector->Colormap, frontsector->SpecialColors[sector_t::walltop]); if (rw_pic != DONT_DRAW) { RenderFakeWall(ds, x1, x2, fover ? fover : rover, wallshade, basecolormap); } else rw_pic = nullptr; break; } } else { // top to viewz for (i = 0; i < (int)backsector->e->XFloor.ffloors.Size(); i++) { rover = backsector->e->XFloor.ffloors[i]; if (!(rover->flags & FF_EXISTS)) continue; // visible? passed = 0; if (!(rover->flags & FF_RENDERSIDES) || rover->top.plane->isSlope() || rover->bottom.plane->isSlope() || rover->bottom.plane->Zat0() >= clip3d->sclipTop || rover->top.plane->Zat0() <= floorHeight || rover->bottom.plane->Zat0() >= ceilingHeight) { if ((unsigned)i == backsector->e->XFloor.ffloors.Size() - 1) { passed = 1; } else { continue; } } rw_pic = nullptr; if (rover->top.plane->Zat0() <= clip3d->sclipBottom || passed) { // maybe wall from inside rendering? fover = nullptr; for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) { fover = frontsector->e->XFloor.ffloors[j]; if (fover->model == rover->model) { // never fover = nullptr; break; } if (!(fover->flags & FF_EXISTS)) continue; if (!(fover->flags & FF_RENDERSIDES)) continue; // no sloped walls, it's bugged if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; // visible? if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) continue; // no if (fover->top.plane->Zat0() <= clip3d->sclipBottom) { // no, last possible fover = nullptr; break; } // it is, render inside? if (!(fover->flags & (FF_BOTHPLANES | FF_INVERTPLANES))) { // no fover = nullptr; } break; } // nothing if (!fover || (unsigned)j == frontsector->e->XFloor.ffloors.Size()) { break; } // correct texture if (fover->flags & rover->flags & FF_SWIMMABLE) { rw_pic = DONT_DRAW; // don't ever draw (but treat as something has been found) } else if (fover->flags & FF_UPPERTEXTURE) { rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); } else if (fover->flags & FF_LOWERTEXTURE) { rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); } else { rw_pic = TexMan(fover->master->sidedef[0]->GetTexture(side_t::mid), true); } } else if (frontsector->e->XFloor.ffloors.Size()) { // maybe not visible? fover = nullptr; for (j = 0; j < (int)frontsector->e->XFloor.ffloors.Size(); j++) { fover = frontsector->e->XFloor.ffloors[j]; if (fover->model == rover->model) { // never break; } if (!(fover->flags & FF_EXISTS)) continue; if (!(fover->flags & FF_RENDERSIDES)) continue; // no sloped walls, its bugged if (fover->top.plane->isSlope() || fover->bottom.plane->isSlope()) continue; // visible? if (fover->bottom.plane->Zat0() >= clip3d->sclipTop) continue; // no if (fover->top.plane->Zat0() <= clip3d->sclipBottom) { // visible, last possible fover = nullptr; break; } if ((fover->flags & FF_SOLID) == (rover->flags & FF_SOLID) && !(!(rover->flags & FF_SOLID) && (fover->alpha == 255 || rover->alpha == 255)) ) { break; } if (fover->flags & rover->flags & FF_SWIMMABLE) { // don't ever draw (but treat as something has been found) rw_pic = DONT_DRAW; } fover = nullptr; // visible break; } if (fover && (unsigned)j != frontsector->e->XFloor.ffloors.Size()) { // not visible break; } } if (rw_pic == nullptr) { fover = nullptr; if (rover->flags & FF_UPPERTEXTURE) { rw_pic = TexMan(curline->sidedef->GetTexture(side_t::top), true); } else if (rover->flags & FF_LOWERTEXTURE) { rw_pic = TexMan(curline->sidedef->GetTexture(side_t::bottom), true); } else { rw_pic = TexMan(rover->master->sidedef[0]->GetTexture(side_t::mid), true); } } // correct colors now FDynamicColormap *basecolormap = nullptr; wallshade = ds->shade; CameraLight *cameraLight = CameraLight::Instance(); if (cameraLight->FixedLightLevel() < 0) { if ((ds->bFakeBoundary & 3) == 2) { for (j = backsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) { if (clip3d->sclipTop <= backsector->e->XFloor.lightlist[j].plane.Zat0()) { lightlist_t *lit = &backsector->e->XFloor.lightlist[j]; basecolormap = GetColorTable(lit->extra_colormap, frontsector->SpecialColors[sector_t::walltop]); bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, Thread->Viewport.get()), foggy); break; } } } else { for (j = frontsector->e->XFloor.lightlist.Size() - 1; j >= 0; j--) { if (clip3d->sclipTop <= frontsector->e->XFloor.lightlist[j].plane.Zat0()) { lightlist_t *lit = &frontsector->e->XFloor.lightlist[j]; basecolormap = GetColorTable(lit->extra_colormap, frontsector->SpecialColors[sector_t::walltop]); bool foggy = (level.fadeto || basecolormap->Fade || (level.flags & LEVEL_HASFADETABLE)); // [RH] set foggy flag wallshade = LightVisibility::LightLevelToShade(curline->sidedef->GetLightLevel(ds->foggy, *lit->p_lightlevel, lit->lightsource != nullptr) + LightVisibility::ActualExtraLight(ds->foggy, Thread->Viewport.get()), foggy); break; } } } } if (basecolormap == nullptr) basecolormap = GetColorTable(frontsector->Colormap, frontsector->SpecialColors[sector_t::walltop]); if (rw_pic != DONT_DRAW) { RenderFakeWall(ds, x1, x2, fover ? fover : rover, wallshade, basecolormap); } else { rw_pic = nullptr; } break; } } return; } // Clip a midtexture to the floor and ceiling of the sector in front of it. void RenderDrawSegment::ClipMidtex(int x1, int x2) { ProjectedWallLine most; RenderPortal *renderportal = Thread->Portal.get(); most.Project(Thread->Viewport.get(), curline->frontsector->ceilingplane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); for (int i = x1; i < x2; ++i) { if (wallupper.ScreenY[i] < most.ScreenY[i]) wallupper.ScreenY[i] = most.ScreenY[i]; } most.Project(Thread->Viewport.get(), curline->frontsector->floorplane, &WallC, curline, renderportal->MirrorFlags & RF_XFLIP); for (int i = x1; i < x2; ++i) { if (walllower.ScreenY[i] > most.ScreenY[i]) walllower.ScreenY[i] = most.ScreenY[i]; } } void RenderDrawSegment::GetMaskedWallTopBottom(DrawSegment *ds, double &top, double &bot) { 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); top = MAX(frontcz1, frontcz2); bot = MIN(frontfz1, frontfz2); Clip3DFloors *clip3d = Thread->Clip3D.get(); if (clip3d->fake3D & FAKE3D_CLIPTOP) { top = MIN(top, clip3d->sclipTop); } if (clip3d->fake3D & FAKE3D_CLIPBOTTOM) { bot = MAX(bot, clip3d->sclipBottom); } } }