//----------------------------------------------------------------------------- // // 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 #include "doomdef.h" #include "doomstat.h" #include "doomdata.h" #include "p_lnspec.h" #include "r_sky.h" #include "v_video.h" #include "m_swap.h" #include "filesystem.h" #include "stats.h" #include "a_sharedglobal.h" #include "d_net.h" #include "g_level.h" #include "r_wallsetup.h" #include "v_palette.h" #include "r_data/colormaps.h" #include "r_walldraw.h" #include "r_memory.h" #include "swrenderer/line/r_line.h" #include "swrenderer/scene/r_scene.h" #include "swrenderer/scene/r_light.h" #include "swrenderer/viewport/r_viewport.h" namespace swrenderer { // Transform and clip coordinates. Returns true if it was clipped away bool FWallCoords::Init(RenderThread* thread, const DVector2& pt1, const DVector2& pt2, seg_t* lineseg) { auto viewport = thread->Viewport.get(); RenderPortal* renderportal = thread->Portal.get(); // Rotate to view direction: tleft.X = float(pt1.X * viewport->viewpoint.Sin - pt1.Y * viewport->viewpoint.Cos); tright.X = float(pt2.X * viewport->viewpoint.Sin - pt2.Y * viewport->viewpoint.Cos); tleft.Y = float(pt1.X * viewport->viewpoint.TanCos + pt1.Y * viewport->viewpoint.TanSin); tright.Y = float(pt2.X * viewport->viewpoint.TanCos + pt2.Y * viewport->viewpoint.TanSin); if (renderportal->MirrorFlags & RF_XFLIP) { float t = -tleft.X; tleft.X = -tright.X; tright.X = t; std::swap(tleft.Y, tright.Y); } // Edge clip: float fsx1, fsz1, fsx2, fsz2; if (tleft.X >= -tleft.Y) { if (tleft.X > tleft.Y) return true; // left edge is off the right side if (tleft.Y == 0) return true; fsx1 = viewport->CenterX + tleft.X * viewport->CenterX / tleft.Y; fsz1 = tleft.Y; tx1 = 0.0f; } else { if (tright.X < -tright.Y) return true; // wall is off the left side float den = tleft.X - tright.X - tright.Y + tleft.Y; if (den == 0) return true; fsx1 = 0; tx1 = (tleft.X + tleft.Y) / den; fsz1 = tleft.Y + (tright.Y - tleft.Y) * tx1; } if (fsz1 < TOO_CLOSE_Z) return true; if (tright.X <= tright.Y) { if (tright.X < -tright.Y) return true; // right edge is off the left side if (tright.Y == 0) return true; fsx2 = viewport->CenterX + tright.X * viewport->CenterX / tright.Y; fsz2 = tright.Y; tx2 = 1.0f; } else { if (tleft.X > tleft.Y) return true; // wall is off the right side float den = tright.Y - tleft.Y - tright.X + tleft.X; if (den == 0) return true; fsx2 = viewwidth; tx2 = (tleft.X - tleft.Y) / den; fsz2 = tleft.Y + (tright.Y - tleft.Y) * tx2; } if (fsz2 < TOO_CLOSE_Z) return true; // Find screen range covered by the line: sx1 = xs_RoundToInt(fsx1); sx2 = xs_RoundToInt(fsx2); if (sx2 <= sx1) return true; // Remap texture coordinates to the part covered by the line segment: if (lineseg && lineseg->linedef) { line_t* line = lineseg->linedef; float t1, t2; if (fabs(line->delta.X) > fabs(line->delta.Y)) { t1 = (lineseg->v1->fX() - line->v1->fX()) / line->delta.X; t2 = (lineseg->v2->fX() - line->v1->fX()) / line->delta.X; } else { t1 = (lineseg->v1->fY() - line->v1->fY()) / line->delta.Y; t2 = (lineseg->v2->fY() - line->v1->fY()) / line->delta.Y; } if (line->sidedef[0] != lineseg->sidedef) { t1 = 1.0f - t1; t2 = 1.0f - t2; } tx1 = t1 + tx1 * (t2 - t1); tx2 = t1 + tx2 * (t2 - t1); } // Calculate screen depths for the start and end points (resulting values are at the pixel center): float delta = fsx2 - fsx1; float t1 = (sx1 + 0.5f - fsx1) / delta; float t2 = (sx2 + 0.5f - fsx1) / delta; float invZ1 = 1.0f / fsz1; float invZ2 = 1.0f / fsz2; sz1 = 1.0f / (invZ1 * (1.0f - t1) + invZ2 * t1); sz2 = 1.0f / (invZ1 * (1.0f - t2) + invZ2 * t2); // Adjust texture coordinates to also be at the pixel centers: float ftx1 = tx1 * invZ1; float ftx2 = tx2 * invZ2; tx1 = (ftx1 * (1.0f - t1) + ftx2 * t1) * sz1; tx2 = (ftx1 * (1.0f - t2) + ftx2 * t2) * sz2; return false; } //////////////////////////////////////////////////////////////////////////// ProjectedWallCull ProjectedWallLine::Project(RenderViewport *viewport, double z, const FWallCoords *wallc) { return Project(viewport, z, z, wallc); } ProjectedWallCull ProjectedWallLine::Project(RenderViewport *viewport, double z1, double z2, const FWallCoords *wallc) { float y1 = (float)(viewport->CenterY - z1 * viewport->InvZtoScale / wallc->sz1); float y2 = (float)(viewport->CenterY - z2 * viewport->InvZtoScale / wallc->sz2); if (y1 < 0 && y2 < 0) // entire line is above screen { memset(&ScreenY[wallc->sx1], 0, (wallc->sx2 - wallc->sx1) * sizeof(ScreenY[0])); return ProjectedWallCull::OutsideAbove; } else if (y1 > viewheight && y2 > viewheight) // entire line is below screen { fillshort(&ScreenY[wallc->sx1], wallc->sx2 - wallc->sx1, viewheight); return ProjectedWallCull::OutsideBelow; } if (wallc->sx2 <= wallc->sx1) return ProjectedWallCull::Visible; float rcp_delta = 1.0f / (wallc->sx2 - wallc->sx1); if (y1 >= 0.0f && y2 >= 0.0f && xs_RoundToInt(y1) <= viewheight && xs_RoundToInt(y2) <= viewheight) { for (int x = wallc->sx1; x < wallc->sx2; x++) { float t = (x - wallc->sx1) * rcp_delta; float y = y1 * (1.0f - t) + y2 * t; ScreenY[x] = (short)xs_RoundToInt(y); } } else { for (int x = wallc->sx1; x < wallc->sx2; x++) { float t = (x - wallc->sx1) * rcp_delta; float y = y1 * (1.0f - t) + y2 * t; ScreenY[x] = (short)clamp(xs_RoundToInt(y), 0, viewheight); } } return ProjectedWallCull::Visible; } ProjectedWallCull ProjectedWallLine::Project(RenderViewport *viewport, const secplane_t &plane, const FWallCoords *wallc, seg_t *curline, bool xflip) { if (!plane.isSlope()) { return Project(viewport, plane.Zat0() - viewport->viewpoint.Pos.Z, wallc); } else { // Get Z coordinates at both ends of the line double x, y, den, z1, z2; if (xflip) { x = curline->v2->fX(); y = curline->v2->fY(); if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) { double frac = (wallc->tleft.Y + wallc->tleft.X) / den; x -= frac * (x - curline->v1->fX()); y -= frac * (y - curline->v1->fY()); } z1 = plane.ZatPoint(x, y) - viewport->viewpoint.Pos.Z; if (wallc->sx2 > wallc->sx1 + 1) { x = curline->v1->fX(); y = curline->v1->fY(); if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) { double frac = (wallc->tright.Y - wallc->tright.X) / den; x += frac * (curline->v2->fX() - x); y += frac * (curline->v2->fY() - y); } z2 = plane.ZatPoint(x, y) - viewport->viewpoint.Pos.Z; } else { z2 = z1; } } else { x = curline->v1->fX(); y = curline->v1->fY(); if (wallc->sx1 == 0 && 0 != (den = wallc->tleft.X - wallc->tright.X + wallc->tleft.Y - wallc->tright.Y)) { double frac = (wallc->tleft.Y + wallc->tleft.X) / den; x += frac * (curline->v2->fX() - x); y += frac * (curline->v2->fY() - y); } z1 = plane.ZatPoint(x, y) - viewport->viewpoint.Pos.Z; if (wallc->sx2 > wallc->sx1 + 1) { x = curline->v2->fX(); y = curline->v2->fY(); if (wallc->sx2 == viewwidth && 0 != (den = wallc->tleft.X - wallc->tright.X - wallc->tleft.Y + wallc->tright.Y)) { double frac = (wallc->tright.Y - wallc->tright.X) / den; x -= frac * (x - curline->v1->fX()); y -= frac * (y - curline->v1->fY()); } z2 = plane.ZatPoint(x, y) - viewport->viewpoint.Pos.Z; } else { z2 = z1; } } return Project(viewport, z1, z2, wallc); } } void ProjectedWallLine::ClipTop(int x1, int x2, const DrawSegmentClipInfo& clip) { for (int i = x1; i < x2; i++) { ScreenY[i] = std::max(ScreenY[i], clip.sprtopclip[i]); } } void ProjectedWallLine::ClipBottom(int x1, int x2, const DrawSegmentClipInfo& clip) { for (int i = x1; i < x2; i++) { ScreenY[i] = min(ScreenY[i], clip.sprbottomclip[i]); } } ///////////////////////////////////////////////////////////////////////// void ProjectedWallTexcoords::ProjectTop(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic) { side_t* sidedef = lineseg->sidedef; line_t* linedef = lineseg->linedef; float yscale = GetYScale(sidedef, pic, side_t::top); double cameraZ = viewport->viewpoint.Pos.Z; double texturemid; if (yscale >= 0) { // normal orientation if (linedef->flags & ML_DONTPEGTOP) { // top of texture at top texturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - cameraZ) * yscale; } else { // bottom of texture at bottom texturemid = (backsector->GetPlaneTexZ(sector_t::ceiling) - cameraZ) * yscale + pic->GetHeight(); } } else { // upside down if (linedef->flags & ML_DONTPEGTOP) { // bottom of texture at top texturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - cameraZ) * yscale + pic->GetHeight(); } else { // top of texture at bottom texturemid = (backsector->GetPlaneTexZ(sector_t::ceiling) - cameraZ) * yscale; } } texturemid += GetRowOffset(lineseg, pic, side_t::top); fixed_t xoffset = GetXOffset(lineseg, pic, side_t::top); Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::top), WallC, pic, xoffset, texturemid, yscale, false); } void ProjectedWallTexcoords::ProjectMid(RenderViewport* viewport, sector_t* frontsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic) { side_t* sidedef = lineseg->sidedef; line_t* linedef = lineseg->linedef; float yscale = GetYScale(sidedef, pic, side_t::mid); double cameraZ = viewport->viewpoint.Pos.Z; double texturemid; if (yscale >= 0) { // normal orientation if (linedef->flags & ML_DONTPEGBOTTOM) { // bottom of texture at bottom texturemid = (frontsector->GetPlaneTexZ(sector_t::floor) - cameraZ) * yscale + pic->GetHeight(); } else { // top of texture at top texturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - cameraZ) * yscale; } } else { // upside down if (linedef->flags & ML_DONTPEGBOTTOM) { // top of texture at bottom texturemid = (frontsector->GetPlaneTexZ(sector_t::floor) - cameraZ) * yscale; } else { // bottom of texture at top texturemid = (frontsector->GetPlaneTexZ(sector_t::ceiling) - cameraZ) * yscale + pic->GetHeight(); } } texturemid += GetRowOffset(lineseg, pic, side_t::mid); fixed_t xoffset = GetXOffset(lineseg, pic, side_t::mid); Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::mid), WallC, pic, xoffset, texturemid, yscale, false); } void ProjectedWallTexcoords::ProjectBottom(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic) { side_t* sidedef = lineseg->sidedef; line_t* linedef = lineseg->linedef; double frontlowertop = frontsector->GetPlaneTexZ(sector_t::ceiling); if (frontsector->GetTexture(sector_t::ceiling) == skyflatnum && backsector->GetTexture(sector_t::ceiling) == skyflatnum) { // Putting sky ceilings on the front and back of a line alters the way unpegged // positioning works. frontlowertop = backsector->GetPlaneTexZ(sector_t::ceiling); } float yscale = GetYScale(sidedef, pic, side_t::bottom); double cameraZ = viewport->viewpoint.Pos.Z; double texturemid; if (yscale >= 0) { // normal orientation if (linedef->flags & ML_DONTPEGBOTTOM) { // bottom of texture at bottom texturemid = (frontlowertop - cameraZ) * yscale; } else { // top of texture at top texturemid = (backsector->GetPlaneTexZ(sector_t::floor) - cameraZ) * yscale; } } else { // upside down if (linedef->flags & ML_DONTPEGBOTTOM) { // top of texture at bottom texturemid = (frontlowertop - cameraZ) * yscale; } else { // bottom of texture at top texturemid = (backsector->GetPlaneTexZ(sector_t::floor) - cameraZ) * yscale + pic->GetHeight(); } } texturemid += GetRowOffset(lineseg, pic, side_t::bottom); fixed_t xoffset = GetXOffset(lineseg, pic, side_t::bottom); Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::bottom), WallC, pic, xoffset, texturemid, yscale, false); } void ProjectedWallTexcoords::ProjectTranslucent(RenderViewport* viewport, sector_t* frontsector, sector_t* backsector, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic) { line_t* linedef = lineseg->linedef; side_t* sidedef = lineseg->sidedef; float yscale = GetYScale(sidedef, pic, side_t::mid); double cameraZ = viewport->viewpoint.Pos.Z; double texZFloor = max(frontsector->GetPlaneTexZ(sector_t::floor), backsector->GetPlaneTexZ(sector_t::floor)); double texZCeiling = min(frontsector->GetPlaneTexZ(sector_t::ceiling), backsector->GetPlaneTexZ(sector_t::ceiling)); double texturemid; if (yscale >= 0) { // normal orientation if (linedef->flags & ML_DONTPEGBOTTOM) { // bottom of texture at bottom texturemid = (texZFloor - cameraZ) * yscale + pic->GetHeight(); } else { // top of texture at top texturemid = (texZCeiling - cameraZ) * yscale; } } else { // upside down if (linedef->flags & ML_DONTPEGBOTTOM) { // top of texture at bottom texturemid = (texZFloor - cameraZ) * yscale; } else { // bottom of texture at top texturemid = (texZCeiling - cameraZ) * yscale + pic->GetHeight(); } } texturemid += GetRowOffset(lineseg, pic, side_t::mid); fixed_t xoffset = GetXOffset(lineseg, pic, side_t::mid); Project(viewport, sidedef->TexelLength * GetXScale(sidedef, pic, side_t::mid), WallC, pic, xoffset, texturemid, yscale, false); } void ProjectedWallTexcoords::Project3DFloor(RenderViewport* viewport, F3DFloor* rover, seg_t* lineseg, const FWallCoords& WallC, FSoftwareTexture* pic) { // find positioning side_t* scaledside; side_t::ETexpart scaledpart; if (rover->flags & FF_UPPERTEXTURE) { scaledside = lineseg->sidedef; scaledpart = side_t::top; } else if (rover->flags & FF_LOWERTEXTURE) { scaledside = lineseg->sidedef; scaledpart = side_t::bottom; } else { scaledside = rover->master->sidedef[0]; scaledpart = side_t::mid; } double xscale = pic->GetScale().X * scaledside->GetTextureXScale(scaledpart); float yscale = pic->GetScale().Y * scaledside->GetTextureYScale(scaledpart); double rowoffset = lineseg->sidedef->GetTextureYOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureYOffset(side_t::mid); double planez = rover->model->GetPlaneTexZ(sector_t::ceiling); fixed_t xoffset = FLOAT2FIXED(lineseg->sidedef->GetTextureXOffset(side_t::mid) + rover->master->sidedef[0]->GetTextureXOffset(side_t::mid)); if (rowoffset < 0) { rowoffset += pic->GetHeight(); } double texturemid = (planez - viewport->viewpoint.Pos.Z) * yscale; if (pic->useWorldPanning(lineseg->GetLevel())) { // 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; xoffset = xs_RoundToInt(xoffset * xscale); } else { // rowoffset is added outside the multiply so that it positions the texture // by texels instead of world units. texturemid += rowoffset; } Project(viewport, lineseg->sidedef->TexelLength * xscale, WallC, pic, xoffset, texturemid, yscale, false); } void ProjectedWallTexcoords::Project(RenderViewport *viewport, double walxrepeat, const FWallCoords& WallC, FSoftwareTexture* pic, fixed_t xoffset, double texturemid, float yscale, bool flipx) { float texwidth = pic->GetWidth(); float texheight = pic->GetHeight(); float texU1 = FIXED2FLOAT(xoffset) / texwidth; float texU2 = texU1 + walxrepeat / texwidth; if (walxrepeat < 0.0) { texU1 += 1.0f; texU2 += 1.0f; } if (flipx) { texU1 = 1.0f - texU1; texU2 = 1.0f - texU2; } float texV = texturemid / texheight; // Set up some fake vertices as that makes it easier to calculate the gradients using code already known to work. Vertex v1; v1.x = WallC.sx1 + 0.5f;// WallC.tleft.X; v1.y = 0.0f; v1.w = WallC.sz1;//WallC.tleft.Y; Vertex v2; v2.x = WallC.sx2 + 0.5f;// WallC.tright.X; v2.y = 0.0f; v2.w = WallC.sz2;// WallC.tright.Y; v1.u = texU1 * (1.0f - WallC.tx1) + texU2 * WallC.tx1; v2.u = texU1 * (1.0f - WallC.tx2) + texU2 * WallC.tx2; v1.v = texV; v2.v = texV; Vertex v3; v3.x = v1.x; v3.y = v1.y - 100.0f; v3.w = v1.w; v3.u = v1.u; v3.v = v1.v + yscale * 100.0f / texheight; // Project to screen space v1.w = 1.0f / v1.w; v2.w = 1.0f / v2.w; v3.w = 1.0f / v3.w; v1.y = viewport->CenterY - v1.y * v1.w * viewport->InvZtoScale; v2.y = viewport->CenterY - v2.y * v2.w * viewport->InvZtoScale; v3.y = viewport->CenterY - v3.y * v3.w * viewport->InvZtoScale; // Calculate gradients float bottomX = (v2.x - v3.x) * (v1.y - v3.y) - (v1.x - v3.x) * (v2.y - v3.y); float bottomY = (v1.x - v3.x) * (v2.y - v3.y) - (v2.x - v3.x) * (v1.y - v3.y); wstepX = FindGradientX(bottomX, 1.0f, 1.0f, 1.0f, v1, v2, v3); ustepX = FindGradientX(bottomX, v1.u, v2.u, v3.u, v1, v2, v3); vstepX = FindGradientX(bottomX, v1.v, v2.v, v3.v, v1, v2, v3); wstepY = FindGradientY(bottomY, 1.0f, 1.0f, 1.0f, v1, v2, v3); ustepY = FindGradientY(bottomY, v1.u, v2.u, v3.u, v1, v2, v3); vstepY = FindGradientY(bottomY, v1.v, v2.v, v3.v, v1, v2, v3); startX = v1.x; upos = v1.u * v1.w; vpos = v1.v * v1.w; wpos = v1.w; } double ProjectedWallTexcoords::GetRowOffset(seg_t* lineseg, FSoftwareTexture* tex, side_t::ETexpart texpart) { double yrepeat = GetYScale(lineseg->sidedef, tex, texpart); double rowoffset = lineseg->sidedef->GetTextureYOffset(texpart); if (yrepeat >= 0) { // check if top of texture at top: bool top_at_top = (texpart == side_t::top && (lineseg->linedef->flags & ML_DONTPEGTOP)) || (texpart != side_t::top && !(lineseg->linedef->flags & ML_DONTPEGBOTTOM)); if (rowoffset < 0 && top_at_top) { rowoffset += tex->GetHeight(); } } else { rowoffset = -rowoffset; } if (tex->useWorldPanning(lineseg->GetLevel())) { return rowoffset * yrepeat; } else { // rowoffset is added outside the multiply so that it positions the texture // by texels instead of world units. return rowoffset; } } fixed_t ProjectedWallTexcoords::GetXOffset(seg_t* lineseg, FSoftwareTexture* tex, side_t::ETexpart texpart) { fixed_t TextureOffsetU = FLOAT2FIXED(lineseg->sidedef->GetTextureXOffset(texpart)); double xscale = GetXScale(lineseg->sidedef, tex, texpart); fixed_t xoffset; if (tex->useWorldPanning(lineseg->GetLevel())) { xoffset = xs_RoundToInt(TextureOffsetU * xscale); } else { xoffset = TextureOffsetU; } if (xscale < 0) { xoffset = -xoffset; } return xoffset; } double ProjectedWallTexcoords::GetXScale(side_t* sidedef, FSoftwareTexture* tex, side_t::ETexpart texpart) { double TextureScaleU = sidedef->GetTextureXScale(texpart); return tex->GetScale().X * TextureScaleU; } double ProjectedWallTexcoords::GetYScale(side_t* sidedef, FSoftwareTexture* tex, side_t::ETexpart texpart) { double TextureScaleV = sidedef->GetTextureYScale(texpart); return tex->GetScale().Y * TextureScaleV; } ///////////////////////////////////////////////////////////////////////// void ProjectedWallLight::SetLightLeft(RenderThread *thread, const FWallCoords &wallc) { spritelight = false; x1 = wallc.sx1; CameraLight *cameraLight = CameraLight::Instance(); if (cameraLight->FixedColormap() == nullptr && cameraLight->FixedLightLevel() < 0) { lightleft = float(thread->Light->WallVis(wallc.sz1, foggy)); lightstep = float((thread->Light->WallVis(wallc.sz2, foggy) - lightleft) / (wallc.sx2 - wallc.sx1)); } else { lightleft = 1; lightstep = 0; } } void ProjectedWallLight::SetColormap(const sector_t *frontsector, seg_t *lineseg, int tier, lightlist_t *lit) { if (!lit) { basecolormap = GetColorTable(frontsector->Colormap, frontsector->SpecialColors[sector_t::walltop]); foggy = frontsector->Level->fadeto || frontsector->Colormap.FadeColor || (frontsector->Level->flags & LEVEL_HASFADETABLE); if (!(lineseg->sidedef->Flags & WALLF_POLYOBJ)) lightlevel = lineseg->sidedef->GetLightLevel(foggy, frontsector->lightlevel, tier); else lightlevel = frontsector->GetLightLevel(); } else { basecolormap = GetColorTable(lit->extra_colormap, frontsector->SpecialColors[sector_t::walltop]); foggy = frontsector->Level->fadeto || basecolormap->Fade || (frontsector->Level->flags & LEVEL_HASFADETABLE); lightlevel = lineseg->sidedef->GetLightLevel(foggy, *lit->p_lightlevel, tier, lit->lightsource != nullptr); } } }