Software Edge Clipping With GL?

Discuss programming topics for the various GPL'd game engine sources.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Software Edge Clipping With GL?

Post by Baker »

The software renderer doesn't have Z-fighting with brush models because it clips the edges of brush model to the world.

I've been thinking a little of trying to use this in a GLQuake engine.

I've been looking through the software renderer code, it seems like a somewhat largish challenge would be converting the clipped edges into polygons and getting the texcoords. It looks like the model loader provides all the information needed to do this.

[I'd also kind of like to get alpha-masked textures (fence-like textures) working in software, but there are some obstacles to that too ...]
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Software Edge Clipping With GL?

Post by Baker »

A tighter R_RecursiveWorld node:

Code: Select all

void R_RecursiveWorldNode (mnode_t *node, int clipflags)
{
	int			i, c, side, *pindex;
	vec3_t		acceptpt, rejectpt;
	mplane_t	*plane;
	msurface_t	*surf, **mark;
	mleaf_t		*pleaf;
	double		d, dot;

	if (node->contents == CONTENTS_SOLID)
		return;		// solid

	if (node->visframe != cl.r_visframecount)
		return;

// cull the clipping planes if not trivial accept
// FIXME: the compiler is doing a lousy job of optimizing here; it could be
//  twice as fast in ASM
	if (clipflags)
	{
		for (i = 0 ; i < 4 ; i++)
		{
			if (! (clipflags & (1<<i)) )
				continue;	// don't need to clip against it

		// generate accept and reject points
		// FIXME: do with fast look-ups or integer tests based on the sign bit
		// of the floating point values

			pindex = pfrustum_indexes[i];

			rejectpt[0] = (float)node->minmaxs[pindex[0]];
			rejectpt[1] = (float)node->minmaxs[pindex[1]];
			rejectpt[2] = (float)node->minmaxs[pindex[2]];

			d = DotProduct (rejectpt, view_clipplanes[i].normal);
			d -= view_clipplanes[i].dist;

			if (d <= 0)
				return;

			acceptpt[0] = (float)node->minmaxs[pindex[3+0]];
			acceptpt[1] = (float)node->minmaxs[pindex[3+1]];
			acceptpt[2] = (float)node->minmaxs[pindex[3+2]];

			d = DotProduct (acceptpt, view_clipplanes[i].normal);
			d -= view_clipplanes[i].dist;

			if (d >= 0)
				clipflags &= ~(1<<i);	// node is entirely on screen
		}
	}

// if a leaf node, draw stuff
	if (node->contents < 0)
	{
		pleaf = (mleaf_t *)node;

		mark = pleaf->firstmarksurface;
		c = pleaf->nummarksurfaces;

		if (c)
		{
			for ( ; c ; c--, mark++)
			{
				if (!level.water_vis_known)
					Liquid_Think ((*mark)->flags & SURF_UNDERWATER);

				(*mark)->visframe = cl.r_framecount;
			}
		}

	// deal with model fragments in this leaf
		if (pleaf->efrags)
			R_StoreEfrags (&pleaf->efrags);

		pleaf->key = r_currentkey;
		r_currentkey++;		// all bmodels in a leaf share the same key
	}
	else
	{
	// node is just a decision point, so go down the appropriate sides

	// find which side of the node we are on
		plane = node->plane;

		switch (plane->type)
		{
		case PLANE_X:
			dot = modelorg[0] - plane->dist;
			break;
		case PLANE_Y:
			dot = modelorg[1] - plane->dist;
			break;
		case PLANE_Z:
			dot = modelorg[2] - plane->dist;
			break;
		default:
			dot = DotProduct (modelorg, plane->normal) - plane->dist;
			break;
		}

		if (dot >= 0)
			side = 0;
		else
			side = 1;

	// recurse down the children, front side first
		R_RecursiveWorldNode (node->children[side], clipflags);

	// draw stuff
		c = node->numsurfaces;

		if (c)
		{
			surf = cl.worldmodel->surfaces + node->firstsurface;

			if (dot < 0 -BACKFACE_EPSILON)
				side = SURF_PLANEBACK;
			else if (dot > BACKFACE_EPSILON)
				side = 0;
	
	
			for ( ; c ; c--, surf++)
			{
				if (surf->visframe != cl.r_framecount)
					continue;
	
				if (( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)))
					continue; // wrong side
				
				if (!level.water_vis_known)
					Liquid_Think (surf->flags & SURF_UNDERWATER);
	
				R_RenderFace (surf, clipflags);
	
			} // End of for

			// all surfaces on the same node share the same sequence number
			r_currentkey++;
		} // End of if c

	// recurse down the back side
		R_RecursiveWorldNode (node->children[!side], clipflags);
	} // End of else node->contents < 0
}
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
qbism
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am
Contact:

