Jump to content
Search In
  • More options...
Find results that contain...
Find results in...
drfrag

[SOLVED] I need help to implement line culling in ZDoom

Recommended Posts

Hi, i need help from someone who knows the internals of the Doom engine to port the recent QZDoom feature that culls lines beyond a specified distance to ZDoom. It currently mostly works but i'm getting some HOMS. I think this is pretty cool since greatly enhances performance on big maps with lots of two sided lines even at very long distances. I already ported the sprite culling feature but that was an easy one.

 

Quote

I would do the following, if I were to implement this:

    In RenderOpaquePass::CheckBBox (R_CheckBBox in in ZDoom) I would add a range test. If the bounding box is further than the far clipping distance then return false in this function. This will make it stop walking the BSP deeper than what is to be rendered.
    In FWallCoords::Init clip the line against the far clipping plane. Return false when this results in the entire line being clipped. This will make it stop drawing walls exactly at the clip boundary.
    The two first steps will leave unfilled holes in the distance. The clipper knows the ranges that are unfilled. Walk the solidsegs list (RenderClipSegment in GZD, global variable in ZDoom). Fill those ranges with black.

 

It probably will look better if the black fill is done with the sky if it is outside. I'm not sure how to detect this situation in an easy way as walking the solidsegs list won't contain the information required. Perhaps instead of walking the solidsegs it could draw a fake second line if the real line was clipped against the far clipping plane. That fake line would either draw a sky or black depending on the sector.


The original implementation by dpJudas follows, finally he did it in a different way.

 

https://github.com/raa-eruanna/qzdoom/commit/7d1de667befd80ef1f64082d358e1c813f453511

 

Quote

It essentially works like this: if both vertices for a line is past the far clipping distance then it fills the area covered by the line with the top ceiling (the sky when outside) and tells the clipper it was a solid line.


And this it what i've done. I could upload the branch to GitHub if that helps. My repo is @ https://github.com/drfrag666/gzdoom/tree/zdoom32

Thanks in advance.

Edit: never mind. I've fixed it myself.

 

 src/r_bsp.cpp | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 src/r_bsp.h   |   3 ++
 2 files changed, 137 insertions(+), 7 deletions(-)

diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp
index 8d423b3b3..f45f845f4 100644
--- a/src/r_bsp.cpp
+++ b/src/r_bsp.cpp
@@ -63,6 +63,7 @@ side_t* 		sidedef;
 line_t* 		linedef;
 sector_t*		frontsector;
 sector_t*		backsector;
+sector_t*		fcfrontsector;
 
 // killough 4/7/98: indicates doors closed wrt automap bugfix:
 int				doorclosed;
@@ -77,6 +78,11 @@ extern short	walltop[MAXWIDTH];	// [RH] record max extents of wall
 extern short	wallbottom[MAXWIDTH];
 extern short	wallupper[MAXWIDTH];
 extern short	walllower[MAXWIDTH];
+short	fcwalltop[MAXWIDTH];
+short	fcwallbottom[MAXWIDTH];
+
+visplane_t 				*fcfloorplane;
+visplane_t 				*fcceilingplane;
 
 double			rw_backcz1, rw_backcz2;
 double			rw_backfz1, rw_backfz2;
@@ -107,6 +113,20 @@ subsector_t *InSubsector;
 CVAR (Bool, r_drawflat, false, 0)		// [RH] Don't texture segs?
 EXTERN_CVAR(Bool, r_fullbrightignoresectorcolor);
 
+double line_distance_cull = 1e16;
+
+CUSTOM_CVAR(Float, r_linedistancecull, 8000.0, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)
+{
+	if (r_linedistancecull > 0.0)
+	{
+		line_distance_cull = r_linedistancecull * r_linedistancecull;
+	}
+	else
+	{
+		line_distance_cull = 1e16;
+	}
+}
+
 
 void R_StoreWallRange (int start, int stop);
 
@@ -160,7 +180,7 @@ static cliprange_t		solidsegs[MAXWIDTH/2+2];
 //
 //==========================================================================
 
