mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-20 11:11:34 +00:00
459 lines
10 KiB
C++
459 lines
10 KiB
C++
|
/*
|
||
|
*
|
||
|
** 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 "gl/scene/gl_clipper.h"
|
||
|
|
||
|
|
||
|
|
||
|
ClipNode * ClipNode::freelist;
|
||
|
int Clipper::anglecache;
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Destructor
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
Clipper::~Clipper()
|
||
|
{
|
||
|
Clear();
|
||
|
while (ClipNode::freelist != NULL)
|
||
|
{
|
||
|
ClipNode * node = ClipNode::freelist;
|
||
|
ClipNode::freelist = node->next;
|
||
|
delete node;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// 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;
|
||
|
}
|
||
|
|
||
|
range->Free();
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Clear
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void Clipper::Clear()
|
||
|
{
|
||
|
ClipNode *node = cliphead;
|
||
|
ClipNode *temp;
|
||
|
|
||
|
while (node != NULL)
|
||
|
{
|
||
|
temp = node;
|
||
|
node = node->next;
|
||
|
temp->Free();
|
||
|
}
|
||
|
node = silhouette;
|
||
|
|
||
|
while (node != NULL)
|
||
|
{
|
||
|
temp = node;
|
||
|
node = node->next;
|
||
|
temp->Free();
|
||
|
}
|
||
|
|
||
|
cliphead = NULL;
|
||
|
silhouette = NULL;
|
||
|
anglecache++;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// SetSilhouette
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
void Clipper::SetSilhouette()
|
||
|
{
|
||
|
ClipNode *node = cliphead;
|
||
|
ClipNode *last = NULL;
|
||
|
|
||
|
while (node != NULL)
|
||
|
{
|
||
|
ClipNode *snode = ClipNode::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 = ClipNode::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 = ClipNode::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=ClipNode::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 anround 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 R_PointToPseudoAngle (fixed_t viewx, fixed_t viewy, fixed_t x, fixed_t y)
|
||
|
{
|
||
|
// Note: float won't work here as it's less precise than the BAM values being passed as parameters
|
||
|
double vecx = double(x-viewx);
|
||
|
double vecy = double(y-viewy);
|
||
|
|
||
|
if (vecx == 0 && vecy == 0)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
double result = vecy / (fabs(vecx) + fabs(vecy));
|
||
|
if (vecx < 0)
|
||
|
{
|
||
|
result = 2.f - result;
|
||
|
}
|
||
|
return xs_Fix<30>::ToFix(result);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// R_CheckBBox
|
||
|
// Checks BSP node/subtree bounding box.
|
||
|
// Returns true
|
||
|
// if some part of the bbox might be visible.
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
static const int checkcoord[12][4] = // killough -- static const
|
||
|
{
|
||
|
{3,0,2,1},
|
||
|
{3,0,2,0},
|
||
|
{3,1,2,0},
|
||
|
{0},
|
||
|
{2,0,2,1},
|
||
|
{0,0,0,0},
|
||
|
{3,1,3,0},
|
||
|
{0},
|
||
|
{2,0,3,1},
|
||
|
{2,1,3,1},
|
||
|
{2,1,3,0}
|
||
|
};
|
||
|
|
||
|
bool Clipper::CheckBox(const fixed_t *bspcoord)
|
||
|
{
|
||
|
angle_t angle1, angle2;
|
||
|
|
||
|
int boxpos;
|
||
|
const int* check;
|
||
|
|
||
|
// Find the corners of the box
|
||
|
// that define the edges from current viewpoint.
|
||
|
boxpos = (viewx <= bspcoord[BOXLEFT] ? 0 : viewx < bspcoord[BOXRIGHT ] ? 1 : 2) +
|
||
|
(viewy >= bspcoord[BOXTOP ] ? 0 : viewy > bspcoord[BOXBOTTOM] ? 4 : 8);
|
||
|
|
||
|
if (boxpos == 5) return true;
|
||
|
|
||
|
check = checkcoord[boxpos];
|
||
|
angle1 = R_PointToPseudoAngle (viewx, viewy, bspcoord[check[0]], bspcoord[check[1]]);
|
||
|
angle2 = R_PointToPseudoAngle (viewx, viewy, bspcoord[check[2]], bspcoord[check[3]]);
|
||
|
|
||
|
return SafeCheckRange(angle2, angle1);
|
||
|
}
|
||
|
|