Re: Software Edge Clipping With GL?

Post by qbism »

Fence textures would be incredibly easy in a software engine that already generates its own alphamap tables. It's just a new table. Color index X let's the base color show through.
In terms of mapping use *fenceXXX texture naming and the engine can flag it. The compiler will treat it like water so have to clip brush around it. I can't remember if typical compiler treats all * as water.
I did a similar thing for *glass.
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Software Edge Clipping With GL?

Post by mankrip »

The software renderer doesn't actually clip the edges. It doesn't generate new edges, and it doesn't generate new vertices, so there's no way to use it to reshape the brushes.

The clipping is done at the spans level. It's kinda like a run-lenght encoded Z-buffer check: each span is segmented by the nearer span segments of other planes.

To port this to the GL renderer, a large portion of the software renderer would have to be used, all the way to the spans generation. And then, a fragment shader could be used to make the GPU only draw the pixels that matches the corresponding spans from the software renderer.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Software Edge Clipping With GL?

Post by Baker »

mankrip wrote:The software renderer doesn't actually clip the edges. It doesn't generate new edges, and it doesn't generate new vertices, so there's no way to use it to reshape the brushes.
Ah that sucks. The functions have names like R_ClipEdge. :(
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
leileilol
Posts: 2783
Joined: Fri Oct 15, 2004 3:23 am

Re: Software Edge Clipping With GL?

Post by leileilol »

that's more for preventing from drawing past the refdef's edges iirc
i should not be here
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Software Edge Clipping With GL?

Post by Baker »

leileilol wrote:that's more for preventing from drawing past the refdef's edges iirc
Hmmm. I still insist on "no z-fighting" but this means 2015. I'll probably have to crack open the BSP compiler, get down with windings and CW vs. CCW, plane calcs and clip them against the world model myself. Which is going to depend on efrags, which can be troublesome ...

Very few brushes z-fight, but some of the ones that do, there is no acceptable solution. I was hoping to leverage the rarity of the situation.
qbism wrote:Fence textures would be incredibly easy in a software engine that already generates its own alphamap tables. It's just a new table. Color index X let's the base color show through.
In terms of mapping use *fenceXXX texture naming and the engine can flag it. The compiler will treat it like water so have to clip brush around it. I can't remember if typical compiler treats all * as water.
I did a similar thing for *glass.
A sensible map would only use 'fence textures' on brush model entities. That being said, Kurok and Rubicon Rumble both use alpha masked textures on the world, at least a little.

I say sensible because alpha masked textures on the world is going to show void, which without glClear or some equivalent is going to do render HOM.

What you said about alpha masked textures and alpha maps, I haven't quite yet dug deep enough into rendering to determine even how to do .alpha entities yet. Probably a few days out.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
qbism
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am
Contact:

Re: Software Edge Clipping With GL?

Post by qbism »

Via texture naming the compiler will treat fence textures like water, so won't see void. But will also inherit whatever limitations water brushes have.
Brush models are the 'hard way'' to implement but are more flexible and easier to map. Alpha is implemented this way in Makaqu in a robust way in terms of proper sorting and layered transparency. The only SW engine AFAIK that renders backtoforward correctly.
Knightmare
Posts: 63
Joined: Thu Feb 09, 2012 1:55 am

Re: Software Edge Clipping With GL?

Post by Knightmare »

Baker wrote:A tighter R_RecursiveWorld node:

Code: Select all

void R_RecursiveWorldNode (mnode_t *node, int clipflags)
{
	.
	.
	.
		if (c)
		{
			.
			.
			.	
	
			for ( ; c ; c--, surf++)
			{
				if (surf->visframe != cl.r_framecount)
					continue;
	
				if (( (dot < 0) ^ !!(surf->flags & SURF_PLANEBACK)))
					continue; // wrong side
				
				if (!level.water_vis_known)
					Liquid_Think (surf->flags & SURF_UNDERWATER);
	
				R_RenderFace (surf, clipflags);
	
			} // End of for

			// all surfaces on the same node share the same sequence number
			r_currentkey++;
		} // End of if c

	// recurse down the back side
		R_RecursiveWorldNode (node->children[!side], clipflags);
	} // End of else node->contents < 0
}
It's not a good idea to call your render function from inside R_RecursiveWorldNode(), as it leads to pipeline stalls. You should build a linked list of surfaces there instead, and render it after the BSP tree traversal. This latter approach better lends itself to keeping the GPU's pipeline full.

Ignore this if your R_RenderFace() just adds the surface to a list for later rendering.

