Cull lines not visible from the camera

This commit is contained in:
Magnus Norddahl 2025-05-30 22:20:19 +02:00
commit 2121047fa2
4 changed files with 162 additions and 3 deletions

View file

@ -35,6 +35,7 @@
#include "hw_drawinfo.h"
#include "hw_cvars.h"
#include "hw_drawcontext.h"
#include "hw_visibleset.h"
#include "r_utility.h"
#include "texturemanager.h"
@ -46,7 +47,7 @@ extern thread_local bool isWorkerThread;
//
//==========================================================================
bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector)
bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector, CameraFrustum* frustum)
{
line_t *linedef = sidedef->linedef;
double bs_floorheight1;
@ -155,6 +156,25 @@ bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsecto
return true;
}
if (frustum)
{
// Is the line visible by the camera's clip frustum?
DVector3 p0(linedef->v1->fX(), std::min(bs_ceilingheight1, fs_ceilingheight1), linedef->v1->fY());
DVector3 p1(linedef->v2->fX(), std::min(bs_ceilingheight2, fs_ceilingheight2), linedef->v2->fY());
DVector3 p2(linedef->v1->fX(), std::max(bs_floorheight1, fs_floorheight1), linedef->v1->fY());
DVector3 p3(linedef->v2->fX(), std::max(bs_floorheight2, fs_floorheight2), linedef->v2->fY());
DVector3 aabbMin = p0;
DVector3 aabbMax = p0;
aabbMin.X = std::min(aabbMin.X, p1.X); aabbMin.Y = std::min(aabbMin.Y, p1.Y); aabbMin.Z = std::min(aabbMin.Z, p1.Z);
aabbMax.X = std::max(aabbMax.X, p1.X); aabbMax.Y = std::max(aabbMax.Y, p1.Y); aabbMax.Z = std::max(aabbMax.Z, p1.Z);
aabbMin.X = std::min(aabbMin.X, p2.X); aabbMin.Y = std::min(aabbMin.Y, p2.Y); aabbMin.Z = std::min(aabbMin.Z, p2.Z);
aabbMax.X = std::max(aabbMax.X, p2.X); aabbMax.Y = std::max(aabbMax.Y, p2.Y); aabbMax.Z = std::max(aabbMax.Z, p2.Z);
aabbMin.X = std::min(aabbMin.X, p3.X); aabbMin.Y = std::min(aabbMin.Y, p3.Y); aabbMin.Z = std::min(aabbMin.Z, p3.Z);
aabbMax.X = std::max(aabbMax.X, p3.X); aabbMax.Y = std::max(aabbMax.Y, p3.Y); aabbMax.Z = std::max(aabbMax.Z, p3.Z);
if (!frustum->IsAABBVisible(aabbMin, aabbMax))
return true;
}
return false;
}

View file

@ -10,8 +10,10 @@ enum area_t : int
area_default
};
struct CameraFrustum;
// Global functions.
bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector);
bool hw_CheckClip(side_t * sidedef, sector_t * frontsector, sector_t * backsector, CameraFrustum* frustum = nullptr);
void hw_ClearFakeFlat(HWDrawContext* drawctx);
sector_t * hw_FakeFlat(HWDrawContext* drawctx, sector_t * sec, area_t in_area, bool back, sector_t *localcopy = nullptr);
area_t hw_CheckViewArea(vertex_t *v1, vertex_t *v2, sector_t *frontsector, sector_t *backsector);

View file

@ -52,6 +52,10 @@ void HWVisibleSet::FindPVS(HWDrawInfo* di, int sliceIndex, int sliceCount)
in_area = di->in_area;
mClipPortal = di->mClipPortal;
VSMatrix m = di->VPUniforms.mProjectionMatrix;
m.multMatrix(di->VPUniforms.mViewMatrix);
ClipFrustum.Set(m);
CurrentMapSections = &di->CurrentMapSections;
no_renderflags = TArrayView<uint8_t>(di->no_renderflags.data(), di->no_renderflags.size());
@ -381,7 +385,7 @@ void HWVisibleSet::AddLine(seg_t* seg, bool portalclip)
backsector = hw_FakeFlat(&drawctx, seg->backsector, in_area, true);
if (hw_CheckClip(seg->sidedef, currentsector, backsector))
if (hw_CheckClip(seg->sidedef, currentsector, backsector, &ClipFrustum))
{
clipper.SafeAddClipRange(startAngle, endAngle);
}
@ -653,3 +657,91 @@ void HWVisibleSetThreads::WorkerMain(int sliceIndex)
WorkCondvar.wait(lock);
}
}
/////////////////////////////////////////////////////////////////////////////
void CameraFrustum::Set(const VSMatrix& worldToProjection)
{
Planes[0] = LeftFrustum(worldToProjection);
Planes[1] = TopFrustum(worldToProjection);
Planes[2] = RightFrustum(worldToProjection);
Planes[3] = BottomFrustum(worldToProjection);
Planes[4] = NearFrustum(worldToProjection);
Planes[5] = FarFrustum(worldToProjection);
for (int i = 0; i < 6; i++)
{
AbsPlaneNormals[i] = Planes[i].XYZ();
AbsPlaneNormals[i].X = std::abs(AbsPlaneNormals[i].X);
AbsPlaneNormals[i].Y = std::abs(AbsPlaneNormals[i].Y);
AbsPlaneNormals[i].Z = std::abs(AbsPlaneNormals[i].Z);
}
}
DVector4 CameraFrustum::LeftFrustum(const VSMatrix& matrix)
{
return Normalize(DVector4(
matrix.mMatrix[3 + 0 * 4] + matrix.mMatrix[0 + 0 * 4],
matrix.mMatrix[3 + 1 * 4] + matrix.mMatrix[0 + 1 * 4],
matrix.mMatrix[3 + 2 * 4] + matrix.mMatrix[0 + 2 * 4],
matrix.mMatrix[3 + 3 * 4] + matrix.mMatrix[0 + 3 * 4]));
}
DVector4 CameraFrustum::RightFrustum(const VSMatrix& matrix)
{
return Normalize(DVector4(
matrix.mMatrix[3 + 0 * 4] - matrix.mMatrix[0 + 0 * 4],
matrix.mMatrix[3 + 1 * 4] - matrix.mMatrix[0 + 1 * 4],
matrix.mMatrix[3 + 2 * 4] - matrix.mMatrix[0 + 2 * 4],
matrix.mMatrix[3 + 3 * 4] - matrix.mMatrix[0 + 3 * 4]));
}
DVector4 CameraFrustum::TopFrustum(const VSMatrix& matrix)
{
return Normalize(DVector4(
matrix.mMatrix[3 + 0 * 4] - matrix.mMatrix[1 + 0 * 4],
matrix.mMatrix[3 + 1 * 4] - matrix.mMatrix[1 + 1 * 4],
matrix.mMatrix[3 + 2 * 4] - matrix.mMatrix[1 + 2 * 4],
matrix.mMatrix[3 + 3 * 4] - matrix.mMatrix[1 + 3 * 4]));
}
DVector4 CameraFrustum::BottomFrustum(const VSMatrix& matrix)
{
return Normalize(DVector4(
matrix.mMatrix[3 + 0 * 4] + matrix.mMatrix[1 + 0 * 4],
matrix.mMatrix[3 + 1 * 4] + matrix.mMatrix[1 + 1 * 4],
matrix.mMatrix[3 + 2 * 4] + matrix.mMatrix[1 + 2 * 4],
matrix.mMatrix[3 + 3 * 4] + matrix.mMatrix[1 + 3 * 4]));
}
DVector4 CameraFrustum::NearFrustum(const VSMatrix& matrix)
{
return Normalize(DVector4(
matrix.mMatrix[3 + 0 * 4] + matrix.mMatrix[2 + 0 * 4],
matrix.mMatrix[3 + 1 * 4] + matrix.mMatrix[2 + 1 * 4],
matrix.mMatrix[3 + 2 * 4] + matrix.mMatrix[2 + 2 * 4],
matrix.mMatrix[3 + 3 * 4] + matrix.mMatrix[2 + 3 * 4]));
}
DVector4 CameraFrustum::FarFrustum(const VSMatrix& matrix)
{
return Normalize(DVector4(
matrix.mMatrix[3 + 0 * 4] - matrix.mMatrix[2 + 0 * 4],
matrix.mMatrix[3 + 1 * 4] - matrix.mMatrix[2 + 1 * 4],
matrix.mMatrix[3 + 2 * 4] - matrix.mMatrix[2 + 2 * 4],
matrix.mMatrix[3 + 3 * 4] - matrix.mMatrix[2 + 3 * 4]));
}
DVector4 CameraFrustum::Normalize(DVector4 v)
{
double length = std::sqrt(v.XYZ() | v.XYZ());
if (length > DBL_EPSILON)
{
double rcpLength = 1.0 / length;
v.X *= rcpLength;
v.Y *= rcpLength;
v.Z *= rcpLength;
v.W *= rcpLength;
}
return v;
}

View file

@ -13,6 +13,50 @@ class Clipper;
class HWPortal;
struct HWDrawInfo;
struct CameraFrustum
{
void Set(const VSMatrix& worldToProjection);
bool IsSphereVisible(const DVector3& point, double radius) const
{
for (int i = 0; i < 6; i++)
{
if ((Planes[i].XYZ() | point) < -radius)
return false;
}
return true;
}
bool IsAABBVisible(const DVector3& aabbMin, const DVector3& aabbMax) const
{
DVector3 aabbCenter = (aabbMin + aabbMax) * 0.5;
DVector3 aabbExtents = (aabbMax - aabbMin) * 0.5;
for (int i = 0; i < 6; i++)
{
double distance = Planes[i] | DVector4(aabbCenter, 1.0);
double radius = AbsPlaneNormals[i] | aabbExtents;
bool outside = distance < -radius;
//bool inside = distance > radius;
//bool intersect = distance > -radius && distance < radius;
if (outside)
return false;
}
return true;
}
private:
static DVector4 LeftFrustum(const VSMatrix& matrix);
static DVector4 RightFrustum(const VSMatrix& matrix);
static DVector4 TopFrustum(const VSMatrix& matrix);
static DVector4 BottomFrustum(const VSMatrix& matrix);
static DVector4 NearFrustum(const VSMatrix& matrix);
static DVector4 FarFrustum(const VSMatrix& matrix);
static DVector4 Normalize(DVector4 v);
DVector3 AbsPlaneNormals[6];
DVector4 Planes[6];
};
class HWVisibleSet
{
public:
@ -77,6 +121,7 @@ private:
HWDrawContext drawctx;
FLevelLocals* Level = nullptr;
FRenderViewpoint Viewpoint;
CameraFrustum ClipFrustum;
TArrayView<uint8_t> no_renderflags;
BitArray* CurrentMapSections = nullptr;
TArray<uint8_t> section_renderflags;