From be08a2f800e81151d9d5d0439c5105219898c3a9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Apr 2021 12:08:38 +0200 Subject: [PATCH] - 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. --- .../core/rendering/scene/hw_bunchdrawer.cpp | 67 +++++++++-- source/core/rendering/scene/hw_bunchdrawer.h | 2 + source/core/rendering/scene/hw_clipper.cpp | 105 +++--------------- source/core/rendering/scene/hw_clipper.h | 98 ++++------------ source/core/rendering/scene/hw_drawinfo.cpp | 10 +- source/core/rendering/scene/hw_portal.cpp | 54 ++------- 6 files changed, 108 insertions(+), 228 deletions(-) diff --git a/source/core/rendering/scene/hw_bunchdrawer.cpp b/source/core/rendering/scene/hw_bunchdrawer.cpp index ca7bba269..5c3b5a434 100644 --- a/source/core/rendering/scene/hw_bunchdrawer.cpp +++ b/source/core/rendering/scene/hw_bunchdrawer.cpp @@ -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(); diff --git a/source/core/rendering/scene/hw_bunchdrawer.h b/source/core/rendering/scene/hw_bunchdrawer.h index d8906c6c8..805b60a93 100644 --- a/source/core/rendering/scene/hw_bunchdrawer.h +++ b/source/core/rendering/scene/hw_bunchdrawer.h @@ -32,6 +32,8 @@ class BunchDrawer FixedBitArray gotwall; binangle ang1, ang2; + int sectstartang[MAXSECTORS], sectendang[MAXSECTORS]; + private: enum diff --git a/source/core/rendering/scene/hw_clipper.cpp b/source/core/rendering/scene/hw_clipper.cpp index 124bea91c..0a7eafd86 100644 --- a/source/core/rendering/scene/hw_clipper.cpp +++ b/source/core/rendering/scene/hw_clipper.cpp @@ -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() diff --git a/source/core/rendering/scene/hw_clipper.h b/source/core/rendering/scene/hw_clipper.h index 5c1280d5b..34739ee79 100644 --- a/source/core/rendering/scene/hw_clipper.h +++ b/source/core/rendering/scene/hw_clipper.h @@ -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 } diff --git a/source/core/rendering/scene/hw_drawinfo.cpp b/source/core/rendering/scene/hw_drawinfo.cpp index 196734a83..b459cbfe5 100644 --- a/source/core/rendering/scene/hw_drawinfo.cpp +++ b/source/core/rendering/scene/hw_drawinfo.cpp @@ -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); diff --git a/source/core/rendering/scene/hw_portal.cpp b/source/core/rendering/scene/hw_portal.cpp index cf35cc320..c4ea20cf0 100644 --- a/source/core/rendering/scene/hw_portal.cpp +++ b/source/core/rendering/scene/hw_portal.cpp @@ -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; }