mirror of
https://github.com/ZDoom/Raze.git
synced 2025-04-07 00:11:22 +00:00
- WindowClipper WIP
This commit is contained in:
parent
1e63c8305b
commit
213ed28398
4 changed files with 330 additions and 83 deletions
|
@ -62,6 +62,7 @@ void BunchDrawer::Init(HWDrawInfo *_di, Clipper* c, vec2_t& view, binangle a1, b
|
|||
clipper = c;
|
||||
viewx = view.x * (1/ 16.f);
|
||||
viewy = view.y * -(1/ 16.f);
|
||||
viewz = (float)di->Viewpoint.Pos.Z;
|
||||
iview = view;
|
||||
StartScene();
|
||||
|
||||
|
@ -149,16 +150,12 @@ void BunchDrawer::DeleteBunch(int index)
|
|||
Bunches.Pop();
|
||||
}
|
||||
|
||||
bool BunchDrawer::CheckClip(walltype* wal)
|
||||
bool BunchDrawer::CheckClip(walltype* wal, float* topclip, float* bottomclip)
|
||||
{
|
||||
auto pt2 = wal->point2Wall();
|
||||
sectortype* backsector = wal->nextSector();
|
||||
sectortype* frontsector = wal->sectorp();
|
||||
|
||||
// if one plane is sky on both sides, the line must not clip.
|
||||
if (frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY) return false;
|
||||
if (frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY) return false;
|
||||
|
||||
float bs_floorheight1;
|
||||
float bs_floorheight2;
|
||||
float bs_ceilingheight1;
|
||||
|
@ -177,7 +174,27 @@ bool BunchDrawer::CheckClip(walltype* wal)
|
|||
PlanesAtPoint(backsector, wal->x, wal->y, &bs_ceilingheight1, &bs_floorheight1);
|
||||
PlanesAtPoint(backsector, pt2->x, pt2->y, &bs_ceilingheight2, &bs_floorheight2);
|
||||
|
||||
// now check for closed sectors! No idea if we really need the sky checks. We'll see.
|
||||
*bottomclip = max(max(bs_floorheight1, bs_floorheight2), max(fs_floorheight1, fs_floorheight2));
|
||||
if (*bottomclip < viewz) *bottomclip = -FLT_MAX;
|
||||
|
||||
// if one plane is sky on both sides, the line must not clip.
|
||||
if (frontsector->ceilingstat & backsector->ceilingstat & CSTAT_SECTOR_SKY)
|
||||
{
|
||||
// save some processing with outside areas - no need to add to the clipper if back sector is higher.
|
||||
if (fs_ceilingheight1 <= bs_floorheight1 && fs_ceilingheight2 <= bs_floorheight2) *bottomclip = -FLT_MAX;
|
||||
*topclip = FLT_MAX;
|
||||
return false;
|
||||
}
|
||||
*topclip = min(min(bs_ceilingheight1, bs_ceilingheight2), min(fs_ceilingheight1, fs_ceilingheight2));
|
||||
if (*topclip > viewz) *topclip = FLT_MAX;
|
||||
|
||||
if (frontsector->floorstat & backsector->floorstat & CSTAT_SECTOR_SKY)
|
||||
{
|
||||
*bottomclip = -FLT_MAX;
|
||||
return false;
|
||||
}
|
||||
|
||||
// now check for closed sectors.
|
||||
if (bs_ceilingheight1 <= fs_floorheight1 && bs_ceilingheight2 <= fs_floorheight2)
|
||||
{
|
||||
// backsector's ceiling is below frontsector's floor.
|
||||
|
@ -258,15 +275,30 @@ int BunchDrawer::ClipLine(int aline, bool portal)
|
|||
if (line < 0)
|
||||
return CL_Pass;
|
||||
|
||||
if (cline->partner == -1 || (wall[line].cstat & CSTAT_WALL_1WAY) || CheckClip(&wall[line]))
|
||||
float topclip = 0, bottomclip = 0;
|
||||
if (cline->partner == -1 || (wall[line].cstat & CSTAT_WALL_1WAY) || CheckClip(&wall[line], &topclip, &bottomclip))
|
||||
{
|
||||
// one-sided
|
||||
if (!portal && !dontclip) clipper->AddClipRange(startAngle, endAngle);
|
||||
if (!portal && !dontclip)
|
||||
{
|
||||
clipper->AddClipRange(startAngle, endAngle);
|
||||
Printf("\nWall %d from %2.3f - %2.3f (blocking)\n", line, bamang(startAngle).asdeg(), bamang(endAngle).asdeg());
|
||||
clipper->DumpClipper();
|
||||
}
|
||||
return CL_Draw;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (portal) clipper->RemoveClipRange(startAngle, endAngle);
|
||||
else
|
||||
{
|
||||
if (topclip < viewz || bottomclip > viewz)
|
||||
{
|
||||
clipper->AddWindowRange(startAngle, endAngle, topclip, bottomclip);
|
||||
Printf("\nWall %d from %2.3f - %2.3f, (%2.3f, %2.3f) (passing)\n", line, bamang(startAngle).asdeg(), bamang(endAngle).asdeg(), topclip, bottomclip);
|
||||
clipper->DumpClipper();
|
||||
}
|
||||
}
|
||||
|
||||
// set potentially visible viewing range for this line's back sector.
|
||||
int nsection = cline->partnersection;
|
||||
|
@ -648,7 +680,7 @@ void BunchDrawer::ProcessSection(int sectionnum, bool portal)
|
|||
|
||||
void BunchDrawer::RenderScene(const int* viewsectors, unsigned sectcount, bool portal)
|
||||
{
|
||||
//Printf("----------------------------------------- \nstart at sector %d\n", viewsectors[0]);
|
||||
Printf("----------------------------------------- \nstart at sector %d\n", viewsectors[0]);
|
||||
auto process = [&]()
|
||||
{
|
||||
clipper->Clear(ang1);
|
||||
|
|
|
@ -32,6 +32,7 @@ class BunchDrawer
|
|||
BitArray gotwall;
|
||||
BitArray blockwall;
|
||||
binangle ang1, ang2, angrange;
|
||||
float viewz;
|
||||
|
||||
TArray<int> sectionstartang, sectionendang;
|
||||
|
||||
|
@ -49,7 +50,7 @@ private:
|
|||
bool StartBunch(int sectnum, int linenum, binangle startan, binangle endan, bool portal);
|
||||
bool AddLineToBunch(int line, binangle newan);
|
||||
void DeleteBunch(int index);
|
||||
bool CheckClip(walltype* wal);
|
||||
bool CheckClip(walltype* wal, float* topclip, float* bottomclip);
|
||||
int ClipLine(int line, bool portal);
|
||||
void ProcessBunch(int bnch);
|
||||
int WallInFront(int wall1, int wall2);
|
||||
|
|
|
@ -52,6 +52,7 @@ void Clipper::RemoveRange(ClipNode * range)
|
|||
if (range == cliphead)
|
||||
{
|
||||
cliphead = cliphead->next;
|
||||
if (cliphead) cliphead->prev = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -62,6 +63,22 @@ void Clipper::RemoveRange(ClipNode * range)
|
|||
Free(range);
|
||||
}
|
||||
|
||||
void Clipper::InsertRange(ClipNode* prev, ClipNode* node)
|
||||
{
|
||||
if (prev)
|
||||
{
|
||||
node->next = prev->next;
|
||||
prev->next = node;
|
||||
}
|
||||
else
|
||||
{
|
||||
node->next = cliphead;
|
||||
cliphead = node;
|
||||
}
|
||||
node->prev = prev;
|
||||
if (node->next) node->next->prev = node;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// Clear
|
||||
|
@ -109,7 +126,7 @@ bool Clipper::IsRangeVisible(int startAngle, int endAngle)
|
|||
|
||||
while (ci != nullptr && ci->start < endAngle)
|
||||
{
|
||||
if (startAngle >= ci->start && endAngle <= ci->end)
|
||||
if (startAngle >= ci->start && endAngle <= ci->end && ci->topclip <= ci->bottomclip)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -127,99 +144,300 @@ bool Clipper::IsRangeVisible(int startAngle, int endAngle)
|
|||
|
||||
void Clipper::AddClipRange(int start, int end)
|
||||
{
|
||||
ClipNode *node, *temp, *prevNode;
|
||||
|
||||
if (cliphead)
|
||||
{
|
||||
//check to see if range contains any old ranges
|
||||
node = cliphead;
|
||||
auto node = cliphead;
|
||||
while (node != nullptr && node->start < end)
|
||||
{
|
||||
// check to see if range contains any old ranges.
|
||||
// These can be removed, regardless whether they are a window or fully closed.
|
||||
if (node->start >= start && node->end <= end)
|
||||
{
|
||||
temp = node;
|
||||
auto temp = node;
|
||||
node = node->next;
|
||||
RemoveRange(temp);
|
||||
}
|
||||
else if (node->start<=start && node->end>=end)
|
||||
// check if the new range lies fully within an existing range.
|
||||
else if (node->start <= start && node->end >= end)
|
||||
{
|
||||
return;
|
||||
// existing range is closed, we're done.
|
||||
if (node->topclip <= node->bottomclip)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// this splits up a window node so we got to insert two new nodes
|
||||
else
|
||||
{
|
||||
int nodeend = node->end;
|
||||
node->end = start;
|
||||
|
||||
auto mynode = NewRange(start, end, 0, 0);
|
||||
InsertRange(node, mynode);
|
||||
|
||||
if (end != nodeend)
|
||||
{
|
||||
auto afternode = NewRange(end, nodeend, node->topclip, node->bottomclip);
|
||||
InsertRange(mynode, afternode);
|
||||
}
|
||||
|
||||
// We can only delete the old node after being done with its data.
|
||||
if (node->end == node->start) RemoveRange(node);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
//check to see if range overlaps a range (or possibly 2)
|
||||
|
||||
// at this point we know that overlaps can only be at one side because all full overlaps have been resolved already.
|
||||
// so what follows can at most intersect two other nodes - one at the left and one at the right
|
||||
node = cliphead;
|
||||
while (node != nullptr && node->start <= end)
|
||||
{
|
||||
if (node->end >= start)
|
||||
// node overlaps at the start.
|
||||
if (node->start < start && node->end >= start)
|
||||
{
|
||||
// we found the first overlapping node
|
||||
if (node->start > start)
|
||||
{
|
||||
// the new range overlaps with this node's start point
|
||||
node->start = start;
|
||||
}
|
||||
|
||||
if (node->end < end)
|
||||
if (node->topclip <= node->bottomclip)
|
||||
{
|
||||
node->end = end;
|
||||
auto next = node->next;
|
||||
if (next && next->start <= end)
|
||||
{
|
||||
// check if the following range overlaps. We know already that it will go past the end of the newly added range
|
||||
// (otherwise the first loop above would have taken care of it) so we can skip any checks for the full inclusion case.
|
||||
if (next->topclip <= next->bottomclip)
|
||||
{
|
||||
// next range is closed, so merge the two.
|
||||
node->end = next->end;
|
||||
RemoveRange(next);
|
||||
}
|
||||
else
|
||||
{
|
||||
next->start = end;
|
||||
}
|
||||
}
|
||||
// we're done and do not need to add a new node.
|
||||
return;
|
||||
}
|
||||
|
||||
ClipNode *node2 = node->next;
|
||||
while (node2 && node2->start <= node->end)
|
||||
else
|
||||
{
|
||||
if (node2->end > node->end) node->end = node2->end;
|
||||
ClipNode *delnode = node2;
|
||||
node2 = node2->next;
|
||||
RemoveRange(delnode);
|
||||
// if the old node is a window, restrict its size to the left of the new range and continue checking
|
||||
node->end = start;
|
||||
}
|
||||
return;
|
||||
}
|
||||
node = node->next;
|
||||
// range overlaps at the end.
|
||||
else if (node->start >= start && node->start <= end)
|
||||
{
|
||||
// node is closed - we can just merge this and exit
|
||||
if (node->topclip <= node->bottomclip)
|
||||
{
|
||||
node->start = start;
|
||||
return;
|
||||
}
|
||||
// node is a window - so restrict its size and insert the new node in front of it,
|
||||
else
|
||||
{
|
||||
node->start = end;
|
||||
|
||||
auto mynode = NewRange(start, end, 0, 0);
|
||||
InsertRange(node->prev, mynode);
|
||||
return;
|
||||
}
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
//just add range
|
||||
|
||||
//found no intersections - just add range
|
||||
node = cliphead;
|
||||
prevNode = nullptr;
|
||||
temp = NewRange(start, end);
|
||||
|
||||
ClipNode* prevNode = nullptr;
|
||||
// advance to the place where this can be inserted.
|
||||
while (node != nullptr && node->start < end)
|
||||
{
|
||||
prevNode = node;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
temp->next = node;
|
||||
if (node == nullptr)
|
||||
{
|
||||
temp->prev = prevNode;
|
||||
if (prevNode) prevNode->next = temp;
|
||||
if (!cliphead) cliphead = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (node == cliphead)
|
||||
{
|
||||
cliphead->prev = temp;
|
||||
cliphead = temp;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp->prev = prevNode;
|
||||
prevNode->next = temp;
|
||||
node->prev = temp;
|
||||
}
|
||||
}
|
||||
auto mynode = NewRange(start, end, 0, 0);
|
||||
InsertRange(prevNode, mynode);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp = NewRange(start, end);
|
||||
cliphead = temp;
|
||||
return;
|
||||
cliphead = NewRange(start, end, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
//
|
||||
// AddWindowRange
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void Clipper::AddWindowRange(int start, int end, float topclip, float bottomclip)
|
||||
{
|
||||
if (cliphead)
|
||||
{
|
||||
auto node = cliphead;
|
||||
while (node != nullptr && node->start < end)
|
||||
{
|
||||
// check to see if range contains any old ranges.
|
||||
|
||||
// existing range is part of new one.
|
||||
if (node->start >= start && node->end <= end)
|
||||
{
|
||||
// if old range is a window, make some adjustments.
|
||||
if (topclip <= node->topclip && bottomclip >= node->bottomclip)
|
||||
{
|
||||
// if the new window is more narrow both on top and bottom, we can remove the old range.
|
||||
auto temp = node;
|
||||
node = node->next;
|
||||
RemoveRange(temp);
|
||||
continue;
|
||||
}
|
||||
|
||||
// in all other cases we must adjust the node, and recursively process both sub-ranges.
|
||||
if (topclip < node->topclip) node->topclip = topclip;
|
||||
if (bottomclip > node->bottomclip) node->bottomclip = bottomclip;
|
||||
int nodestart = node->start, nodeend = node->end;
|
||||
// At this point it is just easier to recursively add the sub-ranges because we'd have to run the full program on both anyway,
|
||||
if (start < nodestart) AddWindowRange(start, nodestart, topclip, bottomclip);
|
||||
if (end > nodeend) AddWindowRange(nodeend, end, topclip, bottomclip);
|
||||
return;
|
||||
}
|
||||
// check if the new range lies fully within an existing range.
|
||||
else if (node->start <= start && node->end >= end)
|
||||
{
|
||||
// existing range is closed or a more narrow window on both sides, we're done.
|
||||
if (node->topclip <= node->bottomclip || (topclip >= node->topclip && bottomclip <= node->bottomclip))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// this splits up a window node so we got to insert two new nodes
|
||||
else
|
||||
{
|
||||
// adapt the window for the intersection.
|
||||
if (topclip > node->topclip) topclip = node->topclip;
|
||||
if (bottomclip < node->bottomclip) bottomclip = node->bottomclip;
|
||||
|
||||
int nodeend = node->end;
|
||||
node->end = start;
|
||||
|
||||
auto mynode = NewRange(start, end, topclip, bottomclip);
|
||||
InsertRange(node, mynode);
|
||||
|
||||
if (end != nodeend)
|
||||
{
|
||||
auto afternode = NewRange(end, nodeend, node->topclip, node->bottomclip);
|
||||
InsertRange(mynode, afternode);
|
||||
}
|
||||
|
||||
// We can only delete the old node after being done with its data.
|
||||
if (node->end == node->start) RemoveRange(node);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node = node->next;
|
||||
}
|
||||
}
|
||||
|
||||
// at this point we know that overlaps can only be at one side because all full overlaps have been resolved already.
|
||||
// so what follows can at most intersect two other nodes - one at the left and one at the right
|
||||
node = cliphead;
|
||||
while (node != nullptr && node->start <= end)
|
||||
{
|
||||
// node overlaps at the start.
|
||||
if (node->start < start && node->end >= start)
|
||||
{
|
||||
struct temprange
|
||||
{
|
||||
int start, end;
|
||||
float top, bottom;
|
||||
};
|
||||
temprange ranges[2];
|
||||
memset(ranges, -1, sizeof(ranges));
|
||||
// only split if this changes the old range, otherwise just truncate
|
||||
if (node->topclip > node->bottomclip && (topclip < node->topclip || bottomclip > node->bottomclip))
|
||||
{
|
||||
ranges[0].start = start;
|
||||
ranges[0].end = node->end;
|
||||
ranges[0].top = min(topclip, node->topclip);
|
||||
ranges[0].bottom = max(bottomclip, node->bottomclip);
|
||||
node->end = start;
|
||||
}
|
||||
start = node->end;
|
||||
|
||||
// check if the following range overlaps. We know already that it will go past the end of the newly added range
|
||||
// (otherwise the first loop above would have taken care of it) so we can skip any checks for the full inclusion case.
|
||||
auto next = node->next;
|
||||
if (next && next->start <= end)
|
||||
{
|
||||
// only split if this changes the old range, otherwise just truncate
|
||||
if (next->topclip > next->bottomclip && (topclip < next->topclip || bottomclip > next->bottomclip))
|
||||
{
|
||||
ranges[1].start = next->start;
|
||||
ranges[1].end = end;
|
||||
ranges[1].top = min(topclip, next->topclip);
|
||||
ranges[1].bottom = max(bottomclip, next->bottomclip);
|
||||
next->start = end;
|
||||
}
|
||||
end = next->start;
|
||||
}
|
||||
auto after = node;
|
||||
ClipNode* insert;
|
||||
if (ranges[0].end != -1)
|
||||
{
|
||||
insert = NewRange(ranges[0].start, ranges[0].end, ranges[0].top, ranges[0].bottom);
|
||||
InsertRange(after, insert);
|
||||
after = insert;
|
||||
}
|
||||
insert = NewRange(start, end, topclip, bottomclip);
|
||||
InsertRange(after, insert);
|
||||
after = insert;
|
||||
if (ranges[1].end != -1)
|
||||
{
|
||||
insert = NewRange(ranges[1].start, ranges[1].end, ranges[1].top, ranges[1].bottom);
|
||||
InsertRange(after, insert);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// range overlaps at the end.
|
||||
else if (node->start >= start && node->start <= end)
|
||||
{
|
||||
auto prev = node->prev;
|
||||
if (node->topclip > node->bottomclip && (topclip < node->topclip || bottomclip > node->bottomclip))
|
||||
{
|
||||
auto inode = NewRange(start, node->end, min(topclip, node->topclip), max(bottomclip, node->bottomclip));
|
||||
node->end = start;
|
||||
InsertRange(prev, inode);
|
||||
}
|
||||
start = node->end;
|
||||
auto mynode = NewRange(start, end, topclip, bottomclip);
|
||||
InsertRange(prev, mynode);
|
||||
return;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
//found no intersections - just add range
|
||||
node = cliphead;
|
||||
ClipNode* prevNode = nullptr;
|
||||
// advance to the place where this can be inserted.
|
||||
while (node != nullptr && node->start < end)
|
||||
{
|
||||
prevNode = node;
|
||||
node = node->next;
|
||||
}
|
||||
auto mynode = NewRange(start, end, topclip, bottomclip);
|
||||
InsertRange(prevNode, mynode);
|
||||
}
|
||||
else
|
||||
{
|
||||
cliphead = NewRange(start, end, topclip, bottomclip);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,12 +485,8 @@ void Clipper::RemoveClipRange(int start, int end)
|
|||
}
|
||||
else if (node->start < start && node->end > end)
|
||||
{
|
||||
temp = NewRange(end, node->end);
|
||||
node->end=start;
|
||||
temp->next=node->next;
|
||||
temp->prev=node;
|
||||
node->next=temp;
|
||||
if (temp->next) temp->next->prev=temp;
|
||||
temp = NewRange(end, node->end, node->topclip, node->bottomclip);
|
||||
InsertRange(node, temp);
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
|
@ -289,6 +503,6 @@ void Clipper::DumpClipper()
|
|||
{
|
||||
for (auto node = cliphead; node; node = node->next)
|
||||
{
|
||||
Printf("Range from %f to %f\n", bamang(node->start).asdeg(), bamang(node->end).asdeg());
|
||||
Printf("Range from %2.3f to %2.3f (top = %2.3f, bottom = %2.3f)\n", bamang(node->start).asdeg(), bamang(node->end).asdeg(), node->topclip, node->bottomclip);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,7 @@ class ClipNode
|
|||
|
||||
ClipNode *prev, *next;
|
||||
int start, end;
|
||||
|
||||
bool operator== (const ClipNode &other)
|
||||
{
|
||||
return other.start == start && other.end == end;
|
||||
}
|
||||
float topclip, bottomclip;
|
||||
};
|
||||
|
||||
|
||||
|
@ -27,14 +23,13 @@ class Clipper
|
|||
FMemArena nodearena;
|
||||
ClipNode * freelist = nullptr;
|
||||
|
||||
ClipNode * clipnodes = nullptr;
|
||||
ClipNode * cliphead = nullptr;
|
||||
void RemoveRange(ClipNode* cn);
|
||||
binangle visibleStart, visibleEnd;
|
||||
|
||||
public:
|
||||
bool IsRangeVisible(int startangle, int endangle);
|
||||
void AddClipRange(int startangle, int endangle);
|
||||
void AddWindowRange(int startangle, int endangle, float topclip, float bottomclip);
|
||||
void RemoveClipRange(int startangle, int endangle);
|
||||
|
||||
public:
|
||||
|
@ -59,16 +54,21 @@ private:
|
|||
else return (ClipNode*)nodearena.Alloc(sizeof(ClipNode));
|
||||
}
|
||||
|
||||
ClipNode * NewRange(int start, int end)
|
||||
ClipNode * NewRange(int start, int end, float top, float bottom)
|
||||
{
|
||||
ClipNode * c = GetNew();
|
||||
|
||||
c->start = start;
|
||||
c->end = end;
|
||||
c->topclip = top;
|
||||
c->bottomclip = bottom;
|
||||
c->next = c->prev = NULL;
|
||||
return c;
|
||||
}
|
||||
|
||||
void RemoveRange(ClipNode* cn);
|
||||
void InsertRange(ClipNode* prev, ClipNode* node);
|
||||
|
||||
public:
|
||||
|
||||
void SetVisibleRange(angle_t a1, angle_t a2)
|
||||
|
|
Loading…
Reference in a new issue