From c6f83fa8bda5a5b9fad0bf638022c82de92571f7 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 15 Mar 2021 19:05:08 +0100 Subject: [PATCH] - added GZDoom's clipper. --- source/CMakeLists.txt | 6 + source/core/rendering/scene/hw_clipper.cpp | 397 +++++++++++++++++++++ source/core/rendering/scene/hw_clipper.h | 159 +++++++++ 3 files changed, 562 insertions(+) create mode 100644 source/core/rendering/scene/hw_clipper.cpp create mode 100644 source/core/rendering/scene/hw_clipper.h diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 4f73581ce..e72c62a2f 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -638,6 +638,8 @@ file( GLOB HEADER_FILES core/music/*.h core/menu/*.h core/input/*.h + core/rendering/*.h + core/rendering/scene/*.h common/audio/sound/thirdparty/*.h common/audio/sound/*.h @@ -1069,6 +1071,8 @@ set (PCH_SOURCES core/statusbar2.cpp core/gi.cpp + core/rendering/scene/hw_clipper.cpp + core/console/c_notifybuffer.cpp core/console/d_event.cpp @@ -1361,6 +1365,8 @@ include_directories( core/dobject core/menu core/input + core/rendering + core/rendering/scene platform common/audio/sound common/audio/music diff --git a/source/core/rendering/scene/hw_clipper.cpp b/source/core/rendering/scene/hw_clipper.cpp new file mode 100644 index 000000000..1526710a7 --- /dev/null +++ b/source/core/rendering/scene/hw_clipper.cpp @@ -0,0 +1,397 @@ +/* +* +** gl_clipper.cpp +** +** Handles visibility checks. +** Loosely based on the JDoom clipper. +** +**--------------------------------------------------------------------------- +** Copyright 2003 Tim Stump +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "hw_clipper.h" +#include "basics.h" + +unsigned Clipper::starttime; + +Clipper::Clipper() +{ + starttime++; +} + +//----------------------------------------------------------------------------- +// +// RemoveRange +// +//----------------------------------------------------------------------------- + +void Clipper::RemoveRange(ClipNode * range) +{ + if (range == cliphead) + { + cliphead = cliphead->next; + } + else + { + if (range->prev) range->prev->next = range->next; + if (range->next) range->next->prev = range->prev; + } + + Free(range); +} + +//----------------------------------------------------------------------------- +// +// Clear +// +//----------------------------------------------------------------------------- + +void Clipper::Clear() +{ + ClipNode *node = cliphead; + ClipNode *temp; + + blocked = false; + while (node != NULL) + { + temp = node; + node = node->next; + Free(temp); + } + node = silhouette; + + while (node != NULL) + { + temp = node; + node = node->next; + Free(temp); + } + + cliphead = NULL; + silhouette = NULL; + starttime++; +} + +//----------------------------------------------------------------------------- +// +// SetSilhouette +// +//----------------------------------------------------------------------------- + +void Clipper::SetSilhouette() +{ + ClipNode *node = cliphead; + ClipNode *last = NULL; + + while (node != NULL) + { + ClipNode *snode = NewRange(node->start, node->end); + if (silhouette == NULL) silhouette = snode; + snode->prev = last; + if (last != NULL) last->next = snode; + last = snode; + node = node->next; + } +} + +//----------------------------------------------------------------------------- +// +// IsRangeVisible +// +//----------------------------------------------------------------------------- + +bool Clipper::IsRangeVisible(angle_t startAngle, angle_t endAngle) +{ + ClipNode *ci; + ci = cliphead; + + if (endAngle==0 && ci && ci->start==0) return false; + + while (ci != NULL && ci->start < endAngle) + { + if (startAngle >= ci->start && endAngle <= ci->end) + { + return false; + } + ci = ci->next; + } + + return true; +} + +//----------------------------------------------------------------------------- +// +// AddClipRange +// +//----------------------------------------------------------------------------- + +void Clipper::AddClipRange(angle_t start, angle_t end) +{ + ClipNode *node, *temp, *prevNode; + + if (cliphead) + { + //check to see if range contains any old ranges + node = cliphead; + while (node != NULL && node->start < end) + { + if (node->start >= start && node->end <= end) + { + temp = node; + node = node->next; + RemoveRange(temp); + } + else if (node->start<=start && node->end>=end) + { + return; + } + else + { + node = node->next; + } + } + + //check to see if range overlaps a range (or possibly 2) + node = cliphead; + while (node != NULL && node->start <= end) + { + if (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) + { + node->end = end; + } + + ClipNode *node2 = node->next; + while (node2 && node2->start <= node->end) + { + if (node2->end > node->end) node->end = node2->end; + ClipNode *delnode = node2; + node2 = node2->next; + RemoveRange(delnode); + } + return; + } + node = node->next; + } + + //just add range + node = cliphead; + prevNode = NULL; + temp = NewRange(start, end); + + while (node != NULL && node->start < end) + { + prevNode = node; + node = node->next; + } + + temp->next = node; + if (node == NULL) + { + 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; + } + } + } + else + { + temp = NewRange(start, end); + cliphead = temp; + return; + } +} + + +//----------------------------------------------------------------------------- +// +// RemoveClipRange +// +//----------------------------------------------------------------------------- + +void Clipper::RemoveClipRange(angle_t start, angle_t end) +{ + ClipNode *node; + + if (silhouette) + { + node = silhouette; + while (node != NULL && node->end <= start) + { + node = node->next; + } + if (node != NULL && node->start <= start) + { + if (node->end >= end) return; + start = node->end; + node = node->next; + } + while (node != NULL && 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) +{ + ClipNode *node, *temp; + + if (cliphead) + { + //check to see if range contains any old ranges + node = cliphead; + while (node != NULL && node->start < end) + { + if (node->start >= start && node->end <= end) + { + temp = node; + node = node->next; + RemoveRange(temp); + } + else + { + node = node->next; + } + } + + //check to see if range overlaps a range (or possibly 2) + node = cliphead; + while (node != NULL) + { + if (node->start >= start && node->start <= end) + { + node->start = end; + break; + } + else if (node->end >= start && node->end <= end) + { + node->end=start; + } + 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; + break; + } + node = node->next; + } + } +} + + +//----------------------------------------------------------------------------- +// +// +// +//----------------------------------------------------------------------------- + +angle_t Clipper::AngleToPseudo(angle_t ang) +{ + double vecx = cos(ang * M_PI / ANGLE_180); + double vecy = sin(ang * M_PI / ANGLE_180); + + double result = vecy / (fabs(vecx) + fabs(vecy)); + if (vecx < 0) + { + result = 2.f - result; + } + return xs_Fix<30>::ToFix(result); +} + +//----------------------------------------------------------------------------- +// +// ! 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. +// +//----------------------------------------------------------------------------- + +angle_t Clipper::PointToPseudoAngle(double x, double y) +{ + double vecx = x - viewpoint.X; + double vecy = y - viewpoint.Y; + + if (vecx == 0 && vecy == 0) + { + return 0; + } + else + { + double result = vecy / (fabs(vecx) + fabs(vecy)); + if (vecx < 0) + { + result = 2. - result; + } + return xs_Fix<30>::ToFix(result); + } +} + + + diff --git a/source/core/rendering/scene/hw_clipper.h b/source/core/rendering/scene/hw_clipper.h new file mode 100644 index 000000000..172bf5aac --- /dev/null +++ b/source/core/rendering/scene/hw_clipper.h @@ -0,0 +1,159 @@ +#ifndef __GL_CLIPPER +#define __GL_CLIPPER + +#include "xs_Float.h" +#include "memarena.h" +#include "basics.h" +#include "vectors.h" + +class ClipNode +{ + friend class Clipper; + + ClipNode *prev, *next; + angle_t start, end; + + bool operator== (const ClipNode &other) + { + return other.start == start && other.end == end; + } +}; + + +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 + DVector2 viewpoint; + bool blocked = false; + + static angle_t AngleToPseudo(angle_t ang); + bool IsRangeVisible(angle_t startangle, angle_t endangle); + void RemoveRange(ClipNode * cn); + void AddClipRange(angle_t startangle, angle_t endangle); + void RemoveClipRange(angle_t startangle, angle_t endangle); + void DoRemoveClipRange(angle_t start, angle_t end); + +public: + + Clipper(); + + void Clear(); + + void Free(ClipNode *node) + { + node->next = freelist; + freelist = node; + } + + ClipNode * GetNew() + { + if (freelist) + { + ClipNode * p = freelist; + freelist = p->next; + return p; + } + else return (ClipNode*)nodearena.Alloc(sizeof(ClipNode)); + } + + ClipNode * NewRange(angle_t start, angle_t end) + { + ClipNode * c = GetNew(); + + c->start = start; + c->end = end; + c->next = c->prev = NULL; + return c; + } + + void SetViewpoint(const DVector2 &vp) + { + viewpoint = vp; + } + + void SetSilhouette(); + + bool SafeCheckRange(angle_t startAngle, angle_t endAngle) + { + if(startAngle > endAngle) + { + return (IsRangeVisible(startAngle, ANGLE_MAX) || IsRangeVisible(0, endAngle)); + } + + return IsRangeVisible(startAngle, endAngle); + } + + void SafeAddClipRange(angle_t startangle, angle_t endangle) + { + if(startangle > endangle) + { + // The range has to added in two parts. + AddClipRange(startangle, ANGLE_MAX); + AddClipRange(0, endangle); + } + else + { + // Add the range as usual. + AddClipRange(startangle, endangle); + } + } + + void SafeAddClipRange(const DVector2& v1, const DVector2& v2) + { + angle_t a2 = PointToPseudoAngle(v1.X, v1.Y); + angle_t a1 = PointToPseudoAngle(v2.X, v2.Y); + SafeAddClipRange(a1,a2); + } + + void SafeAddClipRangeRealAngles(angle_t startangle, angle_t endangle) + { + SafeAddClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle)); + } + + + void SafeRemoveClipRange(angle_t startangle, angle_t endangle) + { + if(startangle > endangle) + { + // The range has to added in two parts. + RemoveClipRange(startangle, ANGLE_MAX); + RemoveClipRange(0, endangle); + } + else + { + // Add the range as usual. + RemoveClipRange(startangle, endangle); + } + } + + void SafeRemoveClipRangeRealAngles(angle_t startangle, angle_t endangle) + { + SafeRemoveClipRange(AngleToPseudo(startangle), AngleToPseudo(endangle)); + } + + void SetBlocked(bool on) + { + blocked = on; + } + + bool IsBlocked() const + { + return blocked; + } + + angle_t PointToPseudoAngle(double x, double y); + + inline angle_t GetClipAngle(const DVector2& v) + { + return PointToPseudoAngle(v.X, v.Y); + } + +}; + +#endif