crosshairs in reQuiem (JoeQuake)

Discuss programming topics for the various GPL'd game engine sources.
Post Reply
Johnny Law
Posts: 22
Joined: Mon Apr 28, 2014 8:34 pm
Location: San Carlos, CA
Contact:

crosshairs in reQuiem (JoeQuake)

Post by Johnny Law »

Second in my current HAY WHATS UP WITH THIS series!

Here's something I noticed in reQuiem which apparently goes all the way back to JoeQuake. (I've checked the JoeQuake code but haven't actually run JoeQuake to compare.)

JoeQuake has some built-in 8x8 crosshair graphics. For some reason it doesn't draw them dead-center in the screen; instead the quad for the crosshair graphic is shifted down and to the right by a half pixel from center.

I don't know if this is trying to accomodate the standard "not quite centered" Quake aimpoint, or a misunderstanding of how OpenGL places quad vertices, or something else.

A half pixel doesn't sound like a big deal, but the crosshair graphic is super-low-rez and using nearest-pixel mapping, so at certain crosshair scales and screen resolutions it looks misshapen.

==========

The crosshair graphic also wasn't clamped to the quad, so you could get the texture bleeding from one edge to the other like this:

Image

==========

I've tried just placing the quad dead-center and clamping the texture, and it looks perfectly OK now as far as I can tell.

Obviously you may have to adjust cl_crossx and maybe cl_crossy to try to put the crosshair on your aimpoint, but that's business as usual for Quake (and was still the case even with the half-pixel shift).

So that makes me wonder why the code was the way it was.

==========

The other thing that's confusing me is the placement of custom (user-provided) crosshair images, which can be arbitrarily sized. JoeQuake positions a custom crosshair using "ofs1" (distance from center to the top or left edge of the quad) and "ofs2" (distance from center to right or bottom edge) like this:

Code: Select all

ofs1 = 4 - 4.0 / crosshairpic.width;
ofs2 = 4 + 4.0 / crosshairpic.width;
...so, besides the fact that this is forcing the quad to be square (regardless of the original image's shape), it puts it less off-center for a larger original image.

Whhaaaaat is up with this?
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: crosshairs in reQuiem (JoeQuake)

Post by Spike »

expect between 15-50 defects per 1000 lines of code, depending on language and programmer.

the 0.5 thing might be to work around an issue with the d3d wrapper that was popular at some point (d3d9 uses different texture coord origins or something).

the only way to center the aim point is to 1) use more precise angles for the client->server messages. 2) move the view offset down by 6qu. these two things combined should ensure that the projectile always hits the exact center of the screen.

the center of the screen is not a single pixel. crosshairs look better when they ARE a single pixel. this could be another reason for the 0.5

conwidth/conheight not matching physical coords is another bug. rescaling with nearest-sampling is nasty stuff.

using linear sampling will result in bleeding through to other chars in the charset. the only way around this is to nudge the texture coords in by half a texel to avoid it sampling outside. this is made more awkward if using mipmapping (another reason to not rescale).
GL_CLAMP_TO_EDGE is not an option with atlased images (ie: conchars).

I've no idea what the ofs stuff is about. it could be to counter other offsets elsewhere.
Johnny Law
Posts: 22
Joined: Mon Apr 28, 2014 8:34 pm
Location: San Carlos, CA
Contact:

Re: crosshairs in reQuiem (JoeQuake)

Post by Johnny Law »

Spike wrote:I've no idea what the ofs stuff is about. it could be to counter other offsets elsewhere.
That's the part that really baffles me.

Imagine a custom 4x4 crosshair image. That original code would map it onto an 8x8 quad that is off-center by a whole pixel. Now imagine blowing that image up with simple pixel doubling until it is 16x16. It would still be drawn onto an 8x8 quad, and be effectively the same crosshair, but this time placed off-center by a quarter pixel.

The only guess I have at the moment is that it's an actual code/math mistake, and the custom crosshair stuff was only tested with 8x8 images so that it ended up behaving the same as the built-in crosshair code. (With the half-pixel offset, which still leaves the question of why do that, but at least that reduces it to one mystery instead of two.)

In the absence of better ideas I'll probably just precisely center everything, clamp the texture nicely to the quad, and see if anyone ever complains about some aspect of that.

As for forcing the custom crosshair graphic onto an 8x8 quad regardless of the size of the original image... eh, my first reaction was to size the quad to the image, but I can see an argument for using the same-sized quad as the built-in crosshairs. That would keep the meaning of a given crosshairscale value constant. Probably fine either way, and among the least of the things I should be spending time on. :)

