// //--------------------------------------------------------------------------- // // Copyright(C) 2000-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_renderhacks.cpp ** Handles missing upper and lower textures and self referencing sector hacks ** */ #include "a_sharedglobal.h" #include "a_dynlight.h" #include "r_utility.h" #include "r_sky.h" #include "g_levellocals.h" #include "a_dynlight.h" #include "texturemanager.h" #include "hw_drawinfo.h" #include "hw_drawstructs.h" #include "hwrenderer/utility/hw_clock.h" #include "hw_dynlightdata.h" #include "flatvertices.h" #include "hw_lightbuffer.h" #include "hwrenderer/scene/hw_portal.h" #include "hw_fakeflat.h" //========================================================================== // // Create render list entries from the data generated below // //========================================================================== void HWDrawInfo::DispatchRenderHacks() { TMap::Pair *pair; TMap::Pair *fpair; TMap::Iterator ofi(otherFloorPlanes); HWFlat glflat; glflat.section = nullptr; while (ofi.NextPair(pair)) { auto sec = hw_FakeFlat(&Level->sectors[pair->Key], in_area, false); glflat.ProcessSector(this, sec, SSRF_RENDERFLOOR | SSRF_PLANEHACK); } TMap::Iterator oci(otherCeilingPlanes); while (oci.NextPair(pair)) { auto sec = hw_FakeFlat(&Level->sectors[pair->Key], in_area, false); glflat.ProcessSector(this, sec, SSRF_RENDERCEILING | SSRF_PLANEHACK); } TMap::Iterator ffi(floodFloorSegs); while (ffi.NextPair(fpair)) { auto sec = hw_FakeFlat(&Level->sectors[fpair->Key], in_area, false); glflat.ProcessSector(this, sec, SSRF_RENDERFLOOR | SSRF_FLOODHACK); } TMap::Iterator fci(floodCeilingSegs); while (fci.NextPair(fpair)) { auto sec = hw_FakeFlat(&Level->sectors[fpair->Key], in_area, false); glflat.ProcessSector(this, sec, SSRF_RENDERCEILING | SSRF_FLOODHACK); } } //========================================================================== // // Get the nodes from the render data allocator so we don't have to keep track of them ourselves. // //========================================================================== static gl_subsectorrendernode *NewSubsectorRenderNode() { return (gl_subsectorrendernode*)RenderDataAllocator.Alloc(sizeof(gl_subsectorrendernode)); } static gl_floodrendernode *NewFloodRenderNode() { return (gl_floodrendernode*)RenderDataAllocator.Alloc(sizeof(gl_floodrendernode)); } //========================================================================== // // light setup for render hacks. // This can ignore many of the special checks because // this will never be translucent and never be part of a portal or a 3D floor // //========================================================================== int HWDrawInfo::SetupLightsForOtherPlane(subsector_t * sub, FDynLightData &lightdata, const secplane_t *plane) { if (Level->HasDynamicLights && !isFullbrightScene()) { Plane p; FLightNode * node = sub->section->lighthead; lightdata.Clear(); while (node) { FDynamicLight * light = node->lightsource; if (!light->IsActive()) { node = node->nextLight; continue; } iter_dlightf++; p.Set(plane->Normal(), plane->fD()); draw_dlightf += GetLight(lightdata, sub->sector->PortalGroup, p, light, true); node = node->nextLight; } return screen->mLights->UploadLights(lightdata); } else return -1; } //========================================================================== // // Vertex setup for extra planes // //========================================================================== int HWDrawInfo::CreateOtherPlaneVertices(subsector_t *sub, const secplane_t *plane) { auto alloc = screen->mVertexData->AllocVertices(sub->numlines); auto ptr = alloc.first; for (unsigned int k = 0; k < sub->numlines; k++) { vertex_t *vt = sub->firstline[k].v1; ptr->x = vt->fX(); ptr->z = plane->ZatPoint(vt); ptr->y = vt->fY(); ptr->u = vt->fX() / 64.f; ptr->v = -vt->fY() / 64.f; ptr++; } return alloc.second; } //========================================================================== // // Adds a subsector plane to a sector's render list // //========================================================================== void HWDrawInfo::AddOtherFloorPlane(int sector, gl_subsectorrendernode * node) { auto pNode = otherFloorPlanes.CheckKey(sector); node->next = pNode? *pNode : nullptr; node->lightindex = SetupLightsForOtherPlane(node->sub, lightdata, &Level->sectors[sector].floorplane); node->vertexindex = CreateOtherPlaneVertices(node->sub, &Level->sectors[sector].floorplane); otherFloorPlanes[sector] = node; } void HWDrawInfo::AddOtherCeilingPlane(int sector, gl_subsectorrendernode * node) { auto pNode = otherCeilingPlanes.CheckKey(sector); node->next = pNode? *pNode : nullptr; node->lightindex = SetupLightsForOtherPlane(node->sub, lightdata, &Level->sectors[sector].ceilingplane); node->vertexindex = CreateOtherPlaneVertices(node->sub, &Level->sectors[sector].ceilingplane); otherCeilingPlanes[sector] = node; } //========================================================================== // // Collects all sectors that might need a fake ceiling // //========================================================================== void HWDrawInfo::AddUpperMissingTexture(side_t * side, subsector_t *sub, float Backheight) { if (!side->segs[0]->backsector) return; for (int i = 0; i < side->numsegs; i++) { seg_t *seg = side->segs[i]; // we need find the seg belonging to the passed subsector if (seg->Subsector == sub) { MissingTextureInfo mti = {}; MissingSegInfo msi; if (sub->render_sector != sub->sector || seg->frontsector != sub->sector) { return; } for (unsigned int i = 0; i < MissingUpperTextures.Size(); i++) { if (MissingUpperTextures[i].sub == sub) { // Use the lowest adjoining height to draw a fake ceiling if necessary if (Backheight < MissingUpperTextures[i].Planez) { MissingUpperTextures[i].Planez = Backheight; MissingUpperTextures[i].seg = seg; } msi.MTI_Index = i; msi.seg = seg; MissingUpperSegs.Push(msi); return; } } mti.seg = seg; mti.sub = sub; mti.Planez = Backheight; msi.MTI_Index = MissingUpperTextures.Push(mti); msi.seg = seg; MissingUpperSegs.Push(msi); } } } //========================================================================== // // Collects all sectors that might need a fake floor // //========================================================================== void HWDrawInfo::AddLowerMissingTexture(side_t * side, subsector_t *sub, float Backheight) { sector_t *backsec = side->segs[0]->backsector; if (!backsec) return; if (backsec->transdoor) { // Transparent door hacks alter the backsector's floor height so we should not // process the missing texture for them. if (backsec->transdoorheight == backsec->GetPlaneTexZ(sector_t::floor)) return; } // we need to check all segs of this sidedef for (int i = 0; i < side->numsegs; i++) { seg_t *seg = side->segs[i]; // we need find the seg belonging to the passed subsector if (seg->Subsector == sub) { MissingTextureInfo mti = {}; MissingSegInfo msi; subsector_t * sub = seg->Subsector; if (sub->render_sector != sub->sector || seg->frontsector != sub->sector) { return; } // Ignore FF_FIX's because they are designed to abuse missing textures if (seg->backsector->e->XFloor.ffloors.Size() && (seg->backsector->e->XFloor.ffloors[0]->flags&(FF_FIX | FF_SEETHROUGH)) == FF_FIX) { return; } for (unsigned int i = 0; i < MissingLowerTextures.Size(); i++) { if (MissingLowerTextures[i].sub == sub) { // Use the highest adjoining height to draw a fake floor if necessary if (Backheight > MissingLowerTextures[i].Planez) { MissingLowerTextures[i].Planez = Backheight; MissingLowerTextures[i].seg = seg; } msi.MTI_Index = i; msi.seg = seg; MissingLowerSegs.Push(msi); return; } } mti.seg = seg; mti.sub = sub; mti.Planez = Backheight; msi.MTI_Index = MissingLowerTextures.Push(mti); msi.seg = seg; MissingLowerSegs.Push(msi); } } } //========================================================================== // // // //========================================================================== bool HWDrawInfo::DoOneSectorUpper(subsector_t * subsec, float Planez, area_t in_area) { // Is there a one-sided wall in this sector? // Do this first to avoid unnecessary recursion for (uint32_t i = 0; i < subsec->numlines; i++) { if (subsec->firstline[i].backsector == NULL) return false; if (subsec->firstline[i].PartnerSeg == NULL) return false; } for (uint32_t i = 0; i < subsec->numlines; i++) { seg_t * seg = subsec->firstline + i; subsector_t * backsub = seg->PartnerSeg->Subsector; // already checked? if (backsub->validcount == validcount) continue; backsub->validcount = validcount; if (seg->frontsector != seg->backsector && seg->linedef) { // Note: if this is a real line between sectors // we can be sure that render_sector is the real sector! sector_t * sec = hw_FakeFlat(seg->backsector, in_area, true); // Don't bother with slopes if (sec->ceilingplane.isSlope()) return false; // Is the neighboring ceiling lower than the desired height? if (sec->GetPlaneTexZ(sector_t::ceiling) < Planez) { // todo: check for missing textures. return false; } // This is an exact height match which means we don't have to do any further checks for this sector if (sec->GetPlaneTexZ(sector_t::ceiling) == Planez) { // If there's a texture abort auto tex = TexMan.GetGameTexture(seg->sidedef->GetTexture(side_t::top)); if (!tex || !tex->isValid()) continue; else return false; } } if (!DoOneSectorUpper(backsub, Planez, in_area)) return false; } // all checked ok. This subsector is part of the current fake plane HandledSubsectors.Push(subsec); return 1; } //========================================================================== // // // //========================================================================== bool HWDrawInfo::DoOneSectorLower(subsector_t * subsec, float Planez, area_t in_area) { // Is there a one-sided wall in this subsector? // Do this first to avoid unnecessary recursion for (uint32_t i = 0; i < subsec->numlines; i++) { if (subsec->firstline[i].backsector == NULL) return false; if (subsec->firstline[i].PartnerSeg == NULL) return false; } for (uint32_t i = 0; i < subsec->numlines; i++) { seg_t * seg = subsec->firstline + i; subsector_t * backsub = seg->PartnerSeg->Subsector; // already checked? if (backsub->validcount == validcount) continue; backsub->validcount = validcount; if (seg->frontsector != seg->backsector && seg->linedef) { // Note: if this is a real line between sectors // we can be sure that render_sector is the real sector! sector_t * sec = hw_FakeFlat(seg->backsector, in_area, true); // Don't bother with slopes if (sec->floorplane.isSlope()) return false; // Is the neighboring floor higher than the desired height? if (sec->GetPlaneTexZ(sector_t::floor) > Planez) { // todo: check for missing textures. return false; } // This is an exact height match which means we don't have to do any further checks for this sector if (sec->GetPlaneTexZ(sector_t::floor) == Planez) { // If there's a texture abort auto tex = TexMan.GetGameTexture(seg->sidedef->GetTexture(side_t::bottom)); if (!tex || !tex->isValid()) continue; else return false; } } if (!DoOneSectorLower(backsub, Planez, in_area)) return false; } // all checked ok. This sector is part of the current fake plane HandledSubsectors.Push(subsec); return 1; } //========================================================================== // // // //========================================================================== bool HWDrawInfo::DoFakeBridge(subsector_t * subsec, float Planez, area_t in_area) { // Is there a one-sided wall in this sector? // Do this first to avoid unnecessary recursion for (uint32_t i = 0; i < subsec->numlines; i++) { if (subsec->firstline[i].backsector == NULL) return false; if (subsec->firstline[i].PartnerSeg == NULL) return false; } for (uint32_t i = 0; i < subsec->numlines; i++) { seg_t * seg = subsec->firstline + i; subsector_t * backsub = seg->PartnerSeg->Subsector; // already checked? if (backsub->validcount == validcount) continue; backsub->validcount = validcount; if (seg->frontsector != seg->backsector && seg->linedef) { // Note: if this is a real line between sectors // we can be sure that render_sector is the real sector! sector_t * sec = hw_FakeFlat(seg->backsector, in_area, true); // Don't bother with slopes if (sec->floorplane.isSlope()) return false; // Is the neighboring floor higher than the desired height? if (sec->GetPlaneTexZ(sector_t::floor) < Planez) { // todo: check for missing textures. return false; } // This is an exact height match which means we don't have to do any further checks for this sector // No texture checks though! if (sec->GetPlaneTexZ(sector_t::floor) == Planez) continue; } if (!DoFakeBridge(backsub, Planez, in_area)) return false; } // all checked ok. This sector is part of the current fake plane HandledSubsectors.Push(subsec); return 1; } //========================================================================== // // // //========================================================================== bool HWDrawInfo::DoFakeCeilingBridge(subsector_t * subsec, float Planez, area_t in_area) { // Is there a one-sided wall in this sector? // Do this first to avoid unnecessary recursion for (uint32_t i = 0; i < subsec->numlines; i++) { if (subsec->firstline[i].backsector == NULL) return false; if (subsec->firstline[i].PartnerSeg == NULL) return false; } for (uint32_t i = 0; i < subsec->numlines; i++) { seg_t * seg = subsec->firstline + i; subsector_t * backsub = seg->PartnerSeg->Subsector; // already checked? if (backsub->validcount == validcount) continue; backsub->validcount = validcount; if (seg->frontsector != seg->backsector && seg->linedef) { // Note: if this is a real line between sectors // we can be sure that render_sector is the real sector! sector_t * sec = hw_FakeFlat(seg->backsector, in_area, true); // Don't bother with slopes if (sec->ceilingplane.isSlope()) return false; // Is the neighboring ceiling higher than the desired height? if (sec->GetPlaneTexZ(sector_t::ceiling) > Planez) { // todo: check for missing textures. return false; } // This is an exact height match which means we don't have to do any further checks for this sector // No texture checks though! if (sec->GetPlaneTexZ(sector_t::ceiling) == Planez) continue; } if (!DoFakeCeilingBridge(backsub, Planez, in_area)) return false; } // all checked ok. This sector is part of the current fake plane HandledSubsectors.Push(subsec); return 1; } //========================================================================== // // Draws the fake planes // //========================================================================== void HWDrawInfo::HandleMissingTextures(area_t in_area) { for (unsigned int i = 0; i < MissingUpperTextures.Size(); i++) { if (!MissingUpperTextures[i].seg) continue; HandledSubsectors.Clear(); validcount++; if (MissingUpperTextures[i].Planez > Viewpoint.Pos.Z) { // close the hole only if all neighboring sectors are an exact height match // Otherwise just fill in the missing textures. MissingUpperTextures[i].sub->validcount = validcount; if (DoOneSectorUpper(MissingUpperTextures[i].sub, MissingUpperTextures[i].Planez, in_area)) { sector_t * sec = MissingUpperTextures[i].seg->backsector; for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = HandledSubsectors[j]; AddOtherCeilingPlane(sec->sectornum, node); } if (HandledSubsectors.Size() != 1) { // mark all subsectors in the missing list that got processed by this for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { for (unsigned int k = 0; k < MissingUpperTextures.Size(); k++) { if (MissingUpperTextures[k].sub == HandledSubsectors[j]) { MissingUpperTextures[k].seg = NULL; } } } } else MissingUpperTextures[i].seg = NULL; continue; } } if (!MissingUpperTextures[i].seg->PartnerSeg) continue; subsector_t *backsub = MissingUpperTextures[i].seg->PartnerSeg->Subsector; if (!backsub) continue; validcount++; HandledSubsectors.Clear(); { // It isn't a hole. Now check whether it might be a fake bridge sector_t * fakesector = hw_FakeFlat(MissingUpperTextures[i].seg->frontsector, in_area, false); float planez = (float)fakesector->GetPlaneTexZ(sector_t::ceiling); backsub->validcount = validcount; if (DoFakeCeilingBridge(backsub, planez, in_area)) { for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = HandledSubsectors[j]; AddOtherCeilingPlane(fakesector->sectornum, node); } } continue; } } for (unsigned int i = 0; i < MissingLowerTextures.Size(); i++) { if (!MissingLowerTextures[i].seg) continue; HandledSubsectors.Clear(); validcount++; if (MissingLowerTextures[i].Planez < Viewpoint.Pos.Z) { // close the hole only if all neighboring sectors are an exact height match // Otherwise just fill in the missing textures. MissingLowerTextures[i].sub->validcount = validcount; if (DoOneSectorLower(MissingLowerTextures[i].sub, MissingLowerTextures[i].Planez, in_area)) { sector_t * sec = MissingLowerTextures[i].seg->backsector; for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = HandledSubsectors[j]; AddOtherFloorPlane(sec->sectornum, node); } if (HandledSubsectors.Size() != 1) { // mark all subsectors in the missing list that got processed by this for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { for (unsigned int k = 0; k < MissingLowerTextures.Size(); k++) { if (MissingLowerTextures[k].sub == HandledSubsectors[j]) { MissingLowerTextures[k].seg = NULL; } } } } else MissingLowerTextures[i].seg = NULL; continue; } } if (!MissingLowerTextures[i].seg->PartnerSeg) continue; subsector_t *backsub = MissingLowerTextures[i].seg->PartnerSeg->Subsector; if (!backsub) continue; validcount++; HandledSubsectors.Clear(); { // It isn't a hole. Now check whether it might be a fake bridge sector_t * fakesector = hw_FakeFlat(MissingLowerTextures[i].seg->frontsector, in_area, false); float planez = (float)fakesector->GetPlaneTexZ(sector_t::floor); backsub->validcount = validcount; if (DoFakeBridge(backsub, planez, in_area)) { for (unsigned int j = 0; j < HandledSubsectors.Size(); j++) { gl_subsectorrendernode * node = NewSubsectorRenderNode(); node->sub = HandledSubsectors[j]; AddOtherFloorPlane(fakesector->sectornum, node); } } continue; } } } //========================================================================== // // // //========================================================================== void HWDrawInfo::CreateFloodStencilPoly(wallseg * ws, FFlatVertex *vertices) { vertices[0].Set(ws->x1, ws->z1, ws->y1, 0, 0); vertices[1].Set(ws->x1, ws->z2, ws->y1, 0, 0); vertices[2].Set(ws->x2, ws->z1, ws->y2, 0, 0); vertices[3].Set(ws->x2, ws->z2, ws->y2, 0, 0); } //========================================================================== // // // //========================================================================== void HWDrawInfo::CreateFloodPoly(wallseg * ws, FFlatVertex *vertices, float planez, sector_t * sec, bool ceiling) { float fviewx = Viewpoint.Pos.X; float fviewy = Viewpoint.Pos.Y; float fviewz = Viewpoint.Pos.Z; float prj_fac1 = (planez - fviewz) / (ws->z1 - fviewz); float prj_fac2 = (planez - fviewz) / (ws->z2 - fviewz); float px1 = fviewx + prj_fac1 * (ws->x1 - fviewx); float py1 = fviewy + prj_fac1 * (ws->y1 - fviewy); float px2 = fviewx + prj_fac2 * (ws->x1 - fviewx); float py2 = fviewy + prj_fac2 * (ws->y1 - fviewy); float px3 = fviewx + prj_fac2 * (ws->x2 - fviewx); float py3 = fviewy + prj_fac2 * (ws->y2 - fviewy); float px4 = fviewx + prj_fac1 * (ws->x2 - fviewx); float py4 = fviewy + prj_fac1 * (ws->y2 - fviewy); vertices[0].Set(px1, planez, py1, px1 / 64, -py1 / 64); vertices[1].Set(px2, planez, py2, px2 / 64, -py2 / 64); vertices[2].Set(px4, planez, py4, px4 / 64, -py4 / 64); vertices[3].Set(px3, planez, py3, px3 / 64, -py3 / 64); } //========================================================================== // // // //========================================================================== void HWDrawInfo::PrepareUpperGap(seg_t * seg) { wallseg ws; sector_t * fakefsector = hw_FakeFlat(seg->frontsector, in_area, false); sector_t * fakebsector = hw_FakeFlat(seg->backsector, in_area, true); vertex_t * v1, *v2; // Although the plane can be sloped this code will only be called // when the edge itself is not. double backz = fakebsector->ceilingplane.ZatPoint(seg->v1); double frontz = fakefsector->ceilingplane.ZatPoint(seg->v1); if (fakebsector->GetTexture(sector_t::ceiling) == skyflatnum) return; if (backz < Viewpoint.Pos.Z) return; if (seg->sidedef == seg->linedef->sidedef[0]) { v1 = seg->linedef->v1; v2 = seg->linedef->v2; } else { v1 = seg->linedef->v2; v2 = seg->linedef->v1; } ws.x1 = v1->fX(); ws.y1 = v1->fY(); ws.x2 = v2->fX(); ws.y2 = v2->fY(); ws.z1 = frontz; ws.z2 = backz; auto vertices = screen->mVertexData->AllocVertices(8); CreateFloodStencilPoly(&ws, vertices.first); CreateFloodPoly(&ws, vertices.first+4, ws.z2, fakebsector, true); gl_floodrendernode *node = NewFloodRenderNode(); auto pNode = floodCeilingSegs.CheckKey(fakebsector->sectornum); node->next = pNode? *pNode : nullptr; node->seg = seg; node->vertexindex = vertices.second; floodCeilingSegs[fakebsector->sectornum] = node; } //========================================================================== // // // //========================================================================== void HWDrawInfo::PrepareLowerGap(seg_t * seg) { wallseg ws; sector_t * fakefsector = hw_FakeFlat(seg->frontsector, in_area, false); sector_t * fakebsector = hw_FakeFlat(seg->backsector, in_area, true); vertex_t * v1, *v2; // Although the plane can be sloped this code will only be called // when the edge itself is not. double backz = fakebsector->floorplane.ZatPoint(seg->v1); double frontz = fakefsector->floorplane.ZatPoint(seg->v1); if (fakebsector->GetTexture(sector_t::floor) == skyflatnum) return; if (fakebsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) return; if (seg->sidedef == seg->linedef->sidedef[0]) { v1 = seg->linedef->v1; v2 = seg->linedef->v2; } else { v1 = seg->linedef->v2; v2 = seg->linedef->v1; } ws.x1 = v1->fX(); ws.y1 = v1->fY(); ws.x2 = v2->fX(); ws.y2 = v2->fY(); ws.z2 = frontz; ws.z1 = backz; auto vertices = screen->mVertexData->AllocVertices(8); CreateFloodStencilPoly(&ws, vertices.first); CreateFloodPoly(&ws, vertices.first+4, ws.z1, fakebsector, false); gl_floodrendernode *node = NewFloodRenderNode(); auto pNode = floodFloorSegs.CheckKey(fakebsector->sectornum); node->next = pNode? *pNode : nullptr; node->seg = seg; node->vertexindex = vertices.second; floodFloorSegs[fakebsector->sectornum] = node; } //========================================================================== // // // //========================================================================== void HWDrawInfo::PrepareUnhandledMissingTextures() { if (!Level->notexturefill) { validcount++; for (int i = MissingUpperSegs.Size() - 1; i >= 0; i--) { int index = MissingUpperSegs[i].MTI_Index; if (index >= 0 && MissingUpperTextures[index].seg == NULL) continue; seg_t * seg = MissingUpperSegs[i].seg; // already done! if (seg->linedef->validcount == validcount) continue; // already done seg->linedef->validcount = validcount; if (seg->frontsector->GetPlaneTexZ(sector_t::ceiling) < Viewpoint.Pos.Z) continue; // out of sight // FIXME: The check for degenerate subsectors should be more precise if (seg->PartnerSeg && (seg->PartnerSeg->Subsector->flags & SSECF_DEGENERATE)) continue; if (seg->backsector->transdoor) continue; if (seg->backsector->GetTexture(sector_t::ceiling) == skyflatnum) continue; if (seg->backsector->ValidatePortal(sector_t::ceiling) != NULL) continue; PrepareUpperGap(seg); } validcount++; for (int i = MissingLowerSegs.Size() - 1; i >= 0; i--) { int index = MissingLowerSegs[i].MTI_Index; if (index >= 0 && MissingLowerTextures[index].seg == NULL) continue; seg_t * seg = MissingLowerSegs[i].seg; if (seg->linedef->validcount == validcount) continue; // already done seg->linedef->validcount = validcount; if (seg->frontsector->GetPlaneTexZ(sector_t::floor) > Viewpoint.Pos.Z) continue; // out of sight if (seg->backsector->transdoor) continue; if (seg->backsector->GetTexture(sector_t::floor) == skyflatnum) continue; if (seg->backsector->ValidatePortal(sector_t::floor) != NULL) continue; PrepareLowerGap(seg); } } MissingUpperTextures.Clear(); MissingLowerTextures.Clear(); MissingUpperSegs.Clear(); MissingLowerSegs.Clear(); } //========================================================================== // // Multi-sector deep water hacks // //========================================================================== void HWDrawInfo::AddHackedSubsector(subsector_t * sub) { if (!(Level->maptype == MAPTYPE_HEXEN)) { SubsectorHackInfo sh={sub, 0}; SubsectorHacks.Push (sh); } } //========================================================================== // // Finds a subsector whose plane can be used for rendering // //========================================================================== bool HWDrawInfo::CheckAnchorFloor(subsector_t * sub) { // This subsector has a one sided wall and can be used. if (sub->hacked==3) return true; if (sub->flags & SSECF_DEGENERATE) return false; for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (!seg->PartnerSeg) return true; subsector_t * backsub = seg->PartnerSeg->Subsector; // Find a linedef with a different visplane on the other side. if (!(backsub->flags & SSECF_DEGENERATE) && seg->linedef && (sub->render_sector != backsub->render_sector && sub->sector != backsub->sector)) { // I'm ignoring slopes, scaling and rotation here. The likelihood of ZDoom maps // using such crap hacks is simply too small if (sub->render_sector->GetTexture(sector_t::floor) == backsub->render_sector->GetTexture(sector_t::floor) && sub->render_sector->GetPlaneTexZ(sector_t::floor) == backsub->render_sector->GetPlaneTexZ(sector_t::floor) && sub->render_sector->GetFloorLight() == backsub->render_sector->GetFloorLight()) { continue; } // This means we found an adjoining subsector that clearly would go into another // visplane. That means that this subsector can be used as an anchor. return true; } } return false; } //========================================================================== // // Collect connected subsectors that have to be rendered with the same plane // //========================================================================== bool HWDrawInfo::CollectSubsectorsFloor(subsector_t * sub, sector_t * anchor) { // mark it checked sub->validcount=validcount; // We must collect any subsector that either is connected to this one with a miniseg // or has the same visplane. // We must not collect any subsector that has the anchor's visplane! if (!(sub->flags & SSECF_DEGENERATE)) { // Is not being rendered so don't bother. if (!(ss_renderflags[sub->Index()] & SSRF_PROCESSED)) return true; if (sub->render_sector->GetTexture(sector_t::floor) != anchor->GetTexture(sector_t::floor) || sub->render_sector->GetPlaneTexZ(sector_t::floor) != anchor->GetPlaneTexZ(sector_t::floor) || sub->render_sector->GetFloorLight() != anchor->GetFloorLight()) { if (sub == viewsubsector && Viewpoint.Pos.Z < anchor->GetPlaneTexZ(sector_t::floor)) inview = true; HandledSubsectors.Push(sub); } } // We can assume that all segs in this subsector are connected to a subsector that has // to be checked as well for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (seg->PartnerSeg) { subsector_t * backsub = seg->PartnerSeg->Subsector; // could be an anchor itself. if (!CheckAnchorFloor (backsub)) // must not be an anchor itself! { if (backsub->validcount!=validcount) { if (!CollectSubsectorsFloor (backsub, anchor)) return false; } } else if (sub->render_sector == backsub->render_sector) { // Any anchor not within the original anchor's visplane terminates the processing. if (sub->render_sector->GetTexture(sector_t::floor) != anchor->GetTexture(sector_t::floor) || sub->render_sector->GetPlaneTexZ(sector_t::floor) != anchor->GetPlaneTexZ(sector_t::floor) || sub->render_sector->GetFloorLight() != anchor->GetFloorLight()) { return false; } } if (!seg->linedef || (seg->frontsector==seg->backsector && sub->render_sector!=backsub->render_sector)) lowersegs.Push(seg); } } return true; } //========================================================================== // // Finds a subsector whose plane can be used for rendering // //========================================================================== bool HWDrawInfo::CheckAnchorCeiling(subsector_t * sub) { // This subsector has a one sided wall and can be used. if (sub->hacked==3) return true; if (sub->flags & SSECF_DEGENERATE) return false; for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (!seg->PartnerSeg) return true; subsector_t * backsub = seg->PartnerSeg->Subsector; // Find a linedef with a different visplane on the other side. if (!(backsub->flags & SSECF_DEGENERATE) && seg->linedef && (sub->render_sector != backsub->render_sector && sub->sector != backsub->sector)) { // I'm ignoring slopes, scaling and rotation here. The likelihood of ZDoom maps // using such crap hacks is simply too small if (sub->render_sector->GetTexture(sector_t::ceiling) == backsub->render_sector->GetTexture(sector_t::ceiling) && sub->render_sector->GetPlaneTexZ(sector_t::ceiling) == backsub->render_sector->GetPlaneTexZ(sector_t::ceiling) && sub->render_sector->GetCeilingLight() == backsub->render_sector->GetCeilingLight()) { continue; } // This means we found an adjoining subsector that clearly would go into another // visplane. That means that this subsector can be used as an anchor. return true; } } return false; } //========================================================================== // // Collect connected subsectors that have to be rendered with the same plane // //========================================================================== bool HWDrawInfo::CollectSubsectorsCeiling(subsector_t * sub, sector_t * anchor) { // mark it checked sub->validcount=validcount; // We must collect any subsector that either is connected to this one with a miniseg // or has the same visplane. // We must not collect any subsector that has the anchor's visplane! if (!(sub->flags & SSECF_DEGENERATE)) { // Is not being rendererd so don't bother. if (!(ss_renderflags[sub->Index()]&SSRF_PROCESSED)) return true; if (sub->render_sector->GetTexture(sector_t::ceiling) != anchor->GetTexture(sector_t::ceiling) || sub->render_sector->GetPlaneTexZ(sector_t::ceiling) != anchor->GetPlaneTexZ(sector_t::ceiling) || sub->render_sector->GetCeilingLight() != anchor->GetCeilingLight()) { HandledSubsectors.Push(sub); } } // We can assume that all segs in this subsector are connected to a subsector that has // to be checked as well for(uint32_t j=0;jnumlines;j++) { seg_t * seg = sub->firstline + j; if (seg->PartnerSeg) { subsector_t * backsub = seg->PartnerSeg->Subsector; // could be an anchor itself. if (!CheckAnchorCeiling (backsub)) // must not be an anchor itself! { if (backsub->validcount!=validcount) { if (!CollectSubsectorsCeiling (backsub, anchor)) return false; } } else if (sub->render_sector == backsub->render_sector) { // Any anchor not within the original anchor's visplane terminates the processing. if (sub->render_sector->GetTexture(sector_t::ceiling) != anchor->GetTexture(sector_t::ceiling) || sub->render_sector->GetPlaneTexZ(sector_t::ceiling) != anchor->GetPlaneTexZ(sector_t::ceiling) || sub->render_sector->GetCeilingLight() != anchor->GetCeilingLight()) { return false; } } } } return true; } //========================================================================== // // Process the subsectors that have been marked as hacked // //========================================================================== void HWDrawInfo::ProcessLowerMinisegs(TArray &lowersegs) { for(unsigned int j=0;jSubsector->render_sector, seg->PartnerSeg->Subsector->render_sector); rendered_lines++; } } void HWDrawInfo::HandleHackedSubsectors() { viewsubsector = Level->PointInRenderSubsector(Viewpoint.Pos); // Each subsector may only be processed once in this loop! validcount++; for(unsigned int i=0;ivalidcount!=validcount && CheckAnchorFloor(sub)) { // Now collect everything that is connected with this subsector. HandledSubsectors.Clear(); inview=false; lowersegs.Clear(); if (CollectSubsectorsFloor(sub, sub->render_sector)) { for(unsigned int j=0;jsub = HandledSubsectors[j]; AddOtherFloorPlane(sub->render_sector->sectornum, node); } if (inview) ProcessLowerMinisegs(lowersegs); } } } // Each subsector may only be processed once in this loop! validcount++; for(unsigned int i=0;ivalidcount!=validcount && CheckAnchorCeiling(sub)) { // Now collect everything that is connected with this subsector. HandledSubsectors.Clear(); if (CollectSubsectorsCeiling(sub, sub->render_sector)) { for(unsigned int j=0;jsub = HandledSubsectors[j]; AddOtherCeilingPlane(sub->render_sector->sectornum, node); } } } } SubsectorHacks.Clear(); }