- create an intermediate structure between sectors and subsectors.

A section is a continuous part of a sector or in some case of several nearby continuous parts. For sectors with far away parts multiple sections will be created, especially when they lie in disjoint parts of the map.
This is mainly supposed to cut down on time for linking dynamic lights. Since they need to traverse subsectors to find all touching sidedefs a more coarse data structure that only contains the info needed for this is more suitable. In particular, this does not contain any intra-sector lines, i.e. those with both sides in the same sector.
This commit is contained in:
Christoph Oelckers 2018-11-04 16:47:45 +01:00
parent b1d35eb0b3
commit 49bfdbef9f
11 changed files with 1282 additions and 904 deletions

View File

@ -1049,6 +1049,7 @@ set (PCH_SOURCES
gl/system/gl_buffers.cpp
gl/textures/gl_hwtexture.cpp
gl/textures/gl_samplers.cpp
hwrenderer/data/hw_sections.cpp
hwrenderer/data/flatvertices.cpp
hwrenderer/data/hw_viewpointbuffer.cpp
hwrenderer/dynlights/hw_aabbtree.cpp

View File

@ -2072,7 +2072,7 @@ void AM_drawSubsectors()
continue;
}
if ((!(subsectors[i].flags & SSECMF_DRAWN) || (subsectors[i].render_sector->MoreFlags & SECMF_HIDDEN)) && am_cheat == 0)
if ((!(subsectors[i].flags & SSECMF_DRAWN) || (subsectors[i].flags & SSECF_HOLE) || (subsectors[i].render_sector->MoreFlags & SECMF_HIDDEN)) && am_cheat == 0)
{
continue;
}

View File