I changed Q2's and Daikatana's multitexture rendering path to use texturechains (what Q2's ref_gl calls its linked lists of surfaces), and got a significant performance improvement, especially on ATI/AMD and Intel hardware. Batching dynamic light updates in multitexture (as in MH's enhanced Q2 code) also yielded a similar improvement when dynamicly lit surfaces are present.
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Software Edge Clipping With GL?

Post by mh »

It's also the case that texturechains built from R_RecursiveWorldNode (using the stock code) are in back-to-front order, which is not optimally friendly for modern GPUs. You can reverse the chains before drawing (I suggest reversing into per-lightmap chains) or chain in front-to-back order instead (see JoeQuake for one way of doing it) which can help get the performance up a little more.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Software Edge Clipping With GL?

Post by Baker »

Knightmare wrote:It's not a good idea to call your render function from inside R_RecursiveWorldNode(), as it leads to pipeline stall
The code above is in WinQuake, the software renderer in r_bsp.c. Note the R_RenderFace, which is a function in r_draw.c and the comment "// FIXME: the compiler is doing a lousy job of optimizing here; it could be twice as fast in ASM". GLQuake doesn't use ASM for drawing.

I was hoping to reverse out how it clips edges in brush model entity polygons against the world, but it doesn't work the way I hoped. WinQuake doesn't Z-fight brush model entities vs. the world, I was hoping to unlock this mechanism for use in GL, but looks like this was a mostly a dead lead ...
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Software Edge Clipping With GL?

Post by mankrip »

Baker wrote:I was hoping to reverse out how it clips edges in brush model entity polygons against the world, but it doesn't work the way I hoped. WinQuake doesn't Z-fight brush model entities vs. the world, I was hoping to unlock this mechanism for use in GL, but looks like this was a mostly a dead lead ...
Well, as I said before, you could probably reintegrate the software BSP renderer back in the engine, and use its spans on a fragment shader as a whitelist of pixels that should be rendered for each surface.

Alternatively, you could also just use it to skip submodel BSP faces entirely; if the edges being processed are from a BSP submodel, and no spans are generated, skip the rendering of its corresponding surface in the GL renderer. This should take care of most situations.

_____________________________


On an unrelated note, it doesn't feel right for me to use asterisk-named textures for "fence" textures. The natural way would be using textures with alphamasked texels (color 255) or an alpha channel.

Compiling such surfaces as turbulent surfaces brings up some issues too complicated to be worth it:
- No lightmap
- Non-solid faces
- pointcontents set to water
Using invisible func_wall entities and the like to make them seem solid would be an awkward solution. It's counter-intuitive, and the mapper shouldn't have such an extra burden brought upon him/her.

It's easier to just make a modified VIS compiler to make surfaces with (semi)transparent texels become see-through like water.

The "grey void"/HOM effect problem won't happen on surfaces whose alphamasked texels aren't close to their edges and when they're created in places that the player can't look at from extreme angles.

Using BSP submodels with alphamasked textures is the easiest solution, but it may be unnecessarily slow if not optimized properly. Only their surfaces with alphamasked textures should be treated as see-through; the rest shouldn't.

Also, it would be nice to make them solid to the player, while allowing projectiles such as rockets and grenades to pass through them. This happens in Doom, but I don't remember if such mechanics are present in the vanilla engine.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: Software Edge Clipping With GL?

Post by Baker »

mankrip wrote:Alternatively, you could also just use it to skip submodel BSP faces entirely; if the edges being processed are from a BSP submodel, and no spans are generated, skip the rendering of its corresponding surface in the GL renderer. This should take care of most situations.
If you load Warpspasm in a software renderer, in the starting rooms there are health boxes situated at an angle. And WinQuake doesn't actually render them right (I should have considered this a clue it wasn't clipping the edges in the literal sense.)

Since the majority of brush models might as well be simple boxes, when time permits I'd like to experiment with the idea of literally clipping the edges mostly because it will give me extra practice with 3D calculations.

And should solve case the Warpspasm case too.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
mankrip
Posts: 924
Joined: Fri Jul 04, 2008 3:02 am

Re: Software Edge Clipping With GL?

Post by mankrip »

WarpSpasm runs in WinQuake? I've got to try it.
Ph'nglui mglw'nafh mankrip Hell's end wgah'nagl fhtagn.
==-=-=-=-=-=-=-=-=-=-==
Dev blog / Twitter / YouTube
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Re: Software Edge Clipping With GL?

Post by mh »

mankrip wrote:On an unrelated note, it doesn't feel right for me to use asterisk-named textures for "fence" textures. The natural way would be using textures with alphamasked texels (color 255) or an alpha channel.
The reason for the choice of '{' (not '*') for these was because it was what HL1 used. It felt more right to copy some prior art. Integration also becomes easier if you want your engine to also support HL1 maps (you've only one code-path to write).
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
Post Reply