P.S., w.r.t. this:
Spike wrote:expect between 15-50 defects per 1000 lines of code, depending on language and programmer.
While I would never argue with anyone's cynicism about software ;-) I do get a little suspicious when apparently coming across a bug in something that has been staring many people in the face for many years. It feels like the odds could be a bit higher that I'm just misunderstanding or overlooking something.
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Re: crosshairs in reQuiem (JoeQuake)

Post by Baker »

Johnny Law wrote:...so, besides the fact that this is forcing the quad to be square (regardless of the original image's shape), it puts it less off-center for a larger original image.

Whhaaaaat is up with this?
JoeQuake was based off of FuhQuake essentially (FuhQuake was the dominant Quakeworld client from 2002-2006 and the predecessor to ezQuake) and I'm sure there was a standard crosshair size for the engine.

In fact, it conforms to this
http://ezquake.sourceforge.net/docs/?crosshairs wrote:Making Crosshair Images

This section explains how to make PNG/TGA crosshairs.

Firstly pick a size. The image should be square and preferably have width and height being a power of 2. 64x64 is a good example.

The problem with powers of 2 is that there is no middle pixel. For example if you have a 64x64 image with top left pixel coordinates (1,1) and bottom right coordinates (64,64), then the middle is at (32.5, 32.5) which isn't a real coordinate. (32,32) and (33,33) are the two real coordinates closest to the middle. The client centres the 64x64 crosshair so that the (32,32) pixel is centred on screen. In otherwords (32,32) is treated as the centre.

In general if you have a crosshair image of width = height = 2^n with top left coordinate (1,1) and top right coordinate (2^n, 2^n) then the centre of the crosshair should be positioned over pixel (2^(n-1), 2^(n-1)).
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 ..
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: crosshairs in reQuiem (JoeQuake)

Post by r00k »

Code: Select all

	if (!crosshair_static.value && scr_viewsize.value >= 100 && cl.worldmodel)//dynamic xhair by Entar/LordHavoc
	{
		AngleVectors (cl.viewangles,  fwds, NULL, NULL);	
		VectorCopy(cl_entities[cl.viewentity].origin, start);
		start[2] +=  16;//QuakeC uses + '0 0 16' for gun aim.

		VectorMA(start, 4096, fwds, end);
	
		memset(&tr, 0, sizeof(tr));
		tr.fraction = 1;
		SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &tr);

		if (tr.fraction != 1)
		{
			start[2] +=  16;//Methinks the start gets reset after SV_RecursiveHullCheck....so this is required.
			ML_Project(tr.endpos, end, cl.viewangles, start, (float)scr_vrect.width/scr_vrect.height, r_refdef.fov_y);//Entar
			x = scr_vrect.x + scr_vrect.width/2;
			y = (scr_vrect.y+scr_vrect.height*(end[1]));
			y = scr_vrect.height - y; // Entar : HACKYNESS yes, but it works pretty well
			y -= (y - (scr_vrect.height / 2)) / 2;//
		}
		else
		{	
			x = scr_vrect.x + scr_vrect.width / 2 + cl_crossx.value; 
			y = scr_vrect.y + scr_vrect.height / 2 + cl_crossy.value;
		}
	}
	else
	{
			x = (scr_vrect.x + scr_vrect.width / 2 + cl_crossx.value); 
			y = (scr_vrect.y + scr_vrect.height / 2 + cl_crossy.value);
	}

Code: Select all

void ML_Project (vec3_t in, vec3_t out, vec3_t viewangles, vec3_t vieworg, float wdivh, float fovy)
{
	float modelview[16];
	float proj[16];

	ML_ModelViewMatrix(modelview, viewangles, vieworg);
	ML_ProjectionMatrix(proj, wdivh, fovy);

	{
		float v[4], tempv[4];
		v[0] = in[0];
		v[1] = in[1];
		v[2] = in[2];
		v[3] = 1;

		Matrix4_Transform4(modelview, v, tempv); 
		Matrix4_Transform4(proj, tempv, v);

		v[0] /= v[3];
		v[1] /= v[3];
		v[2] /= v[3];

		out[0] = (1+v[0])/2;
		out[1] = (1+v[1])/2;
		out[2] = (1+v[2])/2;
	}
}

Johnny Law
Posts: 22
Joined: Mon Apr 28, 2014 8:34 pm
Location: San Carlos, CA
Contact:

Re: crosshairs in reQuiem (JoeQuake)

Post by Johnny Law »

Baker wrote: In fact, it conforms to this ...
Interesting.

With the built-in crosshair graphics, crosshair 1 & 5 don't adhere to that spec, but 2/3/4/6 do.

It does explain the offset math used for the custom crosshair graphics, but subpixel shifts are useless as well as ugly for these kinds of images with nearest-pixel mapping. Probably the right thing to do is figure out the nearest whole-pixel shift after applying crosshairsize.
qbism
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am
Contact:

Re: crosshairs in reQuiem (JoeQuake)

Post by qbism »

Coords 1,1 and 64,64 with center at 32.5,32.5.... That's a 63x63 image.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: crosshairs in reQuiem (JoeQuake)

Post by Spike »

thing is, even if you move the crosshair down+right by half a pixel/texel, you still have no middle pixel! the crosshair is right in the middle of 4 adjacent pixels!
moving it by half a pixel either just doesn't do anything (if nearest sampling), or blurs it (linear sampling).
I'd say leave it up to the artists to decide the center point. they can make that pixel the top-left or the bottom-right of the central 4 if they want. its less faffing around that way.
unless of course if you're willing to chop off a single-pixel row+column at the edges of the screen. then you get a middle pixel that would warrent biasing the crosshair to effectively ignore a similar pixel row+column.
plus many engines support npot nowadays, which might be useful if your viewing area has an odd number of pixels...
otherwise its pretty pointless.

@r00k, your ML_Project looks like something I wrote... :P
your 'start[2] += 16;//Methinks the start gets reset after SV_RecursiveHullCheck....so this is required.' line is wrong. should be 6 (or viewheight-16) instead of 16.
QC spawns projectiles at origin+16 and the view is at origin+22.
which might help explain what all the other y maths stuff is about. its not working properly! fudge it!
naturally this does nothing to solve/explain the half-pixel thing.
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: crosshairs in reQuiem (JoeQuake)

Post by r00k »

Ya im not sure where i got the mathlib, possibly fte or dp.

This example was posted way back on quakesource.org in a thread that entar and LH were conversing about sliding the crosshair around, something Entar was adding to his engine project.

I was providing an alternative to having a static + in the center of the screen, personally a 3d laser pointer is the best. :D
QC spawns projectiles at origin+16
this is why i start with

Code: Select all

if (!crosshair_static.value && scr_viewsize.value >= 100 && cl.worldmodel)//dynamic xhair by Entar/LordHavoc
   {
      AngleVectors (cl.viewangles,  fwds, NULL, NULL);   
      VectorCopy(cl_entities[cl.viewentity].origin, start);
      start[2] +=  16;//QuakeC uses + '0 0 16' for gun aim.
but after

Code: Select all

   VectorMA(start, 4096, fwds, end);
   
      memset(&tr, 0, sizeof(tr));
      tr.fraction = 1;
      SV_RecursiveHullCheck (cl.worldmodel->hulls, 0, 0, 1, start, end, &tr);

when i try to just use

Code: Select all

ML_Project(tr.endpos, end, cl.viewangles, start, (float)scr_vrect.width/scr_vrect.height, r_refdef.fov_y);
It really doesnt shoot on the mark. So I just bumped it back up to gun height, the path of the projectile.
All thay Y math was existing code from Entar. :|
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: crosshairs in reQuiem (JoeQuake)

Post by Spike »

the first +=16 is correct, the second should be +=6 instead. then it doesn't need the half-distance fudge part (which would approximate 16/2 instead of 22-16, I guess, either way incorrect).
someone is wrong on the internet! yeah, this is going offtopic. :s
r00k
Posts: 1111
Joined: Sat Nov 13, 2004 10:39 pm

Re: crosshairs in reQuiem (JoeQuake)

Post by r00k »

i dont totally understand the whole math to it,

I added the +16 ,

as it wasnt there and without it didnt work....

so hey shes ugly but she does work....


The real question is why should i need a += 6 when i already modded start +=16 before the call to SV_RecursiveHullCheck ????
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Re: crosshairs in reQuiem (JoeQuake)

Post by Spike »

view is at origin+22. gun is at origin+16.
22-16 = 6.
more correct is to subtract 16 then add cl.viewheight or whereever that value is stored. then it works with non-default viewheights too.
assuming projectiles still fire from 16qu above the player's origin, anyway.
Post Reply