@ -0,0 +1,764 @@
//
//---------------------------------------------------------------------------
//
// Copyright(C) 2008-2018 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/
//
//--------------------------------------------------------------------------
//
#include <future>
#include "i_system.h"
#include "p_local.h"
#include "c_dispatch.h"
#include "r_defs.h"
#include "g_levellocals.h"
#include "hw_sections.h"
#include "earcut.hpp"
#include "stats.h"
#include "p_setup.h"
#include "c_dispatch.h"
#include "memarena.h"
using DoublePoint = std::pair<DVector2, DVector2>;
template<> struct THashTraits<DoublePoint>
{
// Use all bits when hashing doubles instead of converting them to ints.
hash_t Hash(const DoublePoint &key)
{
return (hash_t)SuperFastHash((const char*)(const void*)&key, sizeof(key));
}
int Compare(const DoublePoint &left, const DoublePoint &right) { return left != right; }
};
struct WorkSectionLine
{
vertex_t *start;
vertex_t *end;
side_t *sidedef;
seg_t *refseg;
int tempindex;
int mygroup;
WorkSectionLine *partner;
bool flagged;
};
struct WorkSection
{
int sectorindex;
int mapsection;
bool hasminisegs;
TArray<WorkSectionLine*>segments;
TArray<side_t *> originalSides; // The segs will lose some of these while working on them.
};
struct TriangleWorkData
{
BoundingRect boundingBox;
unsigned boundingLoopStart;
};
struct GroupWork
{
WorkSection *section;
TriangleWorkData *work;
int index;
};
struct Group
{
TArray<GroupWork> groupedSections;
TMap<int, bool> sideMap;
TArray<WorkSectionLine*> segments;
};
class FSectionCreator
{
FMemArena allocator;
TArray<WorkSectionLine *>AllAllocatedLines;
bool verbose = false;
TMap<int, TArray<int>> subsectormap;
TArray<TArray<int>> rawsections; // list of unprocessed subsectors. Sector and mapsection can be retrieved from the elements so aren't stored.
TArray<WorkSection> sections;
TArray<TriangleWorkData> triangles;
TArray<Group> groups;
TArray<int> groupForSection;
public:
FSectionCreator()
{
// These must be manually destroyed but not deleted.
for (auto line : AllAllocatedLines)
{
line->~WorkSectionLine();
}
}
// Allocate these from a static allocator so that they remain fixed in memory and avoid excessive small allocations.
WorkSectionLine *NewLine()
{
void *mem = allocator.Alloc(sizeof(WorkSectionLine));
auto line = new(mem) WorkSectionLine;
AllAllocatedLines.Push(line);
return line;
}
int IndexOf(WorkSectionLine *line)
{
return AllAllocatedLines.Find(line); // slow, but only used for debugging.
}
int MakeKey(int sector, int mapsection)
{
return sector + (mapsection << 16);
}
int MakeKey(const subsector_t &sub)
{
return MakeKey(sub.render_sector->Index(), sub.mapsection);
}
int SectorFromKey(int key)
{
return key & 65535;
}
int MapsectionFromKey(int key)
{
return key >> 16;
}
//==========================================================================
//
// Groups subsectors by both sectors and mapsection
// The parts of a sector in a single map section is the upper boundary
// for what a sector section may contain.
//
//==========================================================================
void GroupSubsectors()
{
for (auto &sub : level.subsectors)
{
int key = MakeKey(sub);
auto &array = subsectormap[key];
array.Push(sub.Index());
}
}
//==========================================================================
//
// Go through the list and split each element further into
// connected groups of subsectors.
//
//==========================================================================
void CompileSections()
{
TMap<int, TArray<int>>::Pair *pair;
TMap<int, TArray<int>>::Iterator it(subsectormap);
while (it.NextPair(pair))
{
CompileSections(pair->Value);
}
subsectormap.Clear();
}
//==========================================================================
//
// Find all groups of connected subsectors and put them into the group list
//
//==========================================================================
void CompileSections(TArray<int> &list)
{
TArray<int> sublist;
TArray<seg_t *> seglist;
while (list.Size() > 0)
{
sublist.Clear();
seglist.Clear();
int index;
list.Pop(index);
auto sub = &level.subsectors[index];
auto collect = [&](subsector_t *sub)
{
sublist.Push(sub->Index());
for (unsigned i = 0; i < sub->numlines; i++)
{
if (sub->firstline[i].PartnerSeg && sub->firstline[i].Subsector->render_sector == sub->firstline[i].PartnerSeg->Subsector->render_sector)
{
seglist.Push(sub->firstline[i].PartnerSeg);
}
}
};
collect(sub);
for (unsigned i = 0; i < seglist.Size(); i++)
{
auto subi = seglist[i]->Subsector->Index();
for (unsigned j = 0; j < list.Size(); j++)
{
if (subi == list[j])
{
collect(&level.subsectors[subi]);
list.Delete(j);
j--;
}
}
}
rawsections.Push(std::move(sublist));
}
}
//==========================================================================
//
//
//==========================================================================
void MakeOutlines()
{
TArray<WorkSectionLine *> lineForSeg(level.segs.Size(), true);
memset(lineForSeg.Data(), 0, sizeof(WorkSectionLine*) * level.segs.Size());
for (auto &list : rawsections)
{
MakeOutline(list, lineForSeg);
}
// Assign partners after everything has been collected
for (auto &section : sections)
{
for (auto seg : section.segments)
{
if (seg->refseg && seg->refseg->PartnerSeg)
{
seg->partner = lineForSeg[seg->refseg->PartnerSeg->Index()];
}
}
}
}
//==========================================================================
//
// Creates an outline for a given section
//
//==========================================================================
void MakeOutline(TArray<int> &rawsection, TArray<WorkSectionLine *> &lineForSeg)
{
TArray<side_t *> foundsides;
TArray<seg_t *> outersegs;
TArray<seg_t *> loopedsegs;
bool hasminisegs = false;
// Collect all the segs that make up the outline of this section.
for (auto j : rawsection)
{
auto sub = &level.subsectors[j];
for (unsigned i = 0; i < sub->numlines; i++)
{
if (!sub->firstline[i].PartnerSeg || sub->firstline[i].Subsector->render_sector != sub->firstline[i].PartnerSeg->Subsector->render_sector)
{
outersegs.Push(&sub->firstline[i]);
if (sub->firstline[i].sidedef == nullptr) hasminisegs = true;
}
if (sub->firstline[i].sidedef)
{
foundsides.Push(sub->firstline[i].sidedef); // This may contain duplicate. Let's deal with those later.
}
}
}
// Loop until all segs have been used.
seg_t *seg = nullptr;
unsigned startindex = 0;
while (outersegs.Size() > 0)
{
if (seg == nullptr)
{
for (unsigned i = 0; i < outersegs.Size(); i++)
{
if (outersegs[i]->sidedef != nullptr && outersegs[i]->sidedef->V1() == outersegs[i]->v1)
{
seg = outersegs[i];
outersegs.Delete(i);
break;
}
}
if (seg == nullptr)
{
// There's only minisegs left. Most likely this is just node garbage.
// Todo: Need to check.
seg = outersegs[0];
outersegs.Delete(0);
}
startindex = loopedsegs.Push(seg);
}
// Find the next seg in the loop.
auto segangle = VecToAngle(seg->v2->fPos() - seg->v1->fPos());
seg_t *pick = nullptr;
int pickindex = -1;
for (unsigned i = 0; i < outersegs.Size(); i++)
{
auto secondseg = outersegs[i];
if (secondseg->v1->fPos() == seg->v2->fPos())
{
// This should never choose a miniseg over a real sidedef.
if (pick == nullptr || (pick->sidedef == nullptr && secondseg->sidedef != nullptr))
{
pick = secondseg;
pickindex = i;
}
else if (pick->sidedef == nullptr || secondseg->sidedef != nullptr)
{
// If there's more than one pick the one with the smallest angle.
auto pickangle = deltaangle(segangle, VecToAngle(pick->v2->fPos() - pick->v1->fPos()));
auto secondangle = deltaangle(segangle, VecToAngle(secondseg->v2->fPos() - secondseg->v1->fPos()));
if (secondangle < pickangle)
{
pick = secondseg;
pickindex = i;
}
}
}
}
if (pick)
{
loopedsegs.Push(pick);
outersegs.Delete(pickindex);
seg = pick;
}
else
{
// There was no more seg connecting to the last one. We should have reached the beginning of the loop again.
if (loopedsegs.Last() == nullptr || loopedsegs[startindex]->v1->fPos() != loopedsegs.Last()->v2->fPos())
{
// Did not find another one but have an unclosed loop. This should never happen and would indicate broken nodes.
// Error out and let the calling code deal with it.
I_Error("Unclosed loop in sector %d at position (%d, %d)\n", loopedsegs[0]->Subsector->render_sector->Index(), (int)loopedsegs[0]->v1->fX(), (int)loopedsegs[0]->v1->fY());
}
seg = nullptr;
loopedsegs.Push(nullptr); // A separator is not really needed but useful for debugging.
}
}
if (loopedsegs.Size() > 0)
{
auto sector = loopedsegs[0]->Subsector->render_sector->Index();
auto mapsec = loopedsegs[0]->Subsector->mapsection;
TArray<WorkSectionLine *> sectionlines(loopedsegs.Size(), true);
for (unsigned i = 0; i < loopedsegs.Size(); i++)
{
if (loopedsegs[i])
{
sectionlines[i] = NewLine();
*sectionlines[i] = { loopedsegs[i]->v1, loopedsegs[i]->v2, loopedsegs[i]->sidedef, loopedsegs[i], -1, (int)sections.Size(), nullptr };
lineForSeg[loopedsegs[i]->Index()] = sectionlines[i];
}
else
{
sectionlines[i] = NewLine();
*sectionlines[i] = { nullptr, nullptr, nullptr, nullptr, -1, (int)sections.Size(), nullptr };
}
}
sections.Push({ sector, mapsec, hasminisegs, std::move(sectionlines), std::move(foundsides) });
}
}
//=============================================================================
//
// Tries to merge continuous segs on the same sidedef
//
//=============================================================================
void MergeLines()
{
for (auto &build : sections)
{
for (int i = (int)build.segments.Size() - 1; i > 0; i--)
{
auto ln1 = build.segments[i];
auto ln2 = build.segments[i - 1];
if (ln1->sidedef && ln2->sidedef && ln1->sidedef == ln2->sidedef)
{
if (ln1->partner == nullptr && ln2->partner == nullptr)
{
// identical references. These 2 lines can be merged.
ln2->end = ln1->end;
build.segments.Delete(i);
}
else if (ln1->partner && ln2->partner && ln1->partner->mygroup == ln2->partner->mygroup)
{
auto &section = sections[ln1->partner->mygroup];
auto index1 = section.segments.Find(ln1->partner); // note that ln1 and ln2 are ordered backward, so the partners are ordered forward.
auto index2 = section.segments.Find(ln2->partner);
if (index2 == index1 + 1 && index2 < section.segments.Size())
{
// Merge both sides at once to ensure the data remains consistent.
ln2->partner->start = ln1->partner->start;
section.segments.Delete(index1);
ln2->end = ln1->end;
build.segments.Delete(i);
}
}
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
void FindOuterLoops()
{
triangles.Resize(sections.Size());
for (unsigned int i = 0; i < sections.Size(); i++)
{
auto &section = sections[i];
auto &work = triangles[i];
BoundingRect bounds = { 1e32, 1e32, -1e32, -1e32 };
BoundingRect loopBounds = { 1e32, 1e32, -1e32, -1e32 };
BoundingRect outermostBounds = { 1e32, 1e32, -1e32, -1e32 };
unsigned outermoststart = ~0u;
unsigned loopstart = 0;
bool ispolyorg = false;
for (unsigned i = 0; i <= section.segments.Size(); i++)
{
if (i < section.segments.Size() && section.segments[i]->refseg)
{
ispolyorg |= !!(section.segments[i]->refseg->Subsector->flags & SSECF_POLYORG);
loopBounds.addVertex(section.segments[i]->start->fX(), section.segments[i]->start->fY());
loopBounds.addVertex(section.segments[i]->end->fX(), section.segments[i]->end->fY());
bounds.addVertex(section.segments[i]->start->fX(), section.segments[i]->start->fY());
bounds.addVertex(section.segments[i]->end->fX(), section.segments[i]->end->fY());
}
else
{
if (outermostBounds.left == 1e32 || loopBounds.contains(outermostBounds))
{
outermostBounds = loopBounds;
outermoststart = loopstart;
loopstart = i + 1;
}
loopBounds = { 1e32, 1e32, -1e32, -1e32 };
}
}
work.boundingLoopStart = outermoststart;
work.boundingBox = outermostBounds;
}
}
//=============================================================================
//
//
//
//=============================================================================
void GroupSections()
{
TArray<GroupWork> workingSet;
GroupWork first = { &sections[0], &triangles[0], 0 };
workingSet.Push(first);
groupForSection.Resize(sections.Size());
for (auto &i : groupForSection) i = -1;
for (unsigned i = 1; i < sections.Size(); i++)
{
auto sect = &sections[i];
if (sect->segments.Size() == 0) continue;
if (sect->sectorindex == first.section->sectorindex && sect->mapsection == first.section->mapsection)
{
workingSet.Push({ sect, &triangles[i], (int)i });
}
else
{
GroupWorkingSet(workingSet);
workingSet.Clear();
first = { &sections[i], &triangles[i], (int)i };
workingSet.Push(first);
}
}
GroupWorkingSet(workingSet);
}
//=============================================================================
//
//
//
//=============================================================================
void GroupWorkingSet(TArray<GroupWork> &workingSet)
{
const double MAX_GROUP_DIST = 256;
TArray<GroupWork> build;
if (workingSet.Size() == 1)
{
groupForSection[workingSet[0].index] = groups.Size();
Group g;
g.groupedSections = std::move(workingSet);
groups.Push(std::move(g));
return;
}
while (workingSet.Size() > 0)
{
build.Clear();
build.Push(workingSet[0]);
groupForSection[workingSet[0].index] = groups.Size();
workingSet.Delete(0);
// Don't use iterators here. These arrays are modified inside.
for (unsigned j = 0; j < build.Size(); j++)
{
auto &current = build[j];
for (int i = 0; i < (int)workingSet.Size(); i++)
{
// Are both sections close together?
double dist = current.work->boundingBox.distanceTo(workingSet[i].work->boundingBox);
if (dist < MAX_GROUP_DIST)
{
build.Push(workingSet[i]);
groupForSection[workingSet[i].index] = groups.Size();
workingSet.Delete(i);
i--;
continue;
}
// Also put in the same group if they share a sidedef.
bool sharing_sd = CheckForSharedSidedef(*current.section, *workingSet[i].section);
if (sharing_sd)
{
build.Push(workingSet[i]);
groupForSection[workingSet[i].index] = groups.Size();
workingSet.Delete(i);
i--;
continue;
}
}
}
Group g;
g.groupedSections = std::move(build);
groups.Push(std::move(g));
}
}
//=============================================================================
//
//
//
//=============================================================================
bool CheckForSharedSidedef(WorkSection &set1, WorkSection &set2)
{
for (auto seg : set1.segments)
{
if (seg->sidedef != nullptr)
{
for (auto seg2 : set2.segments)
{
if (seg2->sidedef == seg->sidedef) return true;
}
}
}
return false;
}
//=============================================================================
//
//
//
//=============================================================================
void ConstructOutput(FSectionContainer &output)
{
output.allSections.Resize(groups.Size());
output.allIndices.Resize(level.subsectors.Size() + level.sides.Size());
output.sectionForSubsectorPtr = &output.allIndices[0];
output.sectionForSidedefPtr = &output.allIndices[level.subsectors.Size()];
unsigned numsegments = 0;
unsigned numsides = 0;
// First index all the line segments here so that we can later do some quick referencing via the global array and count them and the distinct number of sidedefs in the section.
for (auto &group : groups)
{
for (auto &work : group.groupedSections)
{
auto &section = *work.section;
for (auto segment : section.segments)
{
if (segment->refseg)
{
segment->tempindex = numsegments++;
group.segments.Push(segment);
}
}
for (auto side : section.originalSides)
{
group.sideMap[side->Index()] = true;
}
}
numsides += group.sideMap.CountUsed();
}
output.allLines.Resize(numsegments);
output.allSides.Resize(numsides);
numsegments = 0;
numsides = 0;
// Now piece it all together
unsigned curgroup = 0;
for (auto &group : groups)
{
FSection &dest = output.allSections[curgroup];
dest.sector = &level.sectors[group.groupedSections[0].section->sectorindex];
dest.mapsection = (short)group.groupedSections[0].section->mapsection;
dest.hacked = false;
dest.lighthead = nullptr;
dest.validcount = 0;
dest.segments.Set(&output.allLines[numsegments], group.segments.Size());
dest.sides.Set(&output.allSides[numsides], group.sideMap.CountUsed());
dest.bounds = {1e32, 1e32, -1e32, -1e32};
numsegments += group.segments.Size();
for (auto &segment : group.segments)
{
// Use the indices calculated above to store these elements.
auto &fseg = output.allLines[segment->tempindex];
fseg.start = segment->start;
fseg.end = segment->end;
fseg.partner = segment->partner == nullptr ? nullptr : &output.allLines[segment->partner->tempindex];
fseg.sidedef = segment->sidedef;
dest.bounds.addVertex(fseg.start->fX(), fseg.start->fY());
dest.bounds.addVertex(fseg.end->fX(), fseg.end->fY());
}
TMap<int, bool>::Iterator it(group.sideMap);
TMap<int, bool>::Pair *pair;
while (it.NextPair(pair))
{
output.allSides[numsides++] = &level.sides[pair->Key];
}
curgroup++;
}
}
};
//=============================================================================
//
//
//
//=============================================================================
void PrintSections(FSectionContainer &container)
{
for (unsigned i = 0; i < container.allSections.Size(); i++)
{
auto &section = container.allSections[i];
Printf(PRINT_LOG, "\n\nStart of section %d sector %d, mapsection %d, bounds=(%2.3f, %2.3f, %2.3f, %2.3f)\n", i, section.sector->Index(), section.mapsection,
section.bounds.left, section.bounds.top, section.bounds.right, section.bounds.bottom);
for (unsigned j = 0; j < section.segments.Size(); j++)
{
auto &seg = section.segments[j];
if (j > 0 && seg.start != section.segments[j - 1].end)
{
Printf(PRINT_LOG, "\n");
}
FString partnerstring;
if (seg.partner)
{
if (seg.partner->sidedef) partnerstring.Format(", partner = %d (line %d)", seg.partner->sidedef->Index(), seg.partner->sidedef->linedef->Index());
else partnerstring = ", partner = seg";
}
else if (seg.sidedef && seg.sidedef->linedef)
{
partnerstring = ", one-sided line";
}
if (seg.sidedef)
{
Printf(PRINT_LOG, "segment for sidedef %d (line %d) from (%2.6f, %2.6f) to (%2.6f, %2.6f)%s\n",
seg.sidedef->Index(), seg.sidedef->linedef->Index(), seg.start->fX(), seg.start->fY(), seg.end->fX(), seg.end->fY(), partnerstring.GetChars());
}
else
{
Printf(PRINT_LOG, "segment for seg from (%2.6f, %2.6f) to (%2.6f, %2.6f)%s\n",
seg.start->fX(), seg.start->fY(), seg.end->fX(), seg.end->fY(), partnerstring.GetChars());
}
}
}
Printf(PRINT_LOG, "%d sectors, %d subsectors, %d sections\n", level.sectors.Size(), level.subsectors.Size(), container.allSections.Size());
}
//=============================================================================
//
//
//
//=============================================================================
void MakeSections()
{
FSectionContainer container;
cycle_t timer;
timer.Reset();
timer.Clock();
FSectionCreator creat;
creat.GroupSubsectors();
creat.CompileSections();
creat.MakeOutlines();
creat.MergeLines();
creat.FindOuterLoops();
creat.GroupSections();
creat.ConstructOutput(container);
timer.Unclock();
PrintSections(container);
Printf("Time = %2.3f ms\n", timer.TimeMS());
}
CCMD(makesections)
{
MakeSections();
}

View File

@ -0,0 +1,126 @@
#ifndef __GL_SECTIONS_H
#define __GL_SECTIONS_H
#include "tarray.h"
#include "r_defs.h"
//==========================================================================
//
//
//
//==========================================================================
struct BoundingRect
{
double left, top, right, bottom;
bool contains(const BoundingRect & other)
{
return left <= other.left && top <= other.top && right >= other.right && bottom >= other.bottom;
}
bool intersects(const BoundingRect & other)
{
return !(other.left > right ||
other.right < left ||
other.top > bottom ||
other.bottom < top);
}
void Union(const BoundingRect & other)
{
if (other.left < left) left = other.left;
if (other.right > right) right = other.right;
if (other.top < top) top = other.top;
if (other.bottom > bottom) bottom = other.bottom;
}
double distanceTo(const BoundingRect &other)
{
if (intersects(other)) return 0;
return std::max(std::min(fabs(left - other.right), fabs(right - other.left)),
std::min(fabs(top - other.bottom), fabs(bottom - other.top)));
}
void addVertex(double x, double y)
{
if (x < left) left = x;
if (x > right) right = x;
if (y < top) top = y;
if (y > bottom) bottom = y;
}
bool operator == (const BoundingRect &other)
{
return left == other.left && top == other.top && right == other.right && bottom == other.bottom;
}
};
struct FSectionLine
{
vertex_t *start;
vertex_t *end;
FSectionLine *partner;
side_t *sidedef;
};
struct FSection
{
// tbd: Do we need a list of subsectors here? Ideally the subsectors should not be used anywhere anymore except for finding out where a location is.
TArrayView<FSectionLine> segments;
TArrayView<side_t *> sides; // contains all sidedefs, including the internal ones that do not make up the outer shape. (this list is not exclusive. A sidedef can be in multiple sections!)
sector_t *sector;
FLightNode *lighthead; // Light nodes (blended and additive)
BoundingRect bounds;
int validcount;
short mapsection;
char hacked; // 1: is part of a render hack
};
class FSectionContainer
{
public:
TArray<FSectionLine> allLines;
TArray<FSection> allSections;
//TArray<vertex_t*> allVertices;
TArray<side_t *> allSides;
TArray<int> allIndices;
int *sectionForSubsectorPtr; // stored inside allIndices
int *sectionForSidedefPtr; // also stored inside allIndices;
FSection *SectionForSubsector(subsector_t *sub)
{
return SectionForSubsector(sub->Index());
}
FSection *SectionForSubsector(int ssindex)
{
return ssindex < 0 ? nullptr : &allSections[sectionForSubsectorPtr[ssindex]];
}
FSection *SectionForSidedef(side_t *side)
{
return SectionForSidedef(side->Index());
}
FSection *SectionForSidedef(int sindex)
{
return sindex < 0 ? nullptr : &allSections[sectionForSidedefPtr[sindex]];
}
void Clear()
{
allLines.Clear();
allSections.Clear();
allIndices.Clear();
}
void Reset()
{
Clear();
allLines.ShrinkToFit();
allSections.ShrinkToFit();
allIndices.ShrinkToFit();
}
};
#endif

View File

@ -4055,6 +4055,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
if (hasglnodes)
{
P_SetRenderSector();
FixMinisegReferences();
FixHoles();
}
bodyqueslot = 0;

View File

@ -160,6 +160,9 @@ bool P_LoadGLNodes(MapData * map);
bool P_CheckNodes(MapData * map, bool rebuilt, int buildtime);
bool P_CheckForGLNodes();
void P_SetRenderSector();
void FixMinisegReferences();
void FixHoles();
void ReportUnpairedMinisegs();
struct sidei_t // [RH] Only keep BOOM sidedef init stuff around for init

View File

@ -247,7 +247,7 @@ static void PrepareSectorData()
for (auto &sub : level.subsectors)
{
sub.sectorindex = (uint16_t)sub.render_sector->subsectorcount++;
sub.render_sector->subsectorcount++;
}
for (auto &sec : level.sectors)
@ -573,7 +573,244 @@ void InitRenderInfo()
}
//==========================================================================
//
// FixMinisegReferences
//
// Sometimes it can happen that two matching minisegs do not have their partner set.
// Fix that here.
//
//==========================================================================
void FixMinisegReferences()
{
TArray<seg_t *> bogussegs;
for (unsigned i = 0; i < level.segs.Size(); i++)
{
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
{
bogussegs.Push(&level.segs[i]);
}
}
for (unsigned i = 0; i < bogussegs.Size(); i++)
{
auto seg1 = bogussegs[i];
seg_t *pick = nullptr;
unsigned int picki = -1;
// Try to fix the reference: If there's exactly one other seg in the set which matches as a partner link those two segs together.
for (unsigned j = i + 1; j < bogussegs.Size(); j++)
{
auto seg2 = bogussegs[j];
if (seg1->v1 == seg2->v2 && seg2->v1 == seg1->v2 && seg1->Subsector->render_sector == seg2->Subsector->render_sector)
{
pick = seg2;
picki = j;
break;
}
}
if (pick)
{
DPrintf(DMSG_NOTIFY, "Linking miniseg pair from (%2.3f, %2.3f) -> (%2.3f, %2.3f) in sector %d\n", pick->v2->fX(), pick->v2->fY(), pick->v1->fX(), pick->v1->fY(), pick->frontsector->Index());
pick->PartnerSeg = seg1;
seg1->PartnerSeg = pick;
assert(seg1->v1 == pick->v2 && pick->v1 == seg1->v2);
bogussegs.Delete(picki);
bogussegs.Delete(i);
i--;
}
}
}
//==========================================================================
//
// FixHoles
//
// ZDBSP can leave holes in the node tree on extremely detailed maps.
// To help out the triangulator these are filled with dummy subsectors
// so that it can process the area correctly.
//
//==========================================================================
void FixHoles()
{
TArray<seg_t *> bogussegs;
TArray<TArray<seg_t *>> segloops;
for (unsigned i = 0; i < level.segs.Size(); i++)
{
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
{
bogussegs.Push(&level.segs[i]);
}
}
while (bogussegs.Size() > 0)
{
segloops.Reserve(1);
auto *segloop = &segloops.Last();
seg_t *startseg;
seg_t *checkseg;
while (bogussegs.Size() > 0)
{
bool foundsome = false;
if (segloop->Size() == 0)
{
bogussegs.Pop(startseg);
segloop->Push(startseg);
checkseg = startseg;
}
for (unsigned i = 0; i < bogussegs.Size(); i++)
{
auto seg1 = bogussegs[i];
if (seg1->v1 == checkseg->v2 && seg1->Subsector->render_sector == checkseg->Subsector->render_sector)
{
foundsome = true;
segloop->Push(seg1);
bogussegs.Delete(i);
i--;
checkseg = seg1;
if (seg1->v2 == startseg->v1)
{
// The loop is complete. Start a new one
segloops.Reserve(1);
segloop = &segloops.Last();
}
}
}
if (!foundsome)
{
if ((*segloop)[0]->v1 != segloop->Last()->v2)
{
// There was no connected seg, leaving an unclosed loop.
// Clear this and continue looking.
segloop->Clear();
}
}
}
for (unsigned i = 0; i < segloops.Size(); i++)
{
if (segloops[i].Size() == 0)
{
segloops.Delete(i);
i--;
}
}
// Add dummy entries to the level's seg and subsector arrays
if (segloops.Size() > 0)
{
// cound the number of segs to add.
unsigned segcount = 0;
for (auto &segloop : segloops)
segcount += segloop.Size();
seg_t *oldsegstartptr = &level.segs[0];
subsector_t *oldssstartptr = &level.subsectors[0];
unsigned newsegstart = level.segs.Reserve(segcount);
unsigned newssstart = level.subsectors.Reserve(segloops.Size());
seg_t *newsegstartptr = &level.segs[0];
subsector_t *newssstartptr = &level.subsectors[0];
// Now fix all references to these in the level data.
// Note that the Index() method does not work here due to the reallocation.
for (auto &seg : level.segs)
{
if (seg.PartnerSeg) seg.PartnerSeg = newsegstartptr + (seg.PartnerSeg - oldsegstartptr);
seg.Subsector = newssstartptr + (seg.Subsector - oldssstartptr);
}
for (auto &sub : level.subsectors)
{
sub.firstline = newsegstartptr + (sub.firstline - oldsegstartptr);
}
for (auto &node : level.nodes)
{
// How hideous... :(
for (auto & p : node.children)
{
auto intp = (intptr_t)p;
if (intp & 1)
{
subsector_t *sp = (subsector_t*)(intp - 1);
sp = newssstartptr + (sp - oldssstartptr);
intp = intptr_t(sp) + 1;
p = (void*)intp;
}
}
}
for (auto &segloop : segloops)
{
for (auto &seg : segloop)
{
seg = newsegstartptr + (seg - oldsegstartptr);
}
}
// The seg lists in the sidedefs and the subsector lists in the sectors are not set yet when this gets called.
// Add the new data. This doesn't care about convexity. It is never directly used to generate a primitive.
for (auto &segloop : segloops)
{
Printf("Adding dummy subsector for sector %d\n", segloop[0]->Subsector->render_sector->Index());
subsector_t &sub = level.subsectors[newssstart++];
memset(&sub, 0, sizeof(sub));
sub.sector = segloop[0]->frontsector;
sub.render_sector = segloop[0]->Subsector->render_sector;
sub.numlines = segloop.Size();
sub.firstline = &level.segs[newsegstart];
sub.flags = SSECF_HOLE;
for (auto otherseg : segloop)
{
Printf(" Adding seg from (%2.3f, %2.3f) -> (%2.3f, %2.3f)\n", otherseg->v2->fX(), otherseg->v2->fY(), otherseg->v1->fX(), otherseg->v1->fY());
seg_t &seg = level.segs[newsegstart++];
memset(&seg, 0, sizeof(seg));
seg.v1 = otherseg->v2;
seg.v2 = otherseg->v1;
seg.frontsector = seg.backsector = otherseg->backsector = otherseg->frontsector;
seg.PartnerSeg = otherseg;
otherseg->PartnerSeg = &seg;
seg.Subsector = &sub;
}
}
}
}
}
//==========================================================================
//
// ReportUnpairedMinisegs
//
// Debug routine
// reports all unpaired minisegs that couldn't be fixed by either
// explicitly pairing them or combining them to a dummy subsector
//
//==========================================================================
void ReportUnpairedMinisegs()
{
int bogus = 0;
for (unsigned i = 0; i < level.segs.Size(); i++)
{
if (level.segs[i].sidedef == nullptr && level.segs[i].PartnerSeg == nullptr)
{
Printf("Unpaired miniseg %d, sector %d, (%d: %2.6f, %2.6f) -> (%d: %2.6f, %2.6f)\n",
i, level.segs[i].Subsector->render_sector->Index(),
level.segs[i].v1->Index(), level.segs[i].v1->fX(), level.segs[i].v1->fY(),
level.segs[i].v2->Index(), level.segs[i].v2->fX(), level.segs[i].v2->fY());
bogus++;
}
}
if (bogus > 0) Printf("%d unpaired minisegs found\n", bogus);
}
//==========================================================================
//
//
@ -594,3 +831,8 @@ CCMD(listmapsections)
}
}
}
CCMD(listbadminisegs)
{
ReportUnpairedMinisegs();
}

View File

@ -1437,6 +1437,7 @@ enum
SSECF_DEGENERATE = 1,
SSECMF_DRAWN = 2,
SSECF_POLYORG = 4,
SSECF_HOLE = 8,
};
struct FPortalCoverage

View File

@ -250,6 +250,12 @@ public:
return Array[Count-1];
}
// returns address of first element
T *Data() const
{
return &Array[0];
}
unsigned int Find(const T& item) const
{
unsigned int i;
@ -267,6 +273,7 @@ public:
::new((void*)&Array[Count]) T(item);
return Count++;
}
void Append(const TArray<T> &item)
{
unsigned start = Reserve(item.Size());
@ -350,6 +357,18 @@ public:
::new ((void *)&Array[index]) T(item);
}
}
// Reserves a range of entries in the middle of the array, shifting elements as needed
void ReserveAt(unsigned int index, unsigned int amount)
{
Grow(amount);
memmove(&Array[index + amount], &Array[index], sizeof(T)*(Count - index - amount));
for (unsigned i = 0; i < amount; i++)
{
::new ((void *)&Array[index + i]) T();
}
}
void ShrinkToFit ()
{
if (Most > Count)
@ -1332,3 +1351,125 @@ public:
memset(&bytes[0], 0, bytes.Size());
}
};
// A wrapper to externally stored data.
// I would have expected something for this in the stl, but std::span is only in C++20.
template <class T>
class TArrayView
{
public:
typedef TIterator<T> iterator;
typedef TIterator<const T> const_iterator;
iterator begin()
{
return &Array[0];
}
const_iterator begin() const
{
return &Array[0];
}
const_iterator cbegin() const
{
return &Array[0];
}
iterator end()
{
return &Array[Count];
}
const_iterator end() const
{
return &Array[Count];
}
const_iterator cend() const
{
return &Array[Count];
}
////////
TArrayView() = default; // intended to keep this type trivial.
TArrayView(T *data, unsigned count = 0)
{
Count = count;
Array = data;
}
TArrayView(const TArrayView<T> &other)
{
Count = other.Count;
Array = other.Array;
}
TArrayView<T> &operator= (const TArrayView<T> &other)
{
Count = other.Count;
Array = other.Array;
return *this;
}
// Check equality of two arrays
bool operator==(const TArrayView<T> &other) const
{
if (Count != other.Count)
{
return false;
}
for (unsigned int i = 0; i < Count; ++i)
{
if (Array[i] != other.Array[i])
{
return false;
}
}
return true;
}
// Return a reference to an element
T &operator[] (size_t index) const
{
return Array[index];
}
// Returns a reference to the last element
T &Last() const
{
return Array[Count - 1];
}
// returns address of first element
T *Data() const
{
return &Array[0];
}
unsigned Size() const
{
return Count;
}
unsigned int Find(const T& item) const
{
unsigned int i;
for (i = 0; i < Count; ++i)
{
if (Array[i] == item)
break;
}
return i;
}
void Set(T *data, unsigned count)
{
Array = data;
Count = count;
}
void Clear()
{
Count = 0;
Array = nullptr;
}
private:
T *Array;
unsigned int Count;
};

View File

@ -1,845 +0,0 @@
/*
** gl_sections.cpp
** Splits sectors into continuous separate parts
**
**---------------------------------------------------------------------------
** Copyright 2008 Christoph Oelckers
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be
** covered by the terms of the GNU Lesser General Public License as published
** by the Free Software Foundation; either version 2.1 of the License, or (at
** your option) any later version.
** 5. Full disclosure of the entire project's source code, except for third
** party libraries is mandatory. (NOTE: This clause is non-negotiable!)
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "gl/system/gl_system.h"
#include <GL/GLU.h>
#include "i_system.h"
#include "p_local.h"
#include "c_dispatch.h"
#include "gl/data/gl_sections.h"
typedef void (CALLBACK *tessFunc)();
TArray<FGLSectionLine> SectionLines;
TArray<FGLSectionLoop> SectionLoops;
TArray<FGLSection> Sections;
TArray<int> SectionForSubsector;
CVAR (Bool, dumpsections, false, 0)
#define ISDONE(no, p) (p[(no)>>3] & (1 << ((no)&7)))
#define SETDONE(no, p) p[(no)>>3] |= (1 << ((no)&7))
inline vertex_t *V1(side_t *s)
{
line_t *ln = s->linedef;
return s == ln->sidedef[0]? ln->v1: ln->v2;
}
inline vertex_t *V2(side_t *s)
{
line_t *ln = s->linedef;
return s == ln->sidedef[0]? ln->v2: ln->v1;
}
//==========================================================================
//
//
//
//==========================================================================
class FSectionCreator
{
static FSectionCreator *creator;
uint8_t *processed_segs;
uint8_t *processed_subsectors;
int *section_for_segs;
vertex_t *v1_l1, *v2_l1;
FGLSectionLoop *loop;
FGLSection *section; // current working section
public:
//==========================================================================
//
//
//
//==========================================================================
FSectionCreator()
{
processed_segs = new uint8_t[(numsegs+7)/8];
processed_subsectors = new uint8_t[(numsubsectors+7)/8];
memset(processed_segs, 0, (numsegs+7)/8);
memset(processed_subsectors, 0, (numsubsectors+7)/8);
section_for_segs = new int[numsegs];
memset(section_for_segs, -1, numsegs * sizeof(int));
}
//==========================================================================
//
//
//
//==========================================================================
~FSectionCreator()
{
delete [] processed_segs;
delete [] processed_subsectors;
delete [] section_for_segs;
}
//==========================================================================
//
//
//
//==========================================================================
void NewLoop()
{
section->numloops++;
loop = &SectionLoops[SectionLoops.Reserve(1)];
loop->startline = SectionLines.Size();
loop->numlines = 0 ;
}
void NewSection(sector_t *sec)
{
section = &Sections[Sections.Reserve(1)];
section->sector = sec;
section->subsectors.Clear();
section->numloops = 0;
section->startloop = SectionLoops.Size();
section->validcount = -1;
NewLoop();
}
void FinalizeSection()
{
}
//==========================================================================
//
//
//
//==========================================================================
bool AddSeg(seg_t *seg)
{
FGLSectionLine &line = SectionLines[SectionLines.Reserve(1)];
bool firstline = loop->numlines == 0;
if (ISDONE(seg-segs, processed_segs))
{
// should never happen!
DPrintf("Tried to add seg %d to Sections twice. Cannot create Sections.\n", seg-segs);
return false;
}
SETDONE(seg-segs, processed_segs);
section_for_segs[seg-segs] = Sections.Size()-1;
line.start = seg->v1;
line.end = seg->v2;
line.sidedef = seg->sidedef;
line.linedef = seg->linedef;
line.refseg = seg;
line.polysub = NULL;
line.otherside = -1;
if (loop->numlines == 0)
{
v1_l1 = seg->v1;
v2_l1 = seg->v2;
}
loop->numlines++;
return true;
}
//==========================================================================
//
// Utility stuff
//
//==========================================================================
sector_t *FrontRenderSector(seg_t *seg)
{
return seg->Subsector->render_sector;
}
sector_t *BackRenderSector(seg_t *seg)
{
if (seg->PartnerSeg == NULL) return NULL;
return seg->PartnerSeg->Subsector->render_sector;
}
bool IntraSectorSeg(seg_t *seg)
{
return FrontRenderSector(seg) == BackRenderSector(seg);
}
//==========================================================================
//
// returns the seg whose partner seg determines where this
// section continues
//
//==========================================================================
bool AddSubSector(subsector_t *subsec, vertex_t *startpt, seg_t **pNextSeg)
{
unsigned i = 0;
if (startpt != NULL)
{
// find the seg in this subsector that starts at the given vertex
for(i = 0; i < subsec->numlines; i++)
{
if (subsec->firstline[i].v1 == startpt) break;
}
if (i == subsec->numlines)
{
DPrintf("Vertex not found in subsector %d. Cannot create Sections.\n", subsec-subsectors);
return false; // Nodes are bad
}
}
else
{
// Find the first unprocessed non-miniseg
for(i = 0; i < subsec->numlines; i++)
{
seg_t *seg = subsec->firstline + i;
if (seg->sidedef == NULL) continue;
if (IntraSectorSeg(seg)) continue;
if (ISDONE(seg-segs, processed_segs)) continue;
break;
}
if (i == subsec->numlines)
{
DPrintf("Unable to find a start seg. Cannot create Sections.\n");
return false; // Nodes are bad
}
startpt = subsec->firstline[i].v1;
}
seg_t *thisseg = subsec->firstline + i;
if (IntraSectorSeg(thisseg))
{
SETDONE(thisseg-segs, processed_segs);
// continue with the loop in the adjoining subsector
*pNextSeg = thisseg;
return true;
}
while(1)
{
if (loop->numlines > 0 && thisseg->v1 == v1_l1 && thisseg->v2 == v2_l1)
{
// This loop is complete
*pNextSeg = NULL;
return true;
}
if (!AddSeg(thisseg)) return NULL;
i = (i+1) % subsec->numlines;
seg_t *nextseg = subsec->firstline + i;
if (thisseg->v2 != nextseg->v1)
{
DPrintf("Segs in subsector %d are not continuous. Cannot create Sections.\n", subsec-subsectors);
return false; // Nodes are bad
}
if (IntraSectorSeg(nextseg))
{
SETDONE(nextseg-segs, processed_segs);
// continue with the loop in the adjoining subsector
*pNextSeg = nextseg;
return true;
}
thisseg = nextseg;
}
}
//=============================================================================
//
//
//
//=============================================================================
bool FindNextSeg(seg_t **pSeg)
{
// find an unprocessed non-miniseg or a miniseg with an unprocessed
// partner subsector that belongs to the same rendersector
for (unsigned i = 0; i < section->subsectors.Size(); i++)
{
for(unsigned j = 0; j < section->subsectors[i]->numlines; j++)
{
seg_t *seg = section->subsectors[i]->firstline + j;
bool intra = IntraSectorSeg(seg);
if (!intra && !ISDONE(seg-segs, processed_segs))
{
*pSeg = seg;
return true;
}
else if (intra &&
!ISDONE(seg->PartnerSeg->Subsector-subsectors, processed_subsectors))
{
*pSeg = seg->PartnerSeg;
return true;
}
}
}
*pSeg = NULL;
return true;
}
//=============================================================================
//
// all segs and subsectors must be grouped into Sections
//
//=============================================================================
bool CheckSections()
{
bool res = true;
for (int i = 0; i < numsegs; i++)
{
if (segs[i].sidedef != NULL && !ISDONE(i, processed_segs) && !IntraSectorSeg(&segs[i]))
{
Printf("Seg %d (Linedef %d) not processed during section creation\n", i, segs[i].linedef-lines);
res = false;
}
}
for (int i = 0; i < numsubsectors; i++)
{
if (!ISDONE(i, processed_subsectors))
{
Printf("Subsector %d (Sector %d) not processed during section creation\n", i, subsectors[i].sector-sectors);
res = false;
}
}
return res;
}
//=============================================================================
//
//
//
//=============================================================================
void DeleteLine(int i)
{
SectionLines.Delete(i);
for(int i = SectionLoops.Size() - 1; i >= 0; i--)
{
FGLSectionLoop *loop = &SectionLoops[i];
if (loop->startline > i) loop->startline--;
}
}
//=============================================================================
//
//
//
//=============================================================================
void MergeLines(FGLSectionLoop *loop)
{
int i;
int deleted = 0;
FGLSectionLine *ln1;
FGLSectionLine *ln2;
// Merge identical lines in the list
for(i = loop->numlines - 1; i > 0; i--)
{
ln1 = loop->GetLine(i);
ln2 = loop->GetLine(i-1);
if (ln1->sidedef == ln2->sidedef && ln1->otherside == ln2->otherside)
{
// identical references. These 2 lines can be merged.
ln2->end = ln1->end;
SectionLines.Delete(loop->startline + i);
loop->numlines--;
deleted++;
}
}
// If we started in the middle of a sidedef the first and last lines
// may reference the same sidedef. check that, too.
int loopstart = 0;
ln1 = loop->GetLine(0);
for(i = loop->numlines - 1; i > 0; i--)
{
ln2 = loop->GetLine(i);
if (ln1->sidedef != ln2->sidedef || ln1->otherside != ln2->otherside)
break;
}
if (i < loop->numlines-1)
{
i++;
ln2 = loop->GetLine(i);
ln1->start = ln2->start;
SectionLines.Delete(loop->startline + i, loop->numlines - i);
deleted += loop->numlines - i;
loop->numlines = i;
}
// Adjust all following loops
for(unsigned ii = unsigned(loop - &SectionLoops[0]) + 1; ii < SectionLoops.Size(); ii++)
{
SectionLoops[ii].startline -= deleted;
}
}
//=============================================================================
//
//
//
//=============================================================================
void SetReferences()
{
for(unsigned i = 0; i < SectionLines.Size(); i++)
{
FGLSectionLine *ln = &SectionLines[i];
seg_t *seg = ln->refseg;
if (seg != NULL)
{
seg_t *partner = seg->PartnerSeg;
if (seg->PartnerSeg == NULL)
{
ln->otherside = -1;
}
else
{
ln->otherside = section_for_segs[partner-segs];
}
}
else
{
ln->otherside = -1;
}
}
for(unsigned i = 0; i < SectionLoops.Size(); i++)
{
MergeLines(&SectionLoops[i]);
}
}
//=============================================================================
//
// cbTessBegin
//
// called when the tesselation of a new loop starts
//
//=============================================================================
static void CALLBACK cbTessBegin(GLenum type, void *section)
{
FGLSection *sect = (FGLSection*)section;
sect->primitives.Push(type);
sect->primitives.Push(sect->vertices.Size());
}
//=============================================================================
//
// cbTessError
//
// called when the tesselation failed
//
//=============================================================================
static void CALLBACK cbTessError(GLenum error, void *section)
{
}
//=============================================================================
//
// cbTessCombine
//
// called when the two or more vertexes are on the same coordinate
//
//=============================================================================
static void CALLBACK cbTessCombine( GLdouble coords[3], void *vert[4], GLfloat w[4], void **dataOut )
{
*dataOut = vert[0];
}
//=============================================================================
//
// cbTessVertex
//
// called when a vertex is found
//
//=============================================================================
static void CALLBACK cbTessVertex( void *vert, void *section )
{
FGLSection *sect = (FGLSection*)section;
sect->vertices.Push(int(intptr_t(vert)));
}
//=============================================================================
//
// cbTessEnd
//
// called when the tesselation of a the current loop ends
//
//=============================================================================
static void CALLBACK cbTessEnd(void *section)
{
}
//=============================================================================
//
//
//
//=============================================================================
void tesselateSections()
{
// init tesselator
GLUtesselator *tess = gluNewTess();
if (!tess)
{
return;
}
// set callbacks
gluTessCallback(tess, GLU_TESS_BEGIN_DATA, (tessFunc)cbTessBegin);
gluTessCallback(tess, GLU_TESS_VERTEX_DATA, (tessFunc)cbTessVertex);
gluTessCallback(tess, GLU_TESS_ERROR_DATA, (tessFunc)cbTessError);
gluTessCallback(tess, GLU_TESS_COMBINE, (tessFunc)cbTessCombine);
gluTessCallback(tess, GLU_TESS_END_DATA, (tessFunc)cbTessEnd);
for(unsigned int i=0;i<Sections.Size(); i++)
{
FGLSection *sect = &Sections[i];
gluTessBeginPolygon(tess, sect);
for(int j=0; j< sect->numloops; j++)
{
gluTessBeginContour(tess);
FGLSectionLoop *loop = sect->GetLoop(j);
for(int k=0; k<loop->numlines; k++)
{
FGLSectionLine *line = loop->GetLine(k);
vertex_t *vert = line->start;
GLdouble v[3] = {
-(double)vert->x/(double)FRACUNIT, // negate to get proper winding
0.0,
(double)vert->y/(double)FRACUNIT
};
gluTessVertex(tess, v, (void*)(vert - vertexes));
}
gluTessEndContour(tess);
}
gluTessEndPolygon(tess);
sect->vertices.Push(-1337);
sect->vertices.ShrinkToFit();
}
gluDeleteTess(tess);
}
//=============================================================================
//
// First mark all subsectors that have no outside boundaries as processed
// No line in such a subsector will ever be part of a section's border
//
//=============================================================================
void MarkInternalSubsectors()
{
for(int i=0; i < numsubsectors; i++)
{
subsector_t *sub = &subsectors[i];
int j;
for(j=0; j < sub->numlines; j++)
{
seg_t *seg = sub->firstline + j;
if (!IntraSectorSeg(seg)) break;
}
if (j==sub->numlines)
{
// All lines are intra-sector so mark this as processed
SETDONE(i, processed_subsectors);
for(j=0; j < sub->numlines; j++)
{
seg_t *seg = sub->firstline + j;
SETDONE((sub->firstline-segs)+j, processed_segs);
if (seg->PartnerSeg != NULL)
{
SETDONE(int(seg->PartnerSeg - segs), processed_segs);
}
}
}
}
}
//=============================================================================
//
//
//
//=============================================================================
bool CreateSections()
{
int pick = 0;
MarkInternalSubsectors();
while (pick < numsubsectors)
{
if (ISDONE(pick, processed_subsectors))
{
pick++;
continue;
}
subsector_t *subsector = &subsectors[pick];
seg_t *workseg = NULL;
vertex_t *startpt = NULL;
NewSection(subsector->render_sector);
while (1)
{
if (!ISDONE(subsector-subsectors, processed_subsectors))
{
SETDONE(subsector-subsectors, processed_subsectors);
section->subsectors.Push(subsector);
SectionForSubsector[subsector - subsectors] = int(section - &Sections[0]);
}
bool result = AddSubSector(subsector, startpt, &workseg);
if (!result)
{
return false; // couldn't create Sections
}
else if (workseg != NULL)
{
// crossing into another subsector
seg_t *partner = workseg->PartnerSeg;
if (workseg->v2 != partner->v1)
{
DPrintf("Inconsistent subsector references in seg %d. Cannot create Sections.\n", workseg-segs);
return false;
}
subsector = partner->Subsector;
startpt = workseg->v1;
}
else
{
// loop complete. Check adjoining subsectors for other loops to
// be added to this section
if (!FindNextSeg(&workseg))
{
return false;
}
else if (workseg == NULL)
{
// No more subsectors found. This section is complete!
FinalizeSection();
break;
}
else
{
subsector = workseg->Subsector;
// If this is a regular seg, start there, otherwise start
// at the subsector's first seg
startpt = workseg->sidedef == NULL? NULL : workseg->v1;
NewLoop();
}
}
}
}
if (!CheckSections()) return false;
SetReferences();
Sections.ShrinkToFit();
SectionLoops.ShrinkToFit();
SectionLines.ShrinkToFit();
tesselateSections();
return true;
}
};
FSectionCreator *FSectionCreator::creator;
//=============================================================================
//
//
//
//=============================================================================
void DumpSection(int no, FGLSection *sect)
{
Printf(PRINT_LOG, "Section %d, sector %d\n{\n", no, sect->sector->sectornum);
for(int i = 0; i < sect->numloops; i++)
{
Printf(PRINT_LOG, "\tLoop %d\n\t{\n", i);
FGLSectionLoop *loop = sect->GetLoop(i);
for(int i = 0; i < loop->numlines; i++)
{
FGLSectionLine *ln = loop->GetLine(i);
if (ln->sidedef != NULL)
{
vertex_t *v1 = V1(ln->sidedef);
vertex_t *v2 = V2(ln->sidedef);
double dx = FIXED2DBL(v2->x-v1->x);
double dy = FIXED2DBL(v2->y-v1->y);
double dx1 = FIXED2DBL(ln->start->x-v1->x);
double dy1 = FIXED2DBL(ln->start->y-v1->y);
double dx2 = FIXED2DBL(ln->end->x-v1->x);
double dy2 = FIXED2DBL(ln->end->y-v1->y);
double d = sqrt(dx*dx+dy*dy);
double d1 = sqrt(dx1*dx1+dy1*dy1);
double d2 = sqrt(dx2*dx2+dy2*dy2);
Printf(PRINT_LOG, "\t\tLinedef %d, %s: Start (%1.2f, %1.2f), End (%1.2f, %1.2f)",
ln->linedef - lines, ln->sidedef == ln->linedef->sidedef[0]? "front":"back",
ln->start->x/65536.f, ln->start->y/65536.f,
ln->end->x/65536.f, ln->end->y/65536.f);
if (ln->otherside != -1)
{
Printf (PRINT_LOG, ", other side = %d", ln->otherside);
}
if (d1 > 0.005 || d2 < 0.995)
{
Printf(PRINT_LOG, ", Range = %1.3f, %1.3f", d1/d, d2/d);
}
}
else
{
Printf(PRINT_LOG, "\t\tMiniseg: Start (%1.3f, %1.3f), End (%1.3f, %1.3f)\n",
ln->start->x/65536.f, ln->start->y/65536.f, ln->end->x/65536.f, ln->end->y/65536.f);
if (ln->otherside != -1)
{
Printf (PRINT_LOG, ", other side = %d", ln->otherside);
}
}
Printf(PRINT_LOG, "\n");
}
Printf(PRINT_LOG, "\t}\n");
}
int prim = 1;
for(unsigned i = 0; i < sect->vertices.Size(); i++)
{
int v = sect->vertices[i];
if (v < 0)
{
if (i > 0)
{
Printf(PRINT_LOG, "\t}\n");
}
switch (v)
{
case -GL_TRIANGLE_FAN:
Printf(PRINT_LOG, "\t%d: Triangle fan\n\t{\n", prim);
break;
case -GL_TRIANGLE_STRIP:
Printf(PRINT_LOG, "\t%d: Triangle strip\n\t{\n", prim);
break;
case -GL_TRIANGLES:
Printf(PRINT_LOG, "\t%d: Triangles\n\t{\n", prim);
break;
default:
break;
}
prim++;
}
else
{
Printf(PRINT_LOG, "\t\tVertex %d: (%1.2f, %1.2f)\n",
v, vertexes[v].x/65536.f, vertexes[v].y/65536.f);
}
}
Printf(PRINT_LOG, "}\n\n");
}
//=============================================================================
//
//
//
//=============================================================================
void DumpSections()
{
for(unsigned i = 0; i < Sections.Size(); i++)
{
DumpSection(i, &Sections[i]);
}
}
//=============================================================================
//
//
//
//=============================================================================
void gl_CreateSections()
{
SectionLines.Clear();
SectionLoops.Clear();
Sections.Clear();
SectionForSubsector.Resize(numsubsectors);
memset(&SectionForSubsector[0], -1, numsubsectors * sizeof(SectionForSubsector[0]));
FSectionCreator creat;
creat.CreateSections();
if (dumpsections) DumpSections();
}

View File

@ -1,57 +0,0 @@
#ifndef __GL_SECTIONS_H
#define __GL_SECTIONS_H
#include "tarray.h"
#include "r_defs.h"
struct FGLSectionLine
{
vertex_t *start;
vertex_t *end;
side_t *sidedef;
line_t *linedef;
seg_t *refseg; // we need to reference at least one seg for each line.
subsector_t *polysub; // If this is part of a polyobject we need a reference to the containing subsector
int otherside;
};
struct FGLSectionLoop
{
int startline;
int numlines;
FGLSectionLine *GetLine(int no);
};
struct FGLSection
{
sector_t *sector;
TArray<subsector_t *> subsectors;
TArray<int> primitives; // each primitive has 2 entries: Primitive type and vertex count
TArray<int> vertices;
int startloop;
int numloops;
int validcount;
FGLSectionLoop *GetLoop(int no);
};
extern TArray<FGLSectionLine> SectionLines;
extern TArray<FGLSectionLoop> SectionLoops;
extern TArray<FGLSection> Sections;
extern TArray<int> SectionForSubsector;
inline FGLSectionLine *FGLSectionLoop::GetLine(int no)
{
return &SectionLines[startline + no];
}
inline FGLSectionLoop *FGLSection::GetLoop(int no)
{
return &SectionLoops[startloop + no];
}
void gl_CreateSections();
#endif