From 28732d63d23bd471195535f68e4ba5211df6ee53 Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Wed, 4 Jan 2017 15:39:47 +0100 Subject: [PATCH] Move r_bsp into a class --- src/swrenderer/line/r_line.cpp | 10 +- src/swrenderer/plane/r_visibleplane.cpp | 9 +- src/swrenderer/r_main.cpp | 23 +- src/swrenderer/r_swrenderer.cpp | 2 +- src/swrenderer/scene/r_3dfloors.cpp | 4 +- src/swrenderer/scene/r_bsp.cpp | 1393 ++++++++++----------- src/swrenderer/scene/r_bsp.h | 70 +- src/swrenderer/scene/r_portal.cpp | 10 +- src/swrenderer/segments/r_drawsegment.cpp | 2 +- src/swrenderer/things/r_decal.cpp | 8 +- src/swrenderer/things/r_particle.cpp | 2 + src/swrenderer/things/r_playersprite.cpp | 2 +- 12 files changed, 758 insertions(+), 777 deletions(-) diff --git a/src/swrenderer/line/r_line.cpp b/src/swrenderer/line/r_line.cpp index e01e9f34a9..19b22ce109 100644 --- a/src/swrenderer/line/r_line.cpp +++ b/src/swrenderer/line/r_line.cpp @@ -134,7 +134,7 @@ namespace swrenderer // kg3D - its fake, no transfer_heights if (!(fake3D & FAKE3D_FAKEBACK)) { // killough 3/8/98, 4/4/98: hack for invisible ceilings / deep water - backsector = R_FakeFlat(backsector, &tempsec, nullptr, nullptr, curline, WallC.sx1, WallC.sx2, rw_frontcz1, rw_frontcz2); + backsector = RenderBSP::Instance()->FakeFlat(backsector, &tempsec, nullptr, nullptr, curline, WallC.sx1, WallC.sx2, rw_frontcz1, rw_frontcz2); } doorclosed = false; // killough 4/16/98 @@ -447,7 +447,7 @@ namespace swrenderer // kg3D - backup for mid and fake walls draw_segment->bkup = R_NewOpening(stop - start); - memcpy(openings + draw_segment->bkup, &ceilingclip[start], sizeof(short)*(stop - start)); + memcpy(openings + draw_segment->bkup, &RenderBSP::Instance()->ceilingclip[start], sizeof(short)*(stop - start)); draw_segment->bFogBoundary = IsFogBoundary(frontsector, backsector); if (sidedef->GetTexture(side_t::mid).isValid() || draw_segment->bFakeBoundary) @@ -559,13 +559,13 @@ namespace swrenderer if (((draw_segment->silhouette & SIL_TOP) || maskedtexture) && draw_segment->sprtopclip == -1) { draw_segment->sprtopclip = R_NewOpening(stop - start); - memcpy(openings + draw_segment->sprtopclip, &ceilingclip[start], sizeof(short)*(stop - start)); + memcpy(openings + draw_segment->sprtopclip, &RenderBSP::Instance()->ceilingclip[start], sizeof(short)*(stop - start)); } if (((draw_segment->silhouette & SIL_BOTTOM) || maskedtexture) && draw_segment->sprbottomclip == -1) { draw_segment->sprbottomclip = R_NewOpening(stop - start); - memcpy(openings + draw_segment->sprbottomclip, &floorclip[start], sizeof(short)*(stop - start)); + memcpy(openings + draw_segment->sprbottomclip, &RenderBSP::Instance()->floorclip[start], sizeof(short)*(stop - start)); } if (maskedtexture && curline->sidedef->GetTexture(side_t::mid).isValid()) @@ -960,6 +960,8 @@ namespace swrenderer R_SetColorMapLight(fixedcolormap, 0, 0); // clip wall to the floor and ceiling + auto ceilingclip = RenderBSP::Instance()->ceilingclip; + auto floorclip = RenderBSP::Instance()->floorclip; for (x = x1; x < x2; ++x) { if (walltop[x] < ceilingclip[x]) diff --git a/src/swrenderer/plane/r_visibleplane.cpp b/src/swrenderer/plane/r_visibleplane.cpp index 3cee3e8694..e312f9cad8 100644 --- a/src/swrenderer/plane/r_visibleplane.cpp +++ b/src/swrenderer/plane/r_visibleplane.cpp @@ -474,12 +474,7 @@ namespace swrenderer if (pl->left >= pl->right) return; - if (r_drawflat) // no texture mapping - { - drawerargs::ds_color += 4; - R_DrawColoredPlane(pl); - } - else if (pl->picnum == skyflatnum) // sky flat + if (pl->picnum == skyflatnum) // sky flat { R_DrawSkyPlane(pl); } @@ -506,7 +501,7 @@ namespace swrenderer basecolormap = pl->colormap; - if (r_drawflat || (!pl->height.isSlope() && !tilt)) + if (!pl->height.isSlope() && !tilt) { R_DrawNormalPlane(pl, xscale, yscale, alpha, additive, masked); } diff --git a/src/swrenderer/r_main.cpp b/src/swrenderer/r_main.cpp index 1dbae64ba3..bdad72d98b 100644 --- a/src/swrenderer/r_main.cpp +++ b/src/swrenderer/r_main.cpp @@ -103,9 +103,7 @@ static void R_ShutdownRenderer(); // EXTERNAL DATA DECLARATIONS ---------------------------------------------- extern short *openings; -extern bool r_fakingunderwater; extern int fuzzviewheight; -extern subsector_t *InSubsector; // PRIVATE DATA DECLARATIONS ----------------------------------------------- @@ -555,24 +553,13 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) R_ClearSprites (); // opening / clipping determination - // [RH] clip ceiling to console bottom - fillshort(floorclip, viewwidth, viewheight); - fillshort(ceilingclip, viewwidth, !screen->Accel2D && ConBottom > viewwindowy && !bRenderingToCanvas ? (ConBottom - viewwindowy) : 0); + RenderBSP::Instance()->ClearClip(); R_FreeOpenings(); NetUpdate (); - // [RH] Show off segs if r_drawflat is 1 - if (r_drawflat) - { - colfunc = &SWPixelFormatDrawers::FillColumn; - spanfunc = &SWPixelFormatDrawers::FillSpan; - } - else - { - colfunc = basecolfunc; - spanfunc = &SWPixelFormatDrawers::DrawSpan; - } + colfunc = basecolfunc; + spanfunc = &SWPixelFormatDrawers::DrawSpan; WindowLeft = 0; WindowRight = viewwidth; @@ -583,7 +570,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) r_dontmaplines = dontmaplines; // [RH] Hack to make windows into underwater areas possible - r_fakingunderwater = false; + RenderBSP::Instance()->ResetFakingUnderwater(); // [RH] Setup particles for this frame P_FindParticleSubsectors (); @@ -597,7 +584,7 @@ void R_RenderActorView (AActor *actor, bool dontmaplines) } // Link the polyobjects right before drawing the scene to reduce the amounts of calls to this function PO_LinkToSubsectors(); - R_RenderScene(); + RenderBSP::Instance()->RenderScene(); R_3D_ResetClip(); // reset clips (floor/ceiling) camera->renderflags = savedflags; WallCycles.Unclock(); diff --git a/src/swrenderer/r_swrenderer.cpp b/src/swrenderer/r_swrenderer.cpp index c68aed77ad..497b8e6747 100644 --- a/src/swrenderer/r_swrenderer.cpp +++ b/src/swrenderer/r_swrenderer.cpp @@ -484,6 +484,6 @@ void FSoftwareRenderer::RenderTextureView (FCanvasTexture *tex, AActor *viewpoin sector_t *FSoftwareRenderer::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel) { - return R_FakeFlat(sec, tempsec, floorlightlevel, ceilinglightlevel, nullptr, 0, 0, 0, 0); + return RenderBSP::Instance()->FakeFlat(sec, tempsec, floorlightlevel, ceilinglightlevel, nullptr, 0, 0, 0, 0); } diff --git a/src/swrenderer/scene/r_3dfloors.cpp b/src/swrenderer/scene/r_3dfloors.cpp index 9a5cdadcbb..5d3bb78221 100644 --- a/src/swrenderer/scene/r_3dfloors.cpp +++ b/src/swrenderer/scene/r_3dfloors.cpp @@ -98,8 +98,8 @@ void R_3D_NewClip() curr = (ClipStack*)M_Malloc(sizeof(ClipStack)); curr->next = 0; - memcpy(curr->floorclip, floorclip, sizeof(short) * MAXWIDTH); - memcpy(curr->ceilingclip, ceilingclip, sizeof(short) * MAXWIDTH); + memcpy(curr->floorclip, RenderBSP::Instance()->floorclip, sizeof(short) * MAXWIDTH); + memcpy(curr->ceilingclip, RenderBSP::Instance()->ceilingclip, sizeof(short) * MAXWIDTH); curr->ffloor = fakeFloor; assert(fakeFloor->floorclip == NULL); assert(fakeFloor->ceilingclip == NULL); diff --git a/src/swrenderer/scene/r_bsp.cpp b/src/swrenderer/scene/r_bsp.cpp index 0440b41bb9..f36d0439b5 100644 --- a/src/swrenderer/scene/r_bsp.cpp +++ b/src/swrenderer/scene/r_bsp.cpp @@ -44,6 +44,7 @@ #include "a_sharedglobal.h" #include "g_level.h" #include "p_effect.h" +#include "c_console.h" // State. #include "doomstat.h" @@ -54,762 +55,746 @@ #include "po_man.h" #include "r_data/colormaps.h" -CVAR (Bool, r_drawflat, false, 0) // [RH] Don't texture segs? EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor); namespace swrenderer { - namespace + RenderBSP *RenderBSP::Instance() { - subsector_t *InSubsector; - sector_t *frontsector; - uint8_t FakeSide; - - SWRenderLine render_line; + static RenderBSP bsp; + return &bsp; } -bool r_fakingunderwater; - -// Clip values are the solid pixel bounding the range. -// floorclip starts out SCREENHEIGHT and is just outside the range -// ceilingclip starts out 0 and is just inside the range -// -short floorclip[MAXWIDTH]; -short ceilingclip[MAXWIDTH]; - - -// -// killough 3/7/98: Hack floor/ceiling heights for deep water etc. -// -// If player's view height is underneath fake floor, lower the -// drawn ceiling to be just under the floor height, and replace -// the drawn floor and ceiling textures, and light level, with -// the control sector's. -// -// Similar for ceiling, only reflected. -// -// killough 4/11/98, 4/13/98: fix bugs, add 'back' parameter -// - -sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, seg_t *backline, int backx1, int backx2, double frontcz1, double frontcz2) -{ - // [RH] allow per-plane lighting - if (floorlightlevel != NULL) + sector_t *RenderBSP::FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, seg_t *backline, int backx1, int backx2, double frontcz1, double frontcz2) { - *floorlightlevel = sec->GetFloorLight (); - } + // If player's view height is underneath fake floor, lower the + // drawn ceiling to be just under the floor height, and replace + // the drawn floor and ceiling textures, and light level, with + // the control sector's. + // + // Similar for ceiling, only reflected. - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = sec->GetCeilingLight (); - } - - FakeSide = FAKED_Center; - - const sector_t *s = sec->GetHeightSec(); - if (s != NULL) - { - sector_t *heightsec = viewsector->heightsec; - bool underwater = r_fakingunderwater || - (heightsec && heightsec->floorplane.PointOnSide(ViewPos) <= 0); - bool doorunderwater = false; - int diffTex = (s->MoreFlags & SECF_CLIPFAKEPLANES); - - // Replace sector being drawn with a copy to be hacked - *tempsec = *sec; - - // Replace floor and ceiling height with control sector's heights. - if (diffTex) + // [RH] allow per-plane lighting + if (floorlightlevel != NULL) { - if (s->floorplane.CopyPlaneIfValid (&tempsec->floorplane, &sec->ceilingplane)) + *floorlightlevel = sec->GetFloorLight(); + } + + if (ceilinglightlevel != NULL) + { + *ceilinglightlevel = sec->GetCeilingLight(); + } + + FakeSide = FAKED_Center; + + const sector_t *s = sec->GetHeightSec(); + if (s != NULL) + { + sector_t *heightsec = viewsector->heightsec; + bool underwater = r_fakingunderwater || + (heightsec && heightsec->floorplane.PointOnSide(ViewPos) <= 0); + bool doorunderwater = false; + int diffTex = (s->MoreFlags & SECF_CLIPFAKEPLANES); + + // Replace sector being drawn with a copy to be hacked + *tempsec = *sec; + + // Replace floor and ceiling height with control sector's heights. + if (diffTex) { - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); - } - else if (s->MoreFlags & SECF_FAKEFLOORONLY) - { - if (underwater) + if (s->floorplane.CopyPlaneIfValid(&tempsec->floorplane, &sec->ceilingplane)) { - tempsec->ColorMap = s->ColorMap; - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); + } + else if (s->MoreFlags & SECF_FAKEFLOORONLY) + { + if (underwater) { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) + tempsec->ColorMap = s->ColorMap; + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) { - *floorlightlevel = s->GetFloorLight (); + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != NULL) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != NULL) + { + *ceilinglightlevel = s->GetCeilingLight(); + } } + FakeSide = FAKED_BelowFloor; + return tempsec; + } + return sec; + } + } + else + { + tempsec->floorplane = s->floorplane; + } - if (ceilinglightlevel != NULL) + if (!(s->MoreFlags & SECF_FAKEFLOORONLY)) + { + if (diffTex) + { + if (s->ceilingplane.CopyPlaneIfValid(&tempsec->ceilingplane, &sec->floorplane)) + { + tempsec->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false); + } + } + else + { + tempsec->ceilingplane = s->ceilingplane; + } + } + + double refceilz = s->ceilingplane.ZatPoint(ViewPos); + double orgceilz = sec->ceilingplane.ZatPoint(ViewPos); + +#if 1 + // [RH] Allow viewing underwater areas through doors/windows that + // are underwater but not in a water sector themselves. + // Only works if you cannot see the top surface of any deep water + // sectors at the same time. + if (backline && !r_fakingunderwater && backline->frontsector->heightsec == NULL) + { + if (frontcz1 <= s->floorplane.ZatPoint(backline->v1) && + frontcz2 <= s->floorplane.ZatPoint(backline->v2)) + { + // Check that the window is actually visible + for (int z = backx1; z < backx2; ++z) + { + if (floorclip[z] > ceilingclip[z]) { - *ceilinglightlevel = s->GetCeilingLight (); + doorunderwater = true; + r_fakingunderwater = true; + break; } } - FakeSide = FAKED_BelowFloor; - return tempsec; } - return sec; + } +#endif + + if (underwater || doorunderwater) + { + tempsec->floorplane = sec->floorplane; + tempsec->ceilingplane = s->floorplane; + tempsec->ceilingplane.FlipVert(); + tempsec->ceilingplane.ChangeHeight(-1 / 65536.); + tempsec->ColorMap = s->ColorMap; + } + + // killough 11/98: prevent sudden light changes from non-water sectors: + if ((underwater && !backline) || doorunderwater) + { // head-below-floor hack + tempsec->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; + + tempsec->ceilingplane = s->floorplane; + tempsec->ceilingplane.FlipVert(); + tempsec->ceilingplane.ChangeHeight(-1 / 65536.); + if (s->GetTexture(sector_t::ceiling) == skyflatnum) + { + tempsec->floorplane = tempsec->ceilingplane; + tempsec->floorplane.FlipVert(); + tempsec->floorplane.ChangeHeight(+1 / 65536.); + tempsec->SetTexture(sector_t::ceiling, tempsec->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform; + } + else + { + tempsec->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false); + tempsec->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform; + } + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != NULL) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != NULL) + { + *ceilinglightlevel = s->GetCeilingLight(); + } + } + FakeSide = FAKED_BelowFloor; + } + else if (heightsec && heightsec->ceilingplane.PointOnSide(ViewPos) <= 0 && + orgceilz > refceilz && !(s->MoreFlags & SECF_FAKEFLOORONLY)) + { // Above-ceiling hack + tempsec->ceilingplane = s->ceilingplane; + tempsec->floorplane = s->ceilingplane; + tempsec->floorplane.FlipVert(); + tempsec->floorplane.ChangeHeight(+1 / 65536.); + tempsec->ColorMap = s->ColorMap; + tempsec->ColorMap = s->ColorMap; + + tempsec->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false); + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false); + tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform; + + if (s->GetTexture(sector_t::floor) != skyflatnum) + { + tempsec->ceilingplane = sec->ceilingplane; + tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); + tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; + } + + if (!(s->MoreFlags & SECF_NOFAKELIGHT)) + { + tempsec->lightlevel = s->lightlevel; + + if (floorlightlevel != NULL) + { + *floorlightlevel = s->GetFloorLight(); + } + + if (ceilinglightlevel != NULL) + { + *ceilinglightlevel = s->GetCeilingLight(); + } + } + FakeSide = FAKED_AboveCeiling; + } + sec = tempsec; // Use other sector + } + return sec; + } + + + + // Checks BSP node/subtree bounding box. + // Returns true if some part of the bbox might be visible. + bool RenderBSP::CheckBBox(float *bspcoord) + { + static const int checkcoord[12][4] = + { + { 3,0,2,1 }, + { 3,0,2,0 }, + { 3,1,2,0 }, + { 0 }, + { 2,0,2,1 }, + { 0,0,0,0 }, + { 3,1,3,0 }, + { 0 }, + { 2,0,3,1 }, + { 2,1,3,1 }, + { 2,1,3,0 } + }; + + int boxx; + int boxy; + int boxpos; + + double x1, y1, x2, y2; + double rx1, ry1, rx2, ry2; + int sx1, sx2; + + // Find the corners of the box + // that define the edges from current viewpoint. + if (ViewPos.X <= bspcoord[BOXLEFT]) + boxx = 0; + else if (ViewPos.X < bspcoord[BOXRIGHT]) + boxx = 1; + else + boxx = 2; + + if (ViewPos.Y >= bspcoord[BOXTOP]) + boxy = 0; + else if (ViewPos.Y > bspcoord[BOXBOTTOM]) + boxy = 1; + else + boxy = 2; + + boxpos = (boxy << 2) + boxx; + if (boxpos == 5) + return true; + + x1 = bspcoord[checkcoord[boxpos][0]] - ViewPos.X; + y1 = bspcoord[checkcoord[boxpos][1]] - ViewPos.Y; + x2 = bspcoord[checkcoord[boxpos][2]] - ViewPos.X; + y2 = bspcoord[checkcoord[boxpos][3]] - ViewPos.Y; + + // check clip list for an open space + + // Sitting on a line? + if (y1 * (x1 - x2) + x1 * (y2 - y1) >= -EQUAL_EPSILON) + return true; + + rx1 = x1 * ViewSin - y1 * ViewCos; + rx2 = x2 * ViewSin - y2 * ViewCos; + ry1 = x1 * ViewTanCos + y1 * ViewTanSin; + ry2 = x2 * ViewTanCos + y2 * ViewTanSin; + + if (MirrorFlags & RF_XFLIP) + { + double t = -rx1; + rx1 = -rx2; + rx2 = t; + swapvalues(ry1, ry2); + } + + if (rx1 >= -ry1) + { + if (rx1 > ry1) return false; // left edge is off the right side + if (ry1 == 0) return false; + sx1 = xs_RoundToInt(CenterX + rx1 * CenterX / ry1); + } + else + { + if (rx2 < -ry2) return false; // wall is off the left side + if (rx1 - rx2 - ry2 + ry1 == 0) return false; // wall does not intersect view volume + sx1 = 0; + } + + if (rx2 <= ry2) + { + if (rx2 < -ry2) return false; // right edge is off the left side + if (ry2 == 0) return false; + sx2 = xs_RoundToInt(CenterX + rx2 * CenterX / ry2); + } + else + { + if (rx1 > ry1) return false; // wall is off the right side + if (ry2 - ry1 - rx2 + rx1 == 0) return false; // wall does not intersect view volume + sx2 = viewwidth; + } + + // Find the first clippost that touches the source post + // (adjacent pixels are touching). + + return R_IsWallSegmentVisible(sx1, sx2); + } + + void RenderBSP::AddPolyobjs(subsector_t *sub) + { + if (sub->BSP == NULL || sub->BSP->bDirty) + { + sub->BuildPolyBSP(); + } + if (sub->BSP->Nodes.Size() == 0) + { + RenderSubsector(&sub->BSP->Subsectors[0]); + } + else + { + RenderBSPNode(&sub->BSP->Nodes.Last()); + } + } + + // kg3D - add fake segs, never rendered + void RenderBSP::FakeDrawLoop(subsector_t *sub, visplane_t *floorplane, visplane_t *ceilingplane) + { + int count; + seg_t* line; + + count = sub->numlines; + line = sub->firstline; + + while (count--) + { + if ((line->sidedef) && !(line->sidedef->Flags & WALLF_POLYOBJ)) + { + renderline.Render(line, InSubsector, frontsector, nullptr, floorplane, ceilingplane); + } + line++; + } + } + + void RenderBSP::RenderSubsector(subsector_t *sub) + { + // Determine floor/ceiling planes. + // Add sprites of things in sector. + // Draw one or more line segments. + + int count; + seg_t* line; + sector_t tempsec; // killough 3/7/98: deep water hack + int floorlightlevel; // killough 3/16/98: set floor lightlevel + int ceilinglightlevel; // killough 4/11/98 + bool outersubsector; + int fll, cll, position; + FSectorPortal *portal; + + // kg3D - fake floor stuff + visplane_t *backupfp; + visplane_t *backupcp; + //secplane_t templane; + lightlist_t *light; + + if (InSubsector != NULL) + { // InSubsector is not NULL. This means we are rendering from a mini-BSP. + outersubsector = false; + } + else + { + outersubsector = true; + InSubsector = sub; + } + +#ifdef RANGECHECK + if (outersubsector && sub - subsectors >= (ptrdiff_t)numsubsectors) + I_Error("RenderSubsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); +#endif + + assert(sub->sector != NULL); + + if (sub->polys) + { // Render the polyobjs in the subsector first + AddPolyobjs(sub); + if (outersubsector) + { + InSubsector = NULL; + } + return; + } + + frontsector = sub->sector; + frontsector->MoreFlags |= SECF_DRAWN; + count = sub->numlines; + line = sub->firstline; + + // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect + frontsector = FakeFlat(frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, nullptr, 0, 0, 0, 0); + + fll = floorlightlevel; + cll = ceilinglightlevel; + + // [RH] set foggy flag + foggy = level.fadeto || frontsector->ColorMap->Fade || (level.flags & LEVEL_HASFADETABLE); + r_actualextralight = foggy ? 0 : extralight << 4; + + // kg3D - fake lights + if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(frontsector, &frontsector->ceilingplane, false); + basecolormap = light->extra_colormap; + // If this is the real ceiling, don't discard plane lighting R_FakeFlat() + // accounted for. + if (light->p_lightlevel != &frontsector->lightlevel) + { + ceilinglightlevel = *light->p_lightlevel; } } else { - tempsec->floorplane = s->floorplane; + basecolormap = (r_fullbrightignoresectorcolor && fixedlightlev >= 0) ? &FullNormalLight : frontsector->ColorMap; } - if (!(s->MoreFlags & SECF_FAKEFLOORONLY)) + portal = frontsector->ValidatePortal(sector_t::ceiling); + + visplane_t *ceilingplane = frontsector->ceilingplane.PointOnSide(ViewPos) > 0 || + frontsector->GetTexture(sector_t::ceiling) == skyflatnum || + portal != NULL || + (frontsector->heightsec && + !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && + frontsector->heightsec->GetTexture(sector_t::floor) == skyflatnum) ? + R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 + frontsector->GetTexture(sector_t::ceiling), + ceilinglightlevel + r_actualextralight, // killough 4/11/98 + frontsector->GetAlpha(sector_t::ceiling), + !!(frontsector->GetFlags(sector_t::ceiling) & PLANEF_ADDITIVE), + frontsector->planes[sector_t::ceiling].xform, + frontsector->sky, + portal + ) : NULL; + + if (ceilingplane) + R_AddPlaneLights(ceilingplane, frontsector->lighthead); + + if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) { - if (diffTex) + light = P_GetPlaneLight(frontsector, &frontsector->floorplane, false); + basecolormap = light->extra_colormap; + // If this is the real floor, don't discard plane lighting R_FakeFlat() + // accounted for. + if (light->p_lightlevel != &frontsector->lightlevel) { - if (s->ceilingplane.CopyPlaneIfValid (&tempsec->ceilingplane, &sec->floorplane)) + floorlightlevel = *light->p_lightlevel; + } + } + else + { + basecolormap = (r_fullbrightignoresectorcolor && fixedlightlev >= 0) ? &FullNormalLight : frontsector->ColorMap; + } + + // killough 3/7/98: Add (x,y) offsets to flats, add deep water check + // killough 3/16/98: add floorlightlevel + // killough 10/98: add support for skies transferred from sidedefs + portal = frontsector->ValidatePortal(sector_t::floor); + + visplane_t *floorplane = frontsector->floorplane.PointOnSide(ViewPos) > 0 || // killough 3/7/98 + frontsector->GetTexture(sector_t::floor) == skyflatnum || + portal != NULL || + (frontsector->heightsec && + !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && + frontsector->heightsec->GetTexture(sector_t::ceiling) == skyflatnum) ? + R_FindPlane(frontsector->floorplane, + frontsector->GetTexture(sector_t::floor), + floorlightlevel + r_actualextralight, // killough 3/16/98 + frontsector->GetAlpha(sector_t::floor), + !!(frontsector->GetFlags(sector_t::floor) & PLANEF_ADDITIVE), + frontsector->planes[sector_t::floor].xform, + frontsector->sky, + portal + ) : NULL; + + if (floorplane) + R_AddPlaneLights(floorplane, frontsector->lighthead); + + // kg3D - fake planes rendering + if (r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) + { + backupfp = floorplane; + backupcp = ceilingplane; + // first check all floors + for (int i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) + { + fakeFloor = frontsector->e->XFloor.ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!fakeFloor->model) continue; + if (fakeFloor->bottom.plane->isSlope()) continue; + if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) { - tempsec->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false); + R_3D_AddHeight(fakeFloor->top.plane, frontsector); } - } - else - { - tempsec->ceilingplane = s->ceilingplane; - } - } - - double refceilz = s->ceilingplane.ZatPoint(ViewPos); - double orgceilz = sec->ceilingplane.ZatPoint(ViewPos); - -#if 1 - // [RH] Allow viewing underwater areas through doors/windows that - // are underwater but not in a water sector themselves. - // Only works if you cannot see the top surface of any deep water - // sectors at the same time. - if (backline && !r_fakingunderwater && backline->frontsector->heightsec == NULL) - { - if (frontcz1 <= s->floorplane.ZatPoint(backline->v1) && - frontcz2 <= s->floorplane.ZatPoint(backline->v2)) - { - // Check that the window is actually visible - for (int z = backx1; z < backx2; ++z) + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (fakeFloor->alpha == 0) continue; + if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue; + fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE); + if (fakeFloor->validcount != validcount) { - if (floorclip[z] > ceilingclip[z]) + fakeFloor->validcount = validcount; + R_3D_NewClip(); + } + double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight < ViewPos.Z && + fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)) + { + fake3D = FAKE3D_FAKEFLOOR; + tempsec = *fakeFloor->model; + tempsec.floorplane = *fakeFloor->top.plane; + tempsec.ceilingplane = *fakeFloor->bottom.plane; + if (!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) { - doorunderwater = true; - r_fakingunderwater = true; - break; + tempsec.SetTexture(sector_t::floor, tempsec.GetTexture(sector_t::ceiling)); + position = sector_t::ceiling; } + else position = sector_t::floor; + frontsector = &tempsec; + + if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(sub->sector, &frontsector->floorplane, false); + basecolormap = light->extra_colormap; + floorlightlevel = *light->p_lightlevel; + } + + ceilingplane = NULL; + floorplane = R_FindPlane(frontsector->floorplane, + frontsector->GetTexture(sector_t::floor), + floorlightlevel + r_actualextralight, // killough 3/16/98 + frontsector->GetAlpha(sector_t::floor), + !!(fakeFloor->flags & FF_ADDITIVETRANS), + frontsector->planes[position].xform, + frontsector->sky, + NULL); + + if (floorplane) + R_AddPlaneLights(floorplane, frontsector->lighthead); + + FakeDrawLoop(sub, floorplane, ceilingplane); + fake3D = 0; + frontsector = sub->sector; } } - } -#endif + // and now ceilings + for (unsigned int i = 0; i < frontsector->e->XFloor.ffloors.Size(); i++) + { + fakeFloor = frontsector->e->XFloor.ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!fakeFloor->model) continue; + if (fakeFloor->top.plane->isSlope()) continue; + if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES | FF_RENDERSIDES))) + { + R_3D_AddHeight(fakeFloor->bottom.plane, frontsector); + } + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (fakeFloor->alpha == 0) continue; + if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE | FF_INVERTSECTOR)) == (FF_SWIMMABLE | FF_INVERTSECTOR)) continue; + fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE); - if (underwater || doorunderwater) + if (fakeFloor->validcount != validcount) + { + fakeFloor->validcount = validcount; + R_3D_NewClip(); + } + double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot); + if (fakeHeight > ViewPos.Z && + fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)) + { + fake3D = FAKE3D_FAKECEILING; + tempsec = *fakeFloor->model; + tempsec.floorplane = *fakeFloor->top.plane; + tempsec.ceilingplane = *fakeFloor->bottom.plane; + if ((!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) || + (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR)) + { + tempsec.SetTexture(sector_t::ceiling, tempsec.GetTexture(sector_t::floor)); + position = sector_t::floor; + } + else position = sector_t::ceiling; + frontsector = &tempsec; + + tempsec.ceilingplane.ChangeHeight(-1 / 65536.); + if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size()) + { + light = P_GetPlaneLight(sub->sector, &frontsector->ceilingplane, false); + basecolormap = light->extra_colormap; + ceilinglightlevel = *light->p_lightlevel; + } + tempsec.ceilingplane.ChangeHeight(1 / 65536.); + + floorplane = NULL; + ceilingplane = R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 + frontsector->GetTexture(sector_t::ceiling), + ceilinglightlevel + r_actualextralight, // killough 4/11/98 + frontsector->GetAlpha(sector_t::ceiling), + !!(fakeFloor->flags & FF_ADDITIVETRANS), + frontsector->planes[position].xform, + frontsector->sky, + NULL); + + if (ceilingplane) + R_AddPlaneLights(ceilingplane, frontsector->lighthead); + + FakeDrawLoop(sub, floorplane, ceilingplane); + fake3D = 0; + frontsector = sub->sector; + } + } + fakeFloor = NULL; + floorplane = backupfp; + ceilingplane = backupcp; + } + + basecolormap = frontsector->ColorMap; + floorlightlevel = fll; + ceilinglightlevel = cll; + + // killough 9/18/98: Fix underwater slowdown, by passing real sector + // instead of fake one. Improve sprite lighting by basing sprite + // lightlevels on floor & ceiling lightlevels in the surrounding area. + // [RH] Handle sprite lighting like Duke 3D: If the ceiling is a sky, sprites are lit by + // it, otherwise they are lit by the floor. + R_AddSprites(sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ? ceilinglightlevel : floorlightlevel, FakeSide); + + // [RH] Add particles + if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors) + { // Only do it for the main BSP. + int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel) / 2 + r_actualextralight); + for (WORD i = ParticlesInSubsec[(unsigned int)(sub - subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) + { + R_ProjectParticle(Particles + i, subsectors[sub - subsectors].sector, shade, FakeSide); + } + } + + count = sub->numlines; + line = sub->firstline; + + while (count--) { - tempsec->floorplane = sec->floorplane; - tempsec->ceilingplane = s->floorplane; - tempsec->ceilingplane.FlipVert (); - tempsec->ceilingplane.ChangeHeight(-1 / 65536.); - tempsec->ColorMap = s->ColorMap; - } - - // killough 11/98: prevent sudden light changes from non-water sectors: - if ((underwater && !backline) || doorunderwater) - { // head-below-floor hack - tempsec->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; - - tempsec->ceilingplane = s->floorplane; - tempsec->ceilingplane.FlipVert (); - tempsec->ceilingplane.ChangeHeight (-1 / 65536.); - if (s->GetTexture(sector_t::ceiling) == skyflatnum) + if (!outersubsector || line->sidedef == NULL || !(line->sidedef->Flags & WALLF_POLYOBJ)) { - tempsec->floorplane = tempsec->ceilingplane; - tempsec->floorplane.FlipVert (); - tempsec->floorplane.ChangeHeight (+1 / 65536.); - tempsec->SetTexture(sector_t::ceiling, tempsec->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform; - } - else - { - tempsec->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false); - tempsec->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform; - } - - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) - { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) + // kg3D - fake planes bounding calculation + if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) { - *floorlightlevel = s->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = s->GetCeilingLight (); + backupfp = floorplane; + backupcp = ceilingplane; + floorplane = NULL; + ceilingplane = NULL; + for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) + { + fakeFloor = line->backsector->e->XFloor.ffloors[i]; + if (!(fakeFloor->flags & FF_EXISTS)) continue; + if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; + if (!fakeFloor->model) continue; + fake3D = FAKE3D_FAKEBACK; + tempsec = *fakeFloor->model; + tempsec.floorplane = *fakeFloor->top.plane; + tempsec.ceilingplane = *fakeFloor->bottom.plane; + if (fakeFloor->validcount != validcount) + { + fakeFloor->validcount = validcount; + R_3D_NewClip(); + } + renderline.Render(line, InSubsector, frontsector, &tempsec, floorplane, ceilingplane); // fake + } + fakeFloor = NULL; + fake3D = 0; + floorplane = backupfp; + ceilingplane = backupcp; } + renderline.Render(line, InSubsector, frontsector, nullptr, floorplane, ceilingplane); // now real } - FakeSide = FAKED_BelowFloor; + line++; } - else if (heightsec && heightsec->ceilingplane.PointOnSide(ViewPos) <= 0 && - orgceilz > refceilz && !(s->MoreFlags & SECF_FAKEFLOORONLY)) - { // Above-ceiling hack - tempsec->ceilingplane = s->ceilingplane; - tempsec->floorplane = s->ceilingplane; - tempsec->floorplane.FlipVert (); - tempsec->floorplane.ChangeHeight (+1 / 65536.); - tempsec->ColorMap = s->ColorMap; - tempsec->ColorMap = s->ColorMap; - - tempsec->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false); - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false); - tempsec->planes[sector_t::ceiling].xform = tempsec->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform; - - if (s->GetTexture(sector_t::floor) != skyflatnum) - { - tempsec->ceilingplane = sec->ceilingplane; - tempsec->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); - tempsec->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; - } - - if (!(s->MoreFlags & SECF_NOFAKELIGHT)) - { - tempsec->lightlevel = s->lightlevel; - - if (floorlightlevel != NULL) - { - *floorlightlevel = s->GetFloorLight (); - } - - if (ceilinglightlevel != NULL) - { - *ceilinglightlevel = s->GetCeilingLight (); - } - } - FakeSide = FAKED_AboveCeiling; - } - sec = tempsec; // Use other sector - } - return sec; -} - - - -// Checks BSP node/subtree bounding box. -// Returns true if some part of the bbox might be visible. -static bool R_CheckBBox (float *bspcoord) // killough 1/28/98: static -{ - static const int checkcoord[12][4] = - { - { 3,0,2,1 }, - { 3,0,2,0 }, - { 3,1,2,0 }, - { 0 }, - { 2,0,2,1 }, - { 0,0,0,0 }, - { 3,1,3,0 }, - { 0 }, - { 2,0,3,1 }, - { 2,1,3,1 }, - { 2,1,3,0 } - }; - - int boxx; - int boxy; - int boxpos; - - double x1, y1, x2, y2; - double rx1, ry1, rx2, ry2; - int sx1, sx2; - - // Find the corners of the box - // that define the edges from current viewpoint. - if (ViewPos.X <= bspcoord[BOXLEFT]) - boxx = 0; - else if (ViewPos.X < bspcoord[BOXRIGHT]) - boxx = 1; - else - boxx = 2; - - if (ViewPos.Y >= bspcoord[BOXTOP]) - boxy = 0; - else if (ViewPos.Y > bspcoord[BOXBOTTOM]) - boxy = 1; - else - boxy = 2; - - boxpos = (boxy<<2)+boxx; - if (boxpos == 5) - return true; - - x1 = bspcoord[checkcoord[boxpos][0]] - ViewPos.X; - y1 = bspcoord[checkcoord[boxpos][1]] - ViewPos.Y; - x2 = bspcoord[checkcoord[boxpos][2]] - ViewPos.X; - y2 = bspcoord[checkcoord[boxpos][3]] - ViewPos.Y; - - // check clip list for an open space - - // Sitting on a line? - if (y1 * (x1 - x2) + x1 * (y2 - y1) >= -EQUAL_EPSILON) - return true; - - rx1 = x1 * ViewSin - y1 * ViewCos; - rx2 = x2 * ViewSin - y2 * ViewCos; - ry1 = x1 * ViewTanCos + y1 * ViewTanSin; - ry2 = x2 * ViewTanCos + y2 * ViewTanSin; - - if (MirrorFlags & RF_XFLIP) - { - double t = -rx1; - rx1 = -rx2; - rx2 = t; - swapvalues(ry1, ry2); - } - - if (rx1 >= -ry1) - { - if (rx1 > ry1) return false; // left edge is off the right side - if (ry1 == 0) return false; - sx1 = xs_RoundToInt(CenterX + rx1 * CenterX / ry1); - } - else - { - if (rx2 < -ry2) return false; // wall is off the left side - if (rx1 - rx2 - ry2 + ry1 == 0) return false; // wall does not intersect view volume - sx1 = 0; - } - - if (rx2 <= ry2) - { - if (rx2 < -ry2) return false; // right edge is off the left side - if (ry2 == 0) return false; - sx2 = xs_RoundToInt(CenterX + rx2 * CenterX / ry2); - } - else - { - if (rx1 > ry1) return false; // wall is off the right side - if (ry2 - ry1 - rx2 + rx1 == 0) return false; // wall does not intersect view volume - sx2 = viewwidth; - } - - // Find the first clippost that touches the source post - // (adjacent pixels are touching). - - return R_IsWallSegmentVisible(sx1, sx2); -} - - -void R_Subsector (subsector_t *sub); -static void R_AddPolyobjs(subsector_t *sub) -{ - if (sub->BSP == NULL || sub->BSP->bDirty) - { - sub->BuildPolyBSP(); - } - if (sub->BSP->Nodes.Size() == 0) - { - R_Subsector(&sub->BSP->Subsectors[0]); - } - else - { - R_RenderBSPNode(&sub->BSP->Nodes.Last()); - } -} - -// kg3D - add fake segs, never rendered -void R_FakeDrawLoop(subsector_t *sub, visplane_t *floorplane, visplane_t *ceilingplane) -{ - int count; - seg_t* line; - - count = sub->numlines; - line = sub->firstline; - - while (count--) - { - if ((line->sidedef) && !(line->sidedef->Flags & WALLF_POLYOBJ)) - { - render_line.Render(line, InSubsector, frontsector, nullptr, floorplane, ceilingplane); - } - line++; - } -} - -// -// R_Subsector -// Determine floor/ceiling planes. -// Add sprites of things in sector. -// Draw one or more line segments. -// -void R_Subsector (subsector_t *sub) -{ - int count; - seg_t* line; - sector_t tempsec; // killough 3/7/98: deep water hack - int floorlightlevel; // killough 3/16/98: set floor lightlevel - int ceilinglightlevel; // killough 4/11/98 - bool outersubsector; - int fll, cll, position; - FSectorPortal *portal; - - // kg3D - fake floor stuff - visplane_t *backupfp; - visplane_t *backupcp; - //secplane_t templane; - lightlist_t *light; - - if (InSubsector != NULL) - { // InSubsector is not NULL. This means we are rendering from a mini-BSP. - outersubsector = false; - } - else - { - outersubsector = true; - InSubsector = sub; - } - -#ifdef RANGECHECK - if (outersubsector && sub - subsectors >= (ptrdiff_t)numsubsectors) - I_Error ("R_Subsector: ss %ti with numss = %i", sub - subsectors, numsubsectors); -#endif - - assert(sub->sector != NULL); - - if (sub->polys) - { // Render the polyobjs in the subsector first - R_AddPolyobjs(sub); if (outersubsector) { InSubsector = NULL; } - return; } - frontsector = sub->sector; - frontsector->MoreFlags |= SECF_DRAWN; - count = sub->numlines; - line = sub->firstline; - - // killough 3/8/98, 4/4/98: Deep water / fake ceiling effect - frontsector = R_FakeFlat(frontsector, &tempsec, &floorlightlevel, &ceilinglightlevel, nullptr, 0, 0, 0, 0); - - fll = floorlightlevel; - cll = ceilinglightlevel; - - // [RH] set foggy flag - foggy = level.fadeto || frontsector->ColorMap->Fade || (level.flags & LEVEL_HASFADETABLE); - r_actualextralight = foggy ? 0 : extralight << 4; - - // kg3D - fake lights - if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) + void RenderBSP::RenderScene() { - light = P_GetPlaneLight(frontsector, &frontsector->ceilingplane, false); - basecolormap = light->extra_colormap; - // If this is the real ceiling, don't discard plane lighting R_FakeFlat() - // accounted for. - if (light->p_lightlevel != &frontsector->lightlevel) + InSubsector = nullptr; + RenderBSPNode(nodes + numnodes - 1); // The head node is the last node output. + } + + // + // RenderBSPNode + // Renders all subsectors below a given node, traversing subtree recursively. + // Just call with BSP root and -1. + // killough 5/2/98: reformatted, removed tail recursion + + void RenderBSP::RenderBSPNode(void *node) + { + if (numnodes == 0) { - ceilinglightlevel = *light->p_lightlevel; - } - } - else - { - basecolormap = (r_fullbrightignoresectorcolor && fixedlightlev >= 0) ? &FullNormalLight : frontsector->ColorMap; - } - - portal = frontsector->ValidatePortal(sector_t::ceiling); - - visplane_t *ceilingplane = frontsector->ceilingplane.PointOnSide(ViewPos) > 0 || - frontsector->GetTexture(sector_t::ceiling) == skyflatnum || - portal != NULL || - (frontsector->heightsec && - !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && - frontsector->heightsec->GetTexture(sector_t::floor) == skyflatnum) ? - R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 - frontsector->GetTexture(sector_t::ceiling), - ceilinglightlevel + r_actualextralight, // killough 4/11/98 - frontsector->GetAlpha(sector_t::ceiling), - !!(frontsector->GetFlags(sector_t::ceiling) & PLANEF_ADDITIVE), - frontsector->planes[sector_t::ceiling].xform, - frontsector->sky, - portal - ) : NULL; - - if (ceilingplane) - R_AddPlaneLights(ceilingplane, frontsector->lighthead); - - if (fixedlightlev < 0 && frontsector->e && frontsector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(frontsector, &frontsector->floorplane, false); - basecolormap = light->extra_colormap; - // If this is the real floor, don't discard plane lighting R_FakeFlat() - // accounted for. - if (light->p_lightlevel != &frontsector->lightlevel) - { - floorlightlevel = *light->p_lightlevel; - } - } - else - { - basecolormap = (r_fullbrightignoresectorcolor && fixedlightlev >= 0) ? &FullNormalLight : frontsector->ColorMap; - } - - // killough 3/7/98: Add (x,y) offsets to flats, add deep water check - // killough 3/16/98: add floorlightlevel - // killough 10/98: add support for skies transferred from sidedefs - portal = frontsector->ValidatePortal(sector_t::floor); - - visplane_t *floorplane = frontsector->floorplane.PointOnSide(ViewPos) > 0 || // killough 3/7/98 - frontsector->GetTexture(sector_t::floor) == skyflatnum || - portal != NULL || - (frontsector->heightsec && - !(frontsector->heightsec->MoreFlags & SECF_IGNOREHEIGHTSEC) && - frontsector->heightsec->GetTexture(sector_t::ceiling) == skyflatnum) ? - R_FindPlane(frontsector->floorplane, - frontsector->GetTexture(sector_t::floor), - floorlightlevel + r_actualextralight, // killough 3/16/98 - frontsector->GetAlpha(sector_t::floor), - !!(frontsector->GetFlags(sector_t::floor) & PLANEF_ADDITIVE), - frontsector->planes[sector_t::floor].xform, - frontsector->sky, - portal - ) : NULL; - - if (floorplane) - R_AddPlaneLights(floorplane, frontsector->lighthead); - - // kg3D - fake planes rendering - if (r_3dfloors && frontsector->e && frontsector->e->XFloor.ffloors.Size()) - { - backupfp = floorplane; - backupcp = ceilingplane; - // first check all floors - for (int i = 0; i < (int)frontsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = frontsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!fakeFloor->model) continue; - if (fakeFloor->bottom.plane->isSlope()) continue; - if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES|FF_RENDERSIDES))) - { - R_3D_AddHeight(fakeFloor->top.plane, frontsector); - } - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (fakeFloor->alpha == 0) continue; - if (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR) continue; - fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE); - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - double fakeHeight = fakeFloor->top.plane->ZatPoint(frontsector->centerspot); - if (fakeHeight < ViewPos.Z && - fakeHeight > frontsector->floorplane.ZatPoint(frontsector->centerspot)) - { - fake3D = FAKE3D_FAKEFLOOR; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - if (!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) - { - tempsec.SetTexture(sector_t::floor, tempsec.GetTexture(sector_t::ceiling)); - position = sector_t::ceiling; - } else position = sector_t::floor; - frontsector = &tempsec; - - if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(sub->sector, &frontsector->floorplane, false); - basecolormap = light->extra_colormap; - floorlightlevel = *light->p_lightlevel; - } - - ceilingplane = NULL; - floorplane = R_FindPlane(frontsector->floorplane, - frontsector->GetTexture(sector_t::floor), - floorlightlevel + r_actualextralight, // killough 3/16/98 - frontsector->GetAlpha(sector_t::floor), - !!(fakeFloor->flags & FF_ADDITIVETRANS), - frontsector->planes[position].xform, - frontsector->sky, - NULL); - - if (floorplane) - R_AddPlaneLights(floorplane, frontsector->lighthead); - - R_FakeDrawLoop(sub, floorplane, ceilingplane); - fake3D = 0; - frontsector = sub->sector; - } - } - // and now ceilings - for (unsigned int i = 0; i < frontsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = frontsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!fakeFloor->model) continue; - if (fakeFloor->top.plane->isSlope()) continue; - if (!(fakeFloor->flags & FF_NOSHADE) || (fakeFloor->flags & (FF_RENDERPLANES|FF_RENDERSIDES))) - { - R_3D_AddHeight(fakeFloor->bottom.plane, frontsector); - } - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (fakeFloor->alpha == 0) continue; - if (!(fakeFloor->flags & FF_THISINSIDE) && (fakeFloor->flags & (FF_SWIMMABLE|FF_INVERTSECTOR)) == (FF_SWIMMABLE|FF_INVERTSECTOR)) continue; - fakeAlpha = MIN(Scale(fakeFloor->alpha, OPAQUE, 255), OPAQUE); - - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - double fakeHeight = fakeFloor->bottom.plane->ZatPoint(frontsector->centerspot); - if (fakeHeight > ViewPos.Z && - fakeHeight < frontsector->ceilingplane.ZatPoint(frontsector->centerspot)) - { - fake3D = FAKE3D_FAKECEILING; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - if ((!(fakeFloor->flags & FF_THISINSIDE) && !(fakeFloor->flags & FF_INVERTSECTOR)) || - (fakeFloor->flags & FF_THISINSIDE && fakeFloor->flags & FF_INVERTSECTOR)) - { - tempsec.SetTexture(sector_t::ceiling, tempsec.GetTexture(sector_t::floor)); - position = sector_t::floor; - } else position = sector_t::ceiling; - frontsector = &tempsec; - - tempsec.ceilingplane.ChangeHeight(-1 / 65536.); - if (fixedlightlev < 0 && sub->sector->e->XFloor.lightlist.Size()) - { - light = P_GetPlaneLight(sub->sector, &frontsector->ceilingplane, false); - basecolormap = light->extra_colormap; - ceilinglightlevel = *light->p_lightlevel; - } - tempsec.ceilingplane.ChangeHeight(1 / 65536.); - - floorplane = NULL; - ceilingplane = R_FindPlane(frontsector->ceilingplane, // killough 3/8/98 - frontsector->GetTexture(sector_t::ceiling), - ceilinglightlevel + r_actualextralight, // killough 4/11/98 - frontsector->GetAlpha(sector_t::ceiling), - !!(fakeFloor->flags & FF_ADDITIVETRANS), - frontsector->planes[position].xform, - frontsector->sky, - NULL); - - if (ceilingplane) - R_AddPlaneLights(ceilingplane, frontsector->lighthead); - - R_FakeDrawLoop(sub, floorplane, ceilingplane); - fake3D = 0; - frontsector = sub->sector; - } - } - fakeFloor = NULL; - floorplane = backupfp; - ceilingplane = backupcp; - } - - basecolormap = frontsector->ColorMap; - floorlightlevel = fll; - ceilinglightlevel = cll; - - // killough 9/18/98: Fix underwater slowdown, by passing real sector - // instead of fake one. Improve sprite lighting by basing sprite - // lightlevels on floor & ceiling lightlevels in the surrounding area. - // [RH] Handle sprite lighting like Duke 3D: If the ceiling is a sky, sprites are lit by - // it, otherwise they are lit by the floor. - R_AddSprites (sub->sector, frontsector->GetTexture(sector_t::ceiling) == skyflatnum ? ceilinglightlevel : floorlightlevel, FakeSide); - - // [RH] Add particles - if ((unsigned int)(sub - subsectors) < (unsigned int)numsubsectors) - { // Only do it for the main BSP. - int shade = LIGHT2SHADE((floorlightlevel + ceilinglightlevel)/2 + r_actualextralight); - for (WORD i = ParticlesInSubsec[(unsigned int)(sub-subsectors)]; i != NO_PARTICLE; i = Particles[i].snext) - { - R_ProjectParticle (Particles + i, subsectors[sub-subsectors].sector, shade, FakeSide); - } - } - - count = sub->numlines; - line = sub->firstline; - - while (count--) - { - if (!outersubsector || line->sidedef == NULL || !(line->sidedef->Flags & WALLF_POLYOBJ)) - { - // kg3D - fake planes bounding calculation - if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size()) - { - backupfp = floorplane; - backupcp = ceilingplane; - floorplane = NULL; - ceilingplane = NULL; - for (unsigned int i = 0; i < line->backsector->e->XFloor.ffloors.Size(); i++) - { - fakeFloor = line->backsector->e->XFloor.ffloors[i]; - if (!(fakeFloor->flags & FF_EXISTS)) continue; - if (!(fakeFloor->flags & FF_RENDERPLANES)) continue; - if (!fakeFloor->model) continue; - fake3D = FAKE3D_FAKEBACK; - tempsec = *fakeFloor->model; - tempsec.floorplane = *fakeFloor->top.plane; - tempsec.ceilingplane = *fakeFloor->bottom.plane; - if (fakeFloor->validcount != validcount) - { - fakeFloor->validcount = validcount; - R_3D_NewClip(); - } - render_line.Render(line, InSubsector, frontsector, &tempsec, floorplane, ceilingplane); // fake - } - fakeFloor = NULL; - fake3D = 0; - floorplane = backupfp; - ceilingplane = backupcp; - } - render_line.Render(line, InSubsector, frontsector, nullptr, floorplane, ceilingplane); // now real - } - line++; - } - if (outersubsector) - { - InSubsector = NULL; - } -} - -void R_RenderScene() -{ - InSubsector = nullptr; - R_RenderBSPNode(nodes + numnodes - 1); // The head node is the last node output. -} - -// -// RenderBSPNode -// Renders all subsectors below a given node, traversing subtree recursively. -// Just call with BSP root and -1. -// killough 5/2/98: reformatted, removed tail recursion - -void R_RenderBSPNode (void *node) -{ - if (numnodes == 0) - { - R_Subsector (subsectors); - return; - } - while (!((size_t)node & 1)) // Keep going until found a subsector - { - node_t *bsp = (node_t *)node; - - // Decide which side the view point is on. - int side = R_PointOnSide (ViewPos, bsp); - - // Recursively divide front space (toward the viewer). - R_RenderBSPNode (bsp->children[side]); - - // Possibly divide back space (away from the viewer). - side ^= 1; - if (!R_CheckBBox (bsp->bbox[side])) + RenderSubsector(subsectors); return; + } + while (!((size_t)node & 1)) // Keep going until found a subsector + { + node_t *bsp = (node_t *)node; - node = bsp->children[side]; + // Decide which side the view point is on. + int side = R_PointOnSide(ViewPos, bsp); + + // Recursively divide front space (toward the viewer). + RenderBSPNode(bsp->children[side]); + + // Possibly divide back space (away from the viewer). + side ^= 1; + if (!CheckBBox(bsp->bbox[side])) + return; + + node = bsp->children[side]; + } + RenderSubsector((subsector_t *)((BYTE *)node - 1)); } - R_Subsector ((subsector_t *)((BYTE *)node - 1)); -} + void RenderBSP::ClearClip() + { + // clip ceiling to console bottom + fillshort(floorclip, viewwidth, viewheight); + fillshort(ceilingclip, viewwidth, !screen->Accel2D && ConBottom > viewwindowy && !bRenderingToCanvas ? (ConBottom - viewwindowy) : 0); + } } diff --git a/src/swrenderer/scene/r_bsp.h b/src/swrenderer/scene/r_bsp.h index ddd1f8bad4..1cc149792b 100644 --- a/src/swrenderer/scene/r_bsp.h +++ b/src/swrenderer/scene/r_bsp.h @@ -1,7 +1,3 @@ -// Emacs style mode select -*- C++ -*- -//----------------------------------------------------------------------------- -// -// $Id:$ // // Copyright (C) 1993-1996 by id Software, Inc. // @@ -14,47 +10,57 @@ // FITNESS FOR A PARTICULAR PURPOSE. See the DOOM Source Code License // for more details. // -// DESCRIPTION: -// Refresh module, BSP traversal and handling. -// -//----------------------------------------------------------------------------- - -#ifndef __R_BSP__ -#define __R_BSP__ +#pragma once #include "tarray.h" #include #include "r_defs.h" - -EXTERN_CVAR (Bool, r_drawflat) // [RH] Don't texture segs? +#include "swrenderer/line/r_line.h" namespace swrenderer { + struct visplane_t; -struct visplane_t; + // The 3072 below is just an arbitrary value picked to avoid + // drawing lines the player is too close to that would overflow + // the texture calculations. + #define TOO_CLOSE_Z (3072.0 / (1<<12)) -// The 3072 below is just an arbitrary value picked to avoid -// drawing lines the player is too close to that would overflow -// the texture calculations. -#define TOO_CLOSE_Z (3072.0 / (1<<12)) + enum + { + FAKED_Center, + FAKED_BelowFloor, + FAKED_AboveCeiling + }; -enum -{ - FAKED_Center, - FAKED_BelowFloor, - FAKED_AboveCeiling -}; + class RenderBSP + { + public: + static RenderBSP *Instance(); -void R_RenderScene(); -void R_RenderBSPNode (void *node); + void ClearClip(); + void RenderScene(); -// killough 4/13/98: fake floors/ceilings for deep water / fake ceilings: -sector_t *R_FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, seg_t *backline, int backx1, int backx2, double frontcz1, double frontcz2); + void ResetFakingUnderwater() { r_fakingunderwater = false; } + sector_t *FakeFlat(sector_t *sec, sector_t *tempsec, int *floorlightlevel, int *ceilinglightlevel, seg_t *backline, int backx1, int backx2, double frontcz1, double frontcz2); -extern short floorclip[MAXWIDTH]; -extern short ceilingclip[MAXWIDTH]; + short floorclip[MAXWIDTH]; + short ceilingclip[MAXWIDTH]; + private: + void RenderBSPNode(void *node); + void RenderSubsector(subsector_t *sub); + + bool CheckBBox(float *bspcoord); + void AddPolyobjs(subsector_t *sub); + void FakeDrawLoop(subsector_t *sub, visplane_t *floorplane, visplane_t *ceilingplane); + + subsector_t *InSubsector; + sector_t *frontsector; + uint8_t FakeSide; + bool r_fakingunderwater; + + SWRenderLine renderline; + }; } - -#endif diff --git a/src/swrenderer/scene/r_portal.cpp b/src/swrenderer/scene/r_portal.cpp index dd8342a683..794f11eca9 100644 --- a/src/swrenderer/scene/r_portal.cpp +++ b/src/swrenderer/scene/r_portal.cpp @@ -204,6 +204,8 @@ namespace swrenderer WindowLeft = pl->left; WindowRight = pl->right; + auto ceilingclip = RenderBSP::Instance()->ceilingclip; + auto floorclip = RenderBSP::Instance()->floorclip; for (i = pl->left; i < pl->right; i++) { if (pl->top[i] == 0x7fff) @@ -249,7 +251,7 @@ namespace swrenderer viewposStack.Push(ViewPos); visplaneStack.Push(pl); - R_RenderScene(); + RenderBSP::Instance()->RenderScene(); R_3D_ResetClip(); // reset clips (floor/ceiling) R_DrawPlanes(); @@ -464,11 +466,13 @@ namespace swrenderer R_3D_EnterSkybox(); // push 3D floor height map CurrentPortalInSkybox = false; // first portal in a skybox should set this variable to false for proper clipping in skyboxes. - // first pass, set clipping + // first pass, set clipping + auto ceilingclip = RenderBSP::Instance()->ceilingclip; + auto floorclip = RenderBSP::Instance()->floorclip; memcpy(ceilingclip + pds->x1, &pds->ceilingclip[0], pds->len * sizeof(*ceilingclip)); memcpy(floorclip + pds->x1, &pds->floorclip[0], pds->len * sizeof(*floorclip)); - R_RenderScene(); + RenderBSP::Instance()->RenderScene(); R_3D_ResetClip(); // reset clips (floor/ceiling) if (!savedvisibility && camera) camera->renderflags &= ~RF_INVISIBLE; diff --git a/src/swrenderer/segments/r_drawsegment.cpp b/src/swrenderer/segments/r_drawsegment.cpp index 7884415dee..2887a26f77 100644 --- a/src/swrenderer/segments/r_drawsegment.cpp +++ b/src/swrenderer/segments/r_drawsegment.cpp @@ -162,7 +162,7 @@ namespace swrenderer } // killough 4/13/98: get correct lightlevel for 2s normal textures - sec = R_FakeFlat(frontsector, &tempsec, nullptr, nullptr, nullptr, 0, 0, 0, 0); + sec = RenderBSP::Instance()->FakeFlat(frontsector, &tempsec, nullptr, nullptr, nullptr, 0, 0, 0, 0); basecolormap = sec->ColorMap; // [RH] Set basecolormap diff --git a/src/swrenderer/things/r_decal.cpp b/src/swrenderer/things/r_decal.cpp index 3acc73d62d..ba08cfd016 100644 --- a/src/swrenderer/things/r_decal.cpp +++ b/src/swrenderer/things/r_decal.cpp @@ -170,7 +170,7 @@ namespace swrenderer else if (pass == 0) { mceilingclip = walltop; - mfloorclip = ceilingclip; + mfloorclip = RenderBSP::Instance()->ceilingclip; needrepeat = 1; } else @@ -186,7 +186,7 @@ namespace swrenderer goto done; } mceilingclip = walltop; - mfloorclip = ceilingclip; + mfloorclip = RenderBSP::Instance()->ceilingclip; break; case RF_CLIPMID: @@ -203,7 +203,7 @@ namespace swrenderer { goto done; } - mceilingclip = floorclip; + mceilingclip = RenderBSP::Instance()->floorclip; mfloorclip = wallbottom; break; } @@ -296,7 +296,7 @@ namespace swrenderer // If this sprite is RF_CLIPFULL on a two-sided line, needrepeat will // be set 1 if we need to draw on the lower wall. In all other cases, // needrepeat will be 0, and the while will fail. - mceilingclip = floorclip; + mceilingclip = RenderBSP::Instance()->floorclip; mfloorclip = wallbottom; R_FinishSetPatchStyle(); } while (needrepeat--); diff --git a/src/swrenderer/things/r_particle.cpp b/src/swrenderer/things/r_particle.cpp index d58e625c4f..9878d362b6 100644 --- a/src/swrenderer/things/r_particle.cpp +++ b/src/swrenderer/things/r_particle.cpp @@ -114,6 +114,8 @@ namespace swrenderer // entered, we don't need to clip it to drawsegs like a normal sprite. // Clip particles behind walls. + auto ceilingclip = RenderBSP::Instance()->ceilingclip; + auto floorclip = RenderBSP::Instance()->floorclip; if (y1 < ceilingclip[x1]) y1 = ceilingclip[x1]; if (y1 < ceilingclip[x2 - 1]) y1 = ceilingclip[x2 - 1]; if (y2 >= floorclip[x1]) y2 = floorclip[x1] - 1; diff --git a/src/swrenderer/things/r_playersprite.cpp b/src/swrenderer/things/r_playersprite.cpp index 5dad94c707..1316a40ca2 100644 --- a/src/swrenderer/things/r_playersprite.cpp +++ b/src/swrenderer/things/r_playersprite.cpp @@ -123,7 +123,7 @@ namespace swrenderer else { // This used to use camera->Sector but due to interpolation that can be incorrect // when the interpolated viewpoint is in a different sector than the camera. - sec = R_FakeFlat(viewsector, &tempsec, &floorlight, &ceilinglight, nullptr, 0, 0, 0, 0); + sec = RenderBSP::Instance()->FakeFlat(viewsector, &tempsec, &floorlight, &ceilinglight, nullptr, 0, 0, 0, 0); // [RH] set basecolormap basecolormap = sec->ColorMap;