From 49bfdbef9f0643e495c80734762822f429586df5 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 4 Nov 2018 16:47:45 +0100 Subject: [PATCH] - 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. --- src/CMakeLists.txt | 1 + src/am_map.cpp | 2 +- src/hwrenderer/data/hw_sections.cpp | 764 +++++++++++++++++++++++++ src/hwrenderer/data/hw_sections.h | 126 +++++ src/p_setup.cpp | 2 + src/p_setup.h | 3 + src/r_data/renderinfo.cpp | 244 +++++++- src/r_defs.h | 1 + src/tarray.h | 141 +++++ unused/gl_sections.cpp | 845 ---------------------------- unused/gl_sections.h | 57 -- 11 files changed, 1282 insertions(+), 904 deletions(-) create mode 100644 src/hwrenderer/data/hw_sections.cpp create mode 100644 src/hwrenderer/data/hw_sections.h delete mode 100644 unused/gl_sections.cpp delete mode 100644 unused/gl_sections.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5ebda5b16..559379a6d9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/am_map.cpp b/src/am_map.cpp index e0296e797a..58ac470855 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -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; } diff --git a/src/hwrenderer/data/hw_sections.cpp b/src/hwrenderer/data/hw_sections.cpp new file mode 100644 index 0000000000..9364b9712b --- /dev/null +++ b/src/hwrenderer/data/hw_sections.cpp @@ -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 +#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; + +template<> struct THashTraits +{ + // 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; + TArraysegments; + TArray 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 groupedSections; + TMap sideMap; + TArray segments; +}; + +class FSectionCreator +{ + FMemArena allocator; + TArrayAllAllocatedLines; + + bool verbose = false; + TMap> subsectormap; + TArray> rawsections; // list of unprocessed subsectors. Sector and mapsection can be retrieved from the elements so aren't stored. + TArray sections; + + TArray triangles; + + TArray groups; + TArray 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>::Pair *pair; + TMap>::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 &list) + { + TArray sublist; + TArray 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 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 §ion : 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 &rawsection, TArray &lineForSeg) + { + TArray foundsides; + TArray outersegs; + TArray 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 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 §ion = 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 §ion = 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 workingSet; + + GroupWork first = { §ions[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 = §ions[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 = { §ions[i], &triangles[i], (int)i }; + workingSet.Push(first); + } + } + GroupWorkingSet(workingSet); + } + + //============================================================================= + // + // + // + //============================================================================= + + void GroupWorkingSet(TArray &workingSet) + { + const double MAX_GROUP_DIST = 256; + TArray 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 ¤t = 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 §ion = *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::Iterator it(group.sideMap); + TMap::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 §ion = 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(); +} \ No newline at end of file diff --git a/src/hwrenderer/data/hw_sections.h b/src/hwrenderer/data/hw_sections.h new file mode 100644 index 0000000000..1cc6f2efa2 --- /dev/null +++ b/src/hwrenderer/data/hw_sections.h @@ -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 segments; + TArrayView 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 allLines; + TArray allSections; + //TArray allVertices; + TArray allSides; + TArray 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 \ No newline at end of file diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 4a48afbb64..d3ff4c4999 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -4055,6 +4055,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame) if (hasglnodes) { P_SetRenderSector(); + FixMinisegReferences(); + FixHoles(); } bodyqueslot = 0; diff --git a/src/p_setup.h b/src/p_setup.h index 31612894b8..1f773e951f 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -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 diff --git a/src/r_data/renderinfo.cpp b/src/r_data/renderinfo.cpp index f0b8600fec..bf59ec0c0a 100644 --- a/src/r_data/renderinfo.cpp +++ b/src/r_data/renderinfo.cpp @@ -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 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 bogussegs; + TArray> 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 = ⊂ + } + } + } + } +} + +//========================================================================== +// +// 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(); +} diff --git a/src/r_defs.h b/src/r_defs.h index f3da27262d..4bccef5269 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -1437,6 +1437,7 @@ enum SSECF_DEGENERATE = 1, SSECMF_DRAWN = 2, SSECF_POLYORG = 4, + SSECF_HOLE = 8, }; struct FPortalCoverage diff --git a/src/tarray.h b/src/tarray.h index 153fcd0ba5..79b8853bd0 100644 --- a/src/tarray.h +++ b/src/tarray.h @@ -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 &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 TArrayView +{ +public: + + typedef TIterator iterator; + typedef TIterator 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 &other) + { + Count = other.Count; + Array = other.Array; + } + TArrayView &operator= (const TArrayView &other) + { + Count = other.Count; + Array = other.Array; + return *this; + } + // Check equality of two arrays + bool operator==(const TArrayView &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; +}; + diff --git a/unused/gl_sections.cpp b/unused/gl_sections.cpp deleted file mode 100644 index 08a68be8e8..0000000000 --- a/unused/gl_sections.cpp +++ /dev/null @@ -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 -#include "i_system.h" -#include "p_local.h" -#include "c_dispatch.h" -#include "gl/data/gl_sections.h" - -typedef void (CALLBACK *tessFunc)(); - -TArray SectionLines; -TArray SectionLoops; -TArray Sections; -TArray 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;inumloops; j++) - { - gluTessBeginContour(tess); - FGLSectionLoop *loop = sect->GetLoop(j); - for(int k=0; knumlines; 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(); -} - diff --git a/unused/gl_sections.h b/unused/gl_sections.h deleted file mode 100644 index 1974fab467..0000000000 --- a/unused/gl_sections.h +++ /dev/null @@ -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 subsectors; - TArray primitives; // each primitive has 2 entries: Primitive type and vertex count - TArray vertices; - int startloop; - int numloops; - int validcount; - - FGLSectionLoop *GetLoop(int no); -}; - -extern TArray SectionLines; -extern TArray SectionLoops; -extern TArray Sections; -extern TArray 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 \ No newline at end of file