-bool R_ClipWallSegment (int first, int last, bool solid)
+bool R_ClipWallSegment (int first, int last, bool solid, bool farclip)
 {
 	cliprange_t *next, *start;
 	int i, j;
@@ -178,7 +198,10 @@ bool R_ClipWallSegment (int first, int last, bool solid)
 		if (last <= start->first)
 		{
 			// Post is entirely visible (above start).
-			R_StoreWallRange (first, last);
+			if (!farclip)
+				R_StoreWallRange (first, last);
+			else
+				R_AddFarClipWallSegment(first, last);
 			if (fake3D & FAKE3D_FAKEMASK)
 			{
 				return true;
@@ -208,7 +231,10 @@ bool R_ClipWallSegment (int first, int last, bool solid)
 		}
 
 		// There is a fragment above *start.
-		R_StoreWallRange (first, start->first);
+		if (!farclip)
+			R_StoreWallRange (first, start->first);
+		else
+			R_AddFarClipWallSegment(first, start->first);
 
 		// Adjust the clip size for solid walls
 		if (solid && !(fake3D & FAKE3D_FAKEMASK))
@@ -225,7 +251,10 @@ bool R_ClipWallSegment (int first, int last, bool solid)
 	while (last >= (next+1)->first)
 	{
 		// There is a fragment between two posts.
-		R_StoreWallRange (next->last, (next+1)->first);
+		if (!farclip)
+			R_StoreWallRange (next->last, (next+1)->first);
+		else
+			R_AddFarClipWallSegment(next->last, (next+1)->first);
 		next++;
 		
 		if (last <= next->last)
@@ -237,7 +266,10 @@ bool R_ClipWallSegment (int first, int last, bool solid)
 	}
 
 	// There is a fragment after *next.
-	R_StoreWallRange (next->last, last);
+	if (!farclip)
+		R_StoreWallRange (next->last, last);
+	else
+		R_AddFarClipWallSegment(next->last, last);
 
 crunch:
 	if (fake3D & FAKE3D_FAKEMASK)
@@ -302,6 +334,93 @@ void R_ClearClipSegs (short left, short right)
 }
 
 
+// FarClipLine
+void R_AddFarClipLine(seg_t *line)
+{
+	fcfrontsector = InSubsector->sector;
+	fcfloorplane = floorplane;
+	fcceilingplane = ceilingplane;
+
+	DVector2 pt1 = line->v1->fPos() - ViewPos;
+	DVector2 pt2 = line->v2->fPos() - ViewPos;
+
+	// Reject lines not facing viewer
+	if (pt1.Y * (pt1.X - pt2.X) + pt1.X * (pt2.Y - pt1.Y) >= 0)
+		return;
+
+	if (WallC.Init(pt1, pt2, 32.0 / (1 << 12)))
+		return;
+
+	if (WallC.sx1 >= WindowRight || WallC.sx2 <= WindowLeft)
+		return;
+
+	if (line->linedef == nullptr)
+		return;
+
+	// reject lines that aren't seen from the portal (if any)
+	// [ZZ] 10.01.2016: lines inside a skybox shouldn't be clipped, although this imposes some limitations on portals in skyboxes.
+	if (!CurrentPortalInSkybox && CurrentPortal && P_ClipLineToPortal(line->linedef, CurrentPortal->dst, ViewPos))
+		return;
+
+	R_ClipWallSegment (WallC.sx1, WallC.sx2, true, true);
+}
+
+void R_AddFarClipWallSegment(int x1, int x2)
+{
+	//WallMost (fcwalltop, fcfrontsector->ceilingplane, &WallC);
+	WallMost (fcwallbottom, fcfrontsector->floorplane, &WallC);
+	memcpy(fcwalltop, fcwallbottom, sizeof(short) * MAXWIDTH);
+
+	// clip wall to the floor and ceiling
+	for (int x = x1; x < x2; ++x)
+	{
+		if (fcwalltop[x] < ceilingclip[x])
+		{
+			fcwalltop[x] = ceilingclip[x];
+		}
+		if (fcwallbottom[x] > floorclip[x])
+		{
+			fcwallbottom[x] = floorclip[x];
+		}
+	}
+	
+	if (fcceilingplane)
+	{
+		fcceilingplane = R_CheckPlane (fcceilingplane, x1, x2);
+
+		for (int x = x1; x < x2; ++x)
+		{
+			short top = (fakeFloor && fake3D & 2) ? fakeFloor->ceilingclip[x] : ceilingclip[x];
+			short bottom = MIN(fcwalltop[x], floorclip[x]);
+			if (top < bottom)
+			{
+				fcceilingplane->top[x] = top;
+				fcceilingplane->bottom[x] = bottom;
+			}
+		}
+	}
+
+	if (fcfloorplane)
+	{
+		fcfloorplane = R_CheckPlane (fcfloorplane, x1, x2);
+		
+		for (int x = x1; x < x2; ++x)
+		{
+			short top = MAX(fcwallbottom[x], ceilingclip[x]);
+			short bottom = (fakeFloor && fake3D & 1) ? fakeFloor->floorclip[x] : floorclip[x];
+			if (top < bottom)
+			{
+				assert(bottom <= viewheight);
+				fcfloorplane->top[x] = top;
+				fcfloorplane->bottom[x] = bottom;
+			}
+		}
+	}
+	
+	return;
+}
+
+
 //
 // killough 3/7/98: Hack floor/ceiling heights for deep water etc.
 //
@@ -767,7 +886,7 @@ void R_AddLine (seg_t *line)
 #endif
 	}
 
-	if (R_ClipWallSegment (WallC.sx1, WallC.sx2, solid))
+	if (R_ClipWallSegment (WallC.sx1, WallC.sx2, solid, false))
 	{
 		InSubsector->flags |= SSECF_DRAWN;
 	}
@@ -1320,9 +1439,17 @@ void R_Subsector (subsector_t *sub)
 	count = sub->numlines;
 	line = sub->firstline;
 
+	DVector2 viewpointPos = ViewPos.XY();
+
 	while (count--)
 	{
-		if (!outersubsector || line->sidedef == NULL || !(line->sidedef->Flags & WALLF_POLYOBJ))
+		double dist1 = (line->v1->fPos() - viewpointPos).LengthSquared();
+		double dist2 = (line->v2->fPos() - viewpointPos).LengthSquared();
+		if (dist1 > line_distance_cull && dist2 > line_distance_cull)
+		{
+			R_AddFarClipLine(line);
+		}
+		else if (!outersubsector || line->sidedef == nullptr || !(line->sidedef->Flags & WALLF_POLYOBJ))
 		{
 			// kg3D - fake planes bounding calculation
 			if (r_3dfloors && line->backsector && frontsector->e && line->backsector->e->XFloor.ffloors.Size())
diff --git a/src/r_bsp.h b/src/r_bsp.h
index 48ca7565b..4bbb35aee 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -119,5 +119,8 @@ void R_RenderBSPNode (void *node);
 // killough 4/13/98: fake floors/ceilings for deep water / fake ceilings:
 sector_t *R_FakeFlat(sector_t *, sector_t *, int *, int *, bool);
 
+// FarClipLine
+void R_AddFarClipLine(seg_t *line);
+void R_AddFarClipWallSegment(int x1, int x2);
 
 #endif

 

Edited by drfrag

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×