vkdoom_m/src/rendering/hwrenderer/scene/hw_renderhacks.cpp
2020-04-26 12:41:13 +02:00

1177 lines
36 KiB
C++

//
//---------------------------------------------------------------------------
//
// 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<int, gl_subsectorrendernode*>::Pair *pair;
TMap<int, gl_floodrendernode*>::Pair *fpair;
TMap<int, gl_subsectorrendernode*>::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<int, gl_subsectorrendernode*>::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<int, gl_floodrendernode*>::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<int, gl_floodrendernode*>::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;j<sub->numlines;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;j<sub->numlines;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;j<sub->numlines;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;j<sub->numlines;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<seg_t *> &lowersegs)
{
for(unsigned int j=0;j<lowersegs.Size();j++)
{
seg_t * seg=lowersegs[j];
HWWall wall;
wall.ProcessLowerMiniseg(this, seg, seg->Subsector->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;i<SubsectorHacks.Size();i++)
{
subsector_t * sub = SubsectorHacks[i].sub;
if (sub->validcount!=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;j<HandledSubsectors.Size();j++)
{
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = 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;i<SubsectorHacks.Size();i++)
{
subsector_t * sub = SubsectorHacks[i].sub;
if (sub->validcount!=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;j<HandledSubsectors.Size();j++)
{
gl_subsectorrendernode * node = NewSubsectorRenderNode();
node->sub = HandledSubsectors[j];
AddOtherCeilingPlane(sub->render_sector->sectornum, node);
}
}
}
}
SubsectorHacks.Clear();
}