- 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:
parent
b1d35eb0b3
commit
49bfdbef9f
11 changed files with 1282 additions and 904 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
764
src/hwrenderer/data/hw_sections.cpp
Normal file
764
src/hwrenderer/data/hw_sections.cpp
Normal 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 §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<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 §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<GroupWork> 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<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 ¤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<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 §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();
|
||||
}
|
||||
126
src/hwrenderer/data/hw_sections.h
Normal file
126
src/hwrenderer/data/hw_sections.h
Normal 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
|
||||
|
|
@ -4055,6 +4055,8 @@ void P_SetupLevel (const char *lumpname, int position, bool newGame)
|
|||
if (hasglnodes)
|
||||
{
|
||||
P_SetRenderSector();
|
||||
FixMinisegReferences();
|
||||
FixHoles();
|
||||
}
|
||||
|
||||
bodyqueslot = 0;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 = ⊂
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// 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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1437,6 +1437,7 @@ enum
|
|||
SSECF_DEGENERATE = 1,
|
||||
SSECMF_DRAWN = 2,
|
||||
SSECF_POLYORG = 4,
|
||||
SSECF_HOLE = 8,
|
||||
};
|
||||
|
||||
struct FPortalCoverage
|
||||
|
|
|
|||
141
src/tarray.h
141
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<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;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue