- clipper rework

* let the clipper work on relative angles to simplify the math.
* properly initialize the initial visible range and preserve it for multiple invocations.
* track the maximum visible angular range per sector. While possibly not sufficient to handle every edge case imaginable it has low overhead and is still useful to eliminate obvious cases that do not need more complex checks. It is enough to fix the blue door in Duke E3L4.
* removed unused elements of the clipper.
This commit is contained in:
Christoph Oelckers 2021-04-24 12:08:38 +02:00
parent c05df44ad4
commit be08a2f800
6 changed files with 108 additions and 228 deletions

View file

@ -70,6 +70,11 @@ void BunchDrawer::Init(HWDrawInfo *_di, Clipper* c, vec2_t& view, binangle a1, b
// Reverse the orientation so that startangle and endangle are properly ordered.
wall[i].clipangle = clipper->PointToAngle(wall[i].pos);
}
for (int i = 0; i < numsectors; i++)
{
sectstartang[i] = -1;
sectendang[i] = -1;
}
}
//==========================================================================
@ -193,16 +198,37 @@ int BunchDrawer::ClipLine(int line, bool portal)
{
auto wal = &wall[line];
auto startAngle = wal->clipangle;
auto endAngle = wall[wal->point2].clipangle;
auto startAngleBam = wal->clipangle;
auto endAngleBam = wall[wal->point2].clipangle;
// Back side, i.e. backface culling - read: endAngle >= startAngle!
if (startAngle.asbam() - endAngle.asbam() < ANGLE_180)
// Back side, i.e. backface culling - read: endAngle <= startAngle!
if (startAngleBam.asbam() - endAngleBam.asbam() < ANGLE_180)
{
return CL_Skip;
}
// convert to clipper coordinates and clamp to valid range.
int startAngle = startAngleBam.asbam() - ang1.asbam();
int endAngle = endAngleBam.asbam() - ang1.asbam();
if (startAngle < 0) startAngle = 0;
if (endAngle < 0) endAngle = INT_MAX;
if (!portal && !clipper->SafeCheckRange(startAngle, endAngle))
// since these values are derived from previous calls of this function they cannot be out of range.
int sect = wal->sector;
int sectStartAngle = sectstartang[sect];
auto sectEndAngle = sectendang[sect];
// check against the maximum possible viewing range of the sector.
// Todo: check if this is sufficient or if we really have to do a more costly check against the single visible segments.
if (sectStartAngle != -1)
{
if (sectStartAngle > endAngle || sectEndAngle < startAngle)
return CL_Skip; // completely outside the valid range for this sector.
if (sectStartAngle > startAngle) startAngle = sectStartAngle;
if (sectEndAngle < endAngle) endAngle = sectEndAngle;
if (endAngle <= startAngle) return CL_Skip; // can this even happen?
}
if (!portal && !clipper->IsRangeVisible(startAngle, endAngle))
{
return CL_Skip;
}
@ -210,12 +236,26 @@ int BunchDrawer::ClipLine(int line, bool portal)
if (wal->nextwall == -1 || (wal->cstat & CSTAT_WALL_1WAY) || CheckClip(wal))
{
// one-sided
if (!portal) clipper->SafeAddClipRange(startAngle, endAngle);
if (!portal) clipper->AddClipRange(startAngle, endAngle);
return CL_Draw;
}
else
{
if (portal) clipper->SafeRemoveClipRange(startAngle, endAngle);
if (portal) clipper->RemoveClipRange(startAngle, endAngle);
// set potentially visible viewing range for this line's back sector.
int nsect = wal->nextsector;
if (sectstartang[nsect] == -1)
{
sectstartang[nsect] = startAngle;
sectendang[nsect] = endAngle;
}
else
{
if (startAngle < sectstartang[nsect]) sectstartang[nsect] = startAngle;
if (endAngle > sectendang[nsect]) sectendang[nsect] = endAngle;
}
return CL_Draw | CL_Pass;
}
}
@ -529,6 +569,14 @@ void BunchDrawer::RenderScene(const int* viewsectors, unsigned sectcount, bool p
//Printf("----------------------------------------- \nstart at sector %d\n", viewsectors[0]);
auto process = [&]()
{
clipper->Clear(ang1);
for (unsigned i = 0; i < sectcount; i++)
{
sectstartang[viewsectors[i]] = 0;
sectendang[viewsectors[i]] = int (ang2.asbam() - ang1.asbam());
}
for (unsigned i = 0; i < sectcount; i++)
ProcessSector(viewsectors[i], portal);
while (Bunches.Size() > 0)
@ -550,12 +598,11 @@ void BunchDrawer::RenderScene(const int* viewsectors, unsigned sectcount, bool p
// The BunchInFront check can fail with angles that may wrap around.
auto rotang = di->Viewpoint.RotAngle;
ang1 = bamang(rotang - ANGLE_90);
ang2 = bamang(rotang + ANGLE_90);
ang2 = bamang(rotang + ANGLE_90 - 1);
process();
clipper->Clear();
gotsector2.Zero();
ang1 = bamang(rotang + ANGLE_90);
ang2 = bamang(rotang - ANGLE_90);
ang2 = bamang(rotang - ANGLE_90 - 1);
process();
}
Bsp.Unclock();

View file

@ -32,6 +32,8 @@ class BunchDrawer
FixedBitArray<MAXWALLS> gotwall;
binangle ang1, ang2;
int sectstartang[MAXSECTORS], sectendang[MAXSECTORS];
private:
enum

View file

@ -40,12 +40,6 @@
#include "build.h"
#include "printf.h"
unsigned Clipper::starttime;
Clipper::Clipper()
{
starttime++;
}
//-----------------------------------------------------------------------------
//
@ -74,20 +68,11 @@ void Clipper::RemoveRange(ClipNode * range)
//
//-----------------------------------------------------------------------------
void Clipper::Clear()
void Clipper::Clear(binangle rangestart)
{
ClipNode *node = cliphead;
ClipNode *temp;
blocked = false;
while (node != nullptr)
{
temp = node;
node = node->next;
Free(temp);
}
node = silhouette;
while (node != nullptr)
{
temp = node;
@ -96,30 +81,17 @@ void Clipper::Clear()
}
cliphead = nullptr;
silhouette = nullptr;
starttime++;
}
//-----------------------------------------------------------------------------
//
// SetSilhouette
//
//-----------------------------------------------------------------------------
void Clipper::SetSilhouette()
{
ClipNode *node = cliphead;
ClipNode *last = nullptr;
while (node != nullptr)
if (visibleStart.asbam() != 0 || visibleEnd.asbam() != 0)
{
ClipNode *snode = NewRange(node->start, node->end);
if (silhouette == nullptr) silhouette = snode;
snode->prev = last;
if (last != nullptr) last->next = snode;
last = snode;
node = node->next;
int vstart = int(visibleStart.asbam() - rangestart.asbam());
if (vstart > 1) AddClipRange(0, vstart - 1);
int vend = int(visibleEnd.asbam() - rangestart.asbam());
if (vend > 0 && vend < INT_MAX - 1) AddClipRange(vend + 1, INT_MAX);
}
}
//-----------------------------------------------------------------------------
@ -128,16 +100,16 @@ void Clipper::SetSilhouette()
//
//-----------------------------------------------------------------------------
bool Clipper::IsRangeVisible(binangle startAngle, binangle endAngle)
bool Clipper::IsRangeVisible(int startAngle, int endAngle)
{
ClipNode *ci;
ci = cliphead;
if (endAngle.asbam()==0 && ci && ci->start==0) return false;
if (endAngle == 0 && ci && ci->start==0) return false;
while (ci != nullptr && ci->start < endAngle.asbam())
while (ci != nullptr && ci->start < endAngle)
{
if (startAngle.asbam() >= ci->start && endAngle.asbam() <= ci->end)
if (startAngle >= ci->start && endAngle <= ci->end)
{
return false;
}
@ -153,9 +125,8 @@ bool Clipper::IsRangeVisible(binangle startAngle, binangle endAngle)
//
//-----------------------------------------------------------------------------
void Clipper::AddClipRange(binangle start_, binangle end_)
void Clipper::AddClipRange(int start, int end)
{
auto start = start_.asbam(), end = end_.asbam();
ClipNode *node, *temp, *prevNode;
if (cliphead)
@ -255,46 +226,11 @@ void Clipper::AddClipRange(binangle start_, binangle end_)
//-----------------------------------------------------------------------------
//
// RemoveClipRange
// RemoveClipRange
//
//-----------------------------------------------------------------------------
void Clipper::RemoveClipRange(binangle start_, binangle end_)
{
auto start = start_.asbam(), end = end_.asbam();
ClipNode *node;
if (silhouette)
{
node = silhouette;
while (node != nullptr && node->end <= start)
{
node = node->next;
}
if (node != nullptr && node->start <= start)
{
if (node->end >= end) return;
start = node->end;
node = node->next;
}
while (node != nullptr && node->start < end)
{
DoRemoveClipRange(start, node->start);
start = node->end;
node = node->next;
}
if (start >= end) return;
}
DoRemoveClipRange(start, end);
}
//-----------------------------------------------------------------------------
//
// RemoveClipRange worker function
//
//-----------------------------------------------------------------------------
void Clipper::DoRemoveClipRange(angle_t start, angle_t end)
void Clipper::RemoveClipRange(int start, int end)
{
ClipNode *node, *temp;
@ -347,15 +283,6 @@ void Clipper::DoRemoveClipRange(angle_t start, angle_t end)
//-----------------------------------------------------------------------------
//
// ! Returns the pseudoangle between the line p1 to (infinity, p1.y) and the
// line from p1 to p2. The pseudoangle has the property that the ordering of
// points by true angle around p1 and ordering of points by pseudoangle are the
// same.
//
// For clipping exact angles are not needed. Only the ordering matters.
// This is about as fast as the fixed point R_PointToAngle2 but without
// the precision issues associated with that function.
//
//-----------------------------------------------------------------------------
void Clipper::DumpClipper()

View file

@ -13,7 +13,7 @@ class ClipNode
friend class Clipper;
ClipNode *prev, *next;
angle_t start, end;
int start, end;
bool operator== (const ClipNode &other)
{
@ -24,27 +24,23 @@ class ClipNode
class Clipper
{
static unsigned starttime;
FMemArena nodearena;
ClipNode * freelist = nullptr;
ClipNode * clipnodes = nullptr;
ClipNode * cliphead = nullptr;
ClipNode * silhouette = nullptr; // will be preserved even when RemoveClipRange is called
vec2_t viewpoint;
bool blocked = false;
void RemoveRange(ClipNode* cn);
binangle visibleStart, visibleEnd;
bool IsRangeVisible(binangle startangle, binangle endangle);
void RemoveRange(ClipNode * cn);
void AddClipRange(binangle startangle, binangle endangle);
void RemoveClipRange(binangle startangle, binangle endangle);
public:
bool IsRangeVisible(int startangle, int endangle);
void AddClipRange(int startangle, int endangle);
void RemoveClipRange(int startangle, int endangle);
public:
Clipper();
void Clear();
static binangle AngleToPseudo(binangle ang);
void Clear(binangle rangestart);
void Free(ClipNode *node)
{
@ -64,7 +60,7 @@ private:
else return (ClipNode*)nodearena.Alloc(sizeof(ClipNode));
}
ClipNode * NewRange(angle_t start, angle_t end)
ClipNode * NewRange(int start, int end)
{
ClipNode * c = GetNew();
@ -74,8 +70,6 @@ private:
return c;
}
void DoRemoveClipRange(angle_t start, angle_t end);
public:
void SetViewpoint(const vec2_t &vp)
@ -83,88 +77,36 @@ public:
viewpoint = vp;
}
void SetSilhouette();
bool SafeCheckRange(binangle startAngle, binangle endAngle)
void SetVisibleRange(angle_t a1, angle_t a2)
{
if(startAngle.asbam() > endAngle.asbam())
if (a2 != 0xffffffff)
{
return (IsRangeVisible(startAngle, bamang(ANGLE_MAX)) || IsRangeVisible(bamang(0), endAngle));
visibleStart = bamang(a1 - a2);
visibleEnd = bamang(a1 + a2);
}
return IsRangeVisible(startAngle, endAngle);
else visibleStart = visibleEnd = bamang(0);
}
void SafeAddClipRange(binangle startangle, binangle endangle)
void RestrictVisibleRange(binangle a1, binangle a2)
{
if(startangle.asbam() > endangle.asbam())
if (visibleStart == visibleEnd)
{
// The range has to added in two parts.
AddClipRange(startangle, bamang(ANGLE_MAX));
AddClipRange(bamang(0), endangle);
visibleStart = a1;
visibleEnd = a2;
}
else
{
// Add the range as usual.
AddClipRange(startangle, endangle);
if (a1.asbam() - visibleStart.asbam() < visibleEnd.asbam() - visibleStart.asbam()) visibleStart = a1;
if (a2.asbam() - visibleStart.asbam() < visibleEnd.asbam() - visibleStart.asbam()) visibleStart = a2;
}
}
void SafeAddClipRange(const vec2_t& v1, const vec2_t& v2)
{
binangle a2 = PointToAngle(v1);
binangle a1 = PointToAngle(v2);
SafeAddClipRange(a1,a2);
}
void SafeRemoveClipRange(binangle startangle, binangle endangle)
{
if(startangle.asbam() > endangle.asbam())
{
// The range has to added in two parts.
RemoveClipRange(startangle, bamang(ANGLE_MAX));
RemoveClipRange(bamang(0), endangle);
}
else
{
// Add the range as usual.
RemoveClipRange(startangle, endangle);
}
}
void SetBlocked(bool on)
{
blocked = on;
}
bool IsBlocked() const
{
return blocked;
}
void DumpClipper();
binangle PointToAngle(const vec2_t& pos)
{
vec2_t vec = pos - viewpoint;
#if 0
if (vec.x == 0 && vec.y == 0)
{
return bamang(0);
}
else
{
double result = vec.y / double(abs(vec.x) + fabs(vec.y));
if (vec.x < 0)
{
result = 2. - result;
}
return bamang(xs_Fix<30>::ToFix(result));
}
#else
return bvectangbam(vec.x, vec.y);
#endif
}

View file

@ -121,7 +121,6 @@ static HWDrawInfo * gl_drawinfo; // This is a linked list of all active DrawInfo
void HWDrawInfo::StartScene(FRenderViewpoint& parentvp, HWViewpointUniforms* uniforms)
{
staticClipper.Clear();
mClipper = &staticClipper;
Viewpoint = parentvp;
@ -369,7 +368,6 @@ void HWDrawInfo::CreateScene(bool portal)
const auto& vp = Viewpoint;
angle_t a1 = FrustumAngle();
if (a1 != 0xffffffff) mClipper->SafeAddClipRange(bamang(vp.RotAngle + a1), bamang(vp.RotAngle - a1));
// reset the portal manager
portalState.StartFrame();
@ -385,6 +383,9 @@ void HWDrawInfo::CreateScene(bool portal)
geoofs = { 0,0 };
vec2_t view = { int(vp.Pos.X * 16), int(vp.Pos.Y * -16) };
if(!portal) mClipper->SetVisibleRange(vp.RotAngle, a1);
if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1));
else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0));
if (vp.SectNums)
@ -420,10 +421,9 @@ void HWDrawInfo::CreateScene(bool portal)
if (eff.geosector[i] == effsect) drawsect = eff.geosectorwarp[i];
}
mClipper->Clear();
mClipper->SafeAddClipRange(bamang(vp.RotAngle + a1), bamang(vp.RotAngle - a1));
if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1));
else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0));
mDrawer.RenderScene(&drawsect, 1, false);
for (int i = 0; i < eff.geocnt; i++)
@ -452,8 +452,6 @@ void HWDrawInfo::CreateScene(bool portal)
if (eff.geosector[i] == effsect) drawsect = eff.geosectorwarp2[i];
}
mClipper->Clear();
mClipper->SafeAddClipRange(bamang(vp.RotAngle + a1), bamang(vp.RotAngle - a1));
if (a1 != 0xffffffff) mDrawer.Init(this, mClipper, view, bamang(vp.RotAngle - a1), bamang(vp.RotAngle + a1));
else mDrawer.Init(this, mClipper, view, bamang(0), bamang(0));
mDrawer.RenderScene(&drawsect, 1, false);

View file

@ -417,36 +417,7 @@ void HWScenePortalBase::DrawContents(HWDrawInfo* di, FRenderState& state)
void HWScenePortalBase::ClearClipper(HWDrawInfo *di, Clipper *clipper)
{
auto outer_di = di->outer;
#if 0 // todo: fixme or remove - Unlike for Doom this won't be of great benefit with Build's rendering approach.
// This requires maximum precision, so convert everything to double.
DAngle angleOffset = deltaangle(DAngle(outer_di->Viewpoint.HWAngles.Yaw.Degrees), DAngle(di->Viewpoint.HWAngles.Yaw.Degrees));
clipper->Clear();
auto& vp = di->Viewpoint;
vec2_t view = { int(vp.Pos.X * 16), int(vp.Pos.Y * -16) };
// Set the clipper to the minimal visible area
clipper->SafeAddClipRange(bamang(0), bamang(0xffffffff));
for (unsigned int i = 0; i < lines.Size(); i++)
{
binangle startang = bvectangbam(lines[i].seg->x - view.x, lines[i].seg->y - view.y);
binangle endang = bvectangbam(wall[lines[i].seg->point2].x - view.x, wall[lines[i].seg->point2].y - view.y);
if (endang.asbam() - startang.asbam() >= ANGLE_180)
{
clipper->SafeRemoveClipRange(startang, endang);
}
}
#endif
// and finally clip it to the visible area
angle_t a1 = di->FrustumAngle();
if (a1 < ANGLE_180) clipper->SafeAddClipRange(bamang(di->Viewpoint.RotAngle + a1), bamang(di->Viewpoint.RotAngle - a1));
// lock the parts that have just been clipped out.
//clipper->SetSilhouette();
clipper->SetVisibleRange(di->Viewpoint.RotAngle, di->FrustumAngle());
}
@ -596,14 +567,11 @@ bool HWMirrorPortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *clippe
di->SetClipLine(line);
di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1));
clipper->Clear();
angle_t af = di->FrustumAngle();
if (af < ANGLE_180) clipper->SafeAddClipRange(bamang(vp.RotAngle + af), bamang(vp.RotAngle - af));
ClearClipper(di, clipper);
auto startan = bvectangbam(line->x - newx, line->y - newy);
auto endan = bvectangbam(wall[line->point2].x - newx, wall[line->point2].y - newy);
clipper->SafeAddClipRange(startan, endan); // we check the line from the backside so angles are reversed.
clipper->RestrictVisibleRange(endan, startan); // we check the line from the backside so angles are reversed.
return true;
}
@ -668,14 +636,12 @@ bool HWLineToLinePortal::Setup(HWDrawInfo *di, FRenderState &rstate, Clipper *cl
di->SetClipLine(line);
di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1));
clipper->Clear();
angle_t af = di->FrustumAngle();
if (af < ANGLE_180) clipper->SafeAddClipRange(bamang(vp.RotAngle + af), bamang(vp.RotAngle - af));
ClearClipper(di, clipper);
auto startan = bvectangbam(origin->x - origx, origin->y - origy);
auto endan = bvectangbam(wall[origin->point2].x - origx, wall[origin->point2].y - origy);
clipper->SafeAddClipRange(endan, startan);
clipper->RestrictVisibleRange(startan, endan);
return true;
}
@ -722,14 +688,12 @@ bool HWLineToSpritePortal::Setup(HWDrawInfo* di, FRenderState& rstate, Clipper*
di->SetClipLine(line);
di->SetupView(rstate, vp.Pos.X, vp.Pos.Y, vp.Pos.Z, !!(state->MirrorFlag & 1), !!(state->PlaneMirrorFlag & 1));
clipper->Clear();
angle_t af = di->FrustumAngle();
if (af < ANGLE_180) clipper->SafeAddClipRange(bamang(vp.RotAngle + af), bamang(vp.RotAngle - af));
ClearClipper(di, clipper);
auto startan = bvectangbam(origin->x - origx, origin->y - origy);
auto endan = bvectangbam(wall[origin->point2].x - origx, wall[origin->point2].y - origy);
clipper->SafeAddClipRange(endan, startan);
clipper->RestrictVisibleRange(startan, endan);
return true;
}