// //--------------------------------------------------------------------------- // // Copyright(C) 2001-2016 Christoph Oelckers // All rights reserved. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser 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 Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see http://www.gnu.org/licenses/ // //-------------------------------------------------------------------------- // /* ** gl_fakeflat.cpp ** Fake flat functions to render stacked sectors ** */ #include "p_lnspec.h" #include "p_local.h" #include "g_levellocals.h" #include "a_sharedglobal.h" #include "d_player.h" #include "r_sky.h" #include "hw_fakeflat.h" #include "hw_drawinfo.h" #include "hw_cvars.h" #include "r_utility.h" #include "texturemanager.h" static sector_t **fakesectorbuffer; extern thread_local bool isWorkerThread; //========================================================================== // // Check whether the player can look beyond this line // //========================================================================== bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector) { line_t *linedef = sidedef->linedef; double bs_floorheight1; double bs_floorheight2; double bs_ceilingheight1; double bs_ceilingheight2; double fs_floorheight1; double fs_floorheight2; double fs_ceilingheight1; double fs_ceilingheight2; // Mirrors and horizons always block the view //if (linedef->special==Line_Mirror || linedef->special==Line_Horizon) return true; // Lines with portals must never block. // Portals which require the sky flat are excluded here, because for them the special sky semantics apply. if (!(frontsector->GetPortal(sector_t::ceiling)->mFlags & PORTSF_SKYFLATONLY) || !(frontsector->GetPortal(sector_t::floor)->mFlags & PORTSF_SKYFLATONLY) || !(backsector->GetPortal(sector_t::ceiling)->mFlags & PORTSF_SKYFLATONLY) || !(backsector->GetPortal(sector_t::floor)->mFlags & PORTSF_SKYFLATONLY)) { return false; } // on large levels this distinction can save some time // That's a lot of avoided multiplications if there's a lot to see! if (frontsector->ceilingplane.isSlope()) { fs_ceilingheight1 = frontsector->ceilingplane.ZatPoint(linedef->v1); fs_ceilingheight2 = frontsector->ceilingplane.ZatPoint(linedef->v2); } else { fs_ceilingheight2 = fs_ceilingheight1 = frontsector->ceilingplane.fD(); } if (frontsector->floorplane.isSlope()) { fs_floorheight1 = frontsector->floorplane.ZatPoint(linedef->v1); fs_floorheight2 = frontsector->floorplane.ZatPoint(linedef->v2); } else { fs_floorheight2 = fs_floorheight1 = -frontsector->floorplane.fD(); } if (backsector->ceilingplane.isSlope()) { bs_ceilingheight1 = backsector->ceilingplane.ZatPoint(linedef->v1); bs_ceilingheight2 = backsector->ceilingplane.ZatPoint(linedef->v2); } else { bs_ceilingheight2 = bs_ceilingheight1 = backsector->ceilingplane.fD(); } if (backsector->floorplane.isSlope()) { bs_floorheight1 = backsector->floorplane.ZatPoint(linedef->v1); bs_floorheight2 = backsector->floorplane.ZatPoint(linedef->v2); } else { bs_floorheight2 = bs_floorheight1 = -backsector->floorplane.fD(); } // now check for closed sectors! if (bs_ceilingheight1 <= fs_floorheight1 && bs_ceilingheight2 <= fs_floorheight2) { auto tex = TexMan.GetGameTexture(sidedef->GetTexture(side_t::top), true); if (!tex || !tex->isValid()) return false; if (backsector->GetTexture(sector_t::ceiling) == skyflatnum && frontsector->GetTexture(sector_t::ceiling) == skyflatnum) return false; return true; } if (fs_ceilingheight1 <= bs_floorheight1 && fs_ceilingheight2 <= bs_floorheight2) { auto tex = TexMan.GetGameTexture(sidedef->GetTexture(side_t::bottom), true); if (!tex || !tex->isValid()) return false; // properly render skies (consider door "open" if both floors are sky): if (backsector->GetTexture(sector_t::ceiling) == skyflatnum && frontsector->GetTexture(sector_t::ceiling) == skyflatnum) return false; return true; } if (bs_ceilingheight1 <= bs_floorheight1 && bs_ceilingheight2 <= bs_floorheight2) { // preserve a kind of transparent door/lift special effect: if (bs_ceilingheight1 < fs_ceilingheight1 || bs_ceilingheight2 < fs_ceilingheight2) { auto tex = TexMan.GetGameTexture(sidedef->GetTexture(side_t::top), true); if (!tex || !tex->isValid()) return false; } if (bs_floorheight1 > fs_floorheight1 || bs_floorheight2 > fs_floorheight2) { auto tex = TexMan.GetGameTexture(sidedef->GetTexture(side_t::bottom), true); if (!tex || !tex->isValid()) return false; } if (backsector->GetTexture(sector_t::ceiling) == skyflatnum && frontsector->GetTexture(sector_t::ceiling) == skyflatnum) return false; if (backsector->GetTexture(sector_t::floor) == skyflatnum && frontsector->GetTexture(sector_t::floor) == skyflatnum) return false; return true; } return false; } //========================================================================== // // check for levels with exposed lower areas. May only be called if actual // state is not known yet! // //========================================================================== area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector) { if (backsector->GetHeightSec() && !frontsector->GetHeightSec()) { sector_t * s = backsector->heightsec; double cz1 = frontsector->ceilingplane.ZatPoint(v1); double cz2 = frontsector->ceilingplane.ZatPoint(v2); double fz1 = s->floorplane.ZatPoint(v1); double fz2 = s->floorplane.ZatPoint(v2); // allow some tolerance in case slopes are involved if (cz1 <= fz1 + 1. / 100 && cz2 <= fz2 + 1. / 100) return area_below; else return area_normal; } return area_default; } //========================================================================== // // // //========================================================================== static FMemArena FakeSectorAllocator(20 * sizeof(sector_t)); static sector_t *allocateSector(sector_t *sec) { if (fakesectorbuffer == nullptr) { unsigned numsectors = sec->Level->sectors.Size(); fakesectorbuffer = (sector_t**)FakeSectorAllocator.Alloc(numsectors * sizeof(sector_t*)); memset(fakesectorbuffer, 0, numsectors * sizeof(sector_t*)); } auto sectornum = sec->sectornum; fakesectorbuffer[sectornum] = (sector_t*)FakeSectorAllocator.Alloc(sizeof(sector_t)); return fakesectorbuffer[sectornum]; } //========================================================================== // // This is mostly like R_FakeFlat but with a few alterations necessitated // by hardware rendering // //========================================================================== sector_t * hw_FakeFlat(sector_t * sec, area_t in_area, bool back, sector_t *localcopy) { if (!sec->GetHeightSec() || sec->heightsec==sec) { // check for backsectors with the ceiling lower than the floor. These will create // visual glitches because upper amd lower textures overlap. if (back && (sec->MoreFlags & SECMF_OVERLAPPING)) { if (fakesectorbuffer && fakesectorbuffer[sec->sectornum]) return fakesectorbuffer[sec->sectornum]; auto dest = localcopy? localcopy : allocateSector(sec); *dest = *sec; dest->floorplane = sec->ceilingplane; dest->floorplane.FlipVert(); dest->planes[sector_t::floor].TexZ = dest->planes[sector_t::ceiling].TexZ; dest->ClearPortal(sector_t::ceiling); dest->ClearPortal(sector_t::floor); return dest; } return sec; } #ifdef _DEBUG if (sec->sectornum==560) { int a = 0; } #endif if (fakesectorbuffer && fakesectorbuffer[sec->sectornum]) { return fakesectorbuffer[sec->sectornum]; } assert(!(isWorkerThread && localcopy == nullptr)); if (in_area==area_above) { if (sec->heightsec->MoreFlags&SECMF_FAKEFLOORONLY /*|| sec->GetTexture(sector_t::ceiling)==skyflatnum*/) in_area=area_normal; } int diffTex = (sec->heightsec->MoreFlags & SECMF_CLIPFAKEPLANES); sector_t * s = sec->heightsec; auto dest = localcopy ? localcopy : allocateSector(sec); *dest = *sec; // Replace floor and ceiling height with control sector's heights. if (diffTex) { if (s->floorplane.CopyPlaneIfValid (&dest->floorplane, &sec->ceilingplane)) { dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); dest->SetPlaneTexZQuick(sector_t::floor, s->GetPlaneTexZ(sector_t::floor)); dest->iboindex[sector_t::floor] = sec->iboindex[sector_t::vbo_fakefloor]; for (int n = 0; n < screen->mPipelineNbr; n++) dest->vboheight[n][sector_t::floor] = s->vboheight[n][sector_t::floor]; } else if (s->MoreFlags & SECMF_FAKEFLOORONLY) { if (in_area==area_below) { dest->CopyColors(s); if (!(s->MoreFlags & SECMF_NOFAKELIGHT)) { dest->lightlevel = s->lightlevel; dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor)); dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling)); dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor)); dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling)); } return dest; } return sec; } } else { dest->SetPlaneTexZQuick(sector_t::floor, s->GetPlaneTexZ(sector_t::floor)); dest->floorplane = s->floorplane; dest->iboindex[sector_t::floor] = sec->iboindex[sector_t::vbo_fakefloor]; for (int n = 0; n < screen->mPipelineNbr; n++) dest->vboheight[n][sector_t::floor] = s->vboheight[n][sector_t::floor]; } if (!(s->MoreFlags&SECMF_FAKEFLOORONLY)) { if (diffTex) { if (s->ceilingplane.CopyPlaneIfValid (&dest->ceilingplane, &sec->floorplane)) { dest->SetTexture(sector_t::ceiling, s->GetTexture(sector_t::ceiling), false); dest->SetPlaneTexZQuick(sector_t::ceiling, s->GetPlaneTexZ(sector_t::ceiling)); dest->iboindex[sector_t::ceiling] = sec->iboindex[sector_t::vbo_fakeceiling]; for (int n = 0; n < screen->mPipelineNbr; n++) dest->vboheight[n][sector_t::ceiling] = s->vboheight[n][sector_t::ceiling]; } } else { dest->ceilingplane = s->ceilingplane; dest->SetPlaneTexZQuick(sector_t::ceiling, s->GetPlaneTexZ(sector_t::ceiling)); dest->iboindex[sector_t::ceiling] = sec->iboindex[sector_t::vbo_fakeceiling]; for (int n = 0; n < screen->mPipelineNbr; n++) dest->vboheight[n][sector_t::ceiling] = s->vboheight[n][sector_t::ceiling]; } } if (in_area==area_below) { dest->CopyColors(s); dest->SetPlaneTexZQuick(sector_t::floor, sec->GetPlaneTexZ(sector_t::floor)); dest->SetPlaneTexZQuick(sector_t::ceiling, s->GetPlaneTexZ(sector_t::floor)); dest->floorplane=sec->floorplane; dest->ceilingplane=s->floorplane; dest->ceilingplane.FlipVert(); dest->iboindex[sector_t::floor] = sec->iboindex[sector_t::floor]; for (int n = 0; n < screen->mPipelineNbr; n++) dest->vboheight[n][sector_t::floor] = sec->vboheight[n][sector_t::floor]; dest->iboindex[sector_t::ceiling] = sec->iboindex[sector_t::vbo_fakefloor]; for (int n = 0; n < screen->mPipelineNbr; n++) dest->vboheight[n][sector_t::ceiling] = s->vboheight[n][sector_t::floor]; dest->ClearPortal(sector_t::ceiling); if (!(s->MoreFlags & SECMF_NOFAKELIGHT)) { dest->lightlevel = s->lightlevel; } //if (!back) { dest->SetTexture(sector_t::floor, diffTex ? sec->GetTexture(sector_t::floor) : s->GetTexture(sector_t::floor), false); dest->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; //dest->ceilingplane = s->floorplane; if (s->GetTexture(sector_t::ceiling) == skyflatnum) { dest->SetTexture(sector_t::ceiling, dest->GetTexture(sector_t::floor), false); //dest->floorplane = dest->ceilingplane; //dest->floorplane.FlipVert (); //dest->floorplane.ChangeHeight (+1); dest->planes[sector_t::ceiling].xform = dest->planes[sector_t::floor].xform; } else { dest->SetTexture(sector_t::ceiling, diffTex ? s->GetTexture(sector_t::floor) : s->GetTexture(sector_t::ceiling), false); dest->planes[sector_t::ceiling].xform = s->planes[sector_t::ceiling].xform; } if (!(s->MoreFlags & SECMF_NOFAKELIGHT)) { dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor)); dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling)); dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor)); dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling)); } } } else if (in_area == area_above) { dest->CopyColors(s); dest->SetPlaneTexZQuick(sector_t::ceiling, sec->GetPlaneTexZ(sector_t::ceiling)); dest->SetPlaneTexZQuick(sector_t::floor, s->GetPlaneTexZ(sector_t::ceiling)); dest->ceilingplane = sec->ceilingplane; dest->floorplane = s->ceilingplane; dest->floorplane.FlipVert(); dest->iboindex[sector_t::floor] = sec->iboindex[sector_t::vbo_fakeceiling]; for (int n = 0; n < screen->mPipelineNbr; n++) dest->vboheight[n][sector_t::floor] = sec->vboheight[n][sector_t::ceiling]; dest->iboindex[sector_t::ceiling] = sec->iboindex[sector_t::ceiling]; for (int n = 0; n < screen->mPipelineNbr; n++) dest->vboheight[n][sector_t::ceiling] = s->vboheight[n][sector_t::ceiling]; dest->ClearPortal(sector_t::floor); if (!(s->MoreFlags & SECMF_NOFAKELIGHT)) { dest->lightlevel = s->lightlevel; } //if (!back) { dest->SetTexture(sector_t::ceiling, diffTex ? sec->GetTexture(sector_t::ceiling) : s->GetTexture(sector_t::ceiling), false); dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::ceiling), false); dest->planes[sector_t::ceiling].xform = dest->planes[sector_t::floor].xform = s->planes[sector_t::ceiling].xform; if (s->GetTexture(sector_t::floor) != skyflatnum) { dest->SetTexture(sector_t::floor, s->GetTexture(sector_t::floor), false); dest->planes[sector_t::floor].xform = s->planes[sector_t::floor].xform; } if (!(s->MoreFlags & SECMF_NOFAKELIGHT)) { dest->lightlevel = s->lightlevel; dest->SetPlaneLight(sector_t::floor, s->GetPlaneLight(sector_t::floor)); dest->SetPlaneLight(sector_t::ceiling, s->GetPlaneLight(sector_t::ceiling)); dest->ChangeFlags(sector_t::floor, -1, s->GetFlags(sector_t::floor)); dest->ChangeFlags(sector_t::ceiling, -1, s->GetFlags(sector_t::ceiling)); } } } return dest; } void hw_ClearFakeFlat() { FakeSectorAllocator.FreeAll(); fakesectorbuffer = nullptr; }