"More than just programming"

Why All Coordinate Systems Suck


NQ Transmission: Why All Coordinate Systems Suck

NQIsaac here, the following is to shed some light on the insanity that is coordinates and the world of graphics APIs. If you don't have a drink, I'd recommend pouring one before starting.

The Basics and Differences

Back in school I'm sure everyone had to graph some function like y = 2x + 1 on some grid paper. You probably started with drawing a big '+' on the paper. You knew that 0,0 was the center of this plus. As you started to graph this line the x value was on the horizontal axis and the y value was on the vertical axis. As both values increase the placement on the grid moves up and right. Most people are probably thinking "Yea, of course it is". Great, welcome to insanity.

As you might have guessed these accepted realites are far from universal. So the first thing to change is that when rendering something the y axis becomes reverse, ie as y increases its position moves down. This reverses the direction of rotation on the x,y plane to be clockwise. Thankfully 0,0 remains in the center for rendering.

Unlike for rendering the mouse position is provided with 0,0 as the top left corner of the screen. The y value is still positive moving down with x unchanged. This is a holdover from way back in the CRT days https://gamedev.stackexchange.com/a/83571

Things get even more complicated when you realize that this has only been for x,y. When you add in the z axis you have a choice. Is positive z towards the screen or away from the screen? Just like before the answer is "yes". The two options are referred to as left-handed (z is positive away from you) or right-handed (z is positive toward you) coordinate systems. In case you were wondering, y in these systems is positive up again!. In fact there are even more options; for example what if we decided to make positive y toward you positive z up and positive x as right? Well that would give you what Blender uses (as well as a headache by this point).

Blatantly ignoring the other variations let's look at what the two main APIs use. DirectX uses a left-handed system while OpenGL uses a right-handed system. So if you want to support both you need to have coordinate translation built in to even render. One of the reasons using an existing engine is tempting is that they will have just decided on a system and you can ignore the myriad of options and spend your time actually building the game. Of course that would make things too easy which is why we are making this choice ourselves for our own engine NQTech.

You might have noticed that I said earlier that y is positive up but I also said y is positive down. Turns out the answer is again a frustrating "yes". When rendering to different places different systems are used. OpenGL lists the following five coordinate spaces (https://learnopengl.com/Getting-started/Coordinate-Systems):

There's no guidance I have to offer here. I spent several hours reading about which was which and I still don't have any clarity on what coordinate system I'm using any given time. You might ask "why isn't there a standard?" because standards are unicorns and programming sucks Side Note: this article is a fantastic read much <3 to Mr. Welch for writing it). If you're confused don't worry, so are we.

Practical Mistakes and Misfortune

Let's move on to some concrete semi-technical examples of some of the problems that can crop up.

A common operation is to rotate something. Here is the GLSL code to rotate a given vector by an angle:

vec2 rotate(vec2 v, float a) { float s = sin(a); float c = cos(a); mat2 m = mat2(c, -s, s, c); return m * v; }

Now this makes sense if you look at the equation https://en.wikipedia.org/wiki/Rotation_matrix. It's making the matrix you see below:

cos(O) | -sin(O) sin(O) | cos(O)

Or rather, it should be. It looks deceptively correct if you forget that we read in row-major order https://en.wikipedia.org/wiki/Row-_and_column-major_order. The gotcha here is that mat2 doesn't operate in row-major order but instead in column-major order. This means that it actually creates the matrix as follows:

cos(O) | sin(O) -sin(O)| cos(O)

What this does is it inverts the direction of rotation. Now this would normally be obvious except for the fact that the coordinate systems being left and right-handed have variations on how this operates already. Inverting the y axis and inverting rotational direction looks totally normal when you think of rotating things way back in trig class. This means that in a left handed system, such as OpenGl, this makes rotation work as it would in right handed systems. Basically you can't intuitively figure out why there's a problem because none of it is intuitive in the first place.

If it was rotation alone we might have noticed sooner or later but atan2 complicates the problem further. It's intuitive to assume that if a function takes an x and a y it will take them in the order x then y. If I offer two options between x and y I say them in that order. If I write down coordinates I write them down x and then y. This is normal.

That said Atan2 was designed by mathematicians so it instead takes arguments in the order y then x (due to the actual operation representation order https://en.wikipedia.org/wiki/Atan2) . If you forget this and put them in backwards you get another novel feature of rotation yet again being inverted. A double inversion makes for a very difficult to track down bug. In this case it is between one and three inversions depending on the rotation matrix, the coordinate system, and if atan2 was correctly used. Needless to say, this bug did not help with the confusion from the coordinate systems.

As a side note it's not just us that screws up the order. If I grep through our dependencies I see several cases of atan2 being used incorrectly even in library code.


What We're Doing Now

We want to try and impose some sanity on this staggering selection of variations. For 2d planes, just x and y, we want to exclusively use standard mathematical coordinates making x and y positive up and right. We wanted to use a left-handed coordinate system as it's intuitive like "that wall is 6 meters away". 0,0 is the center of everything. At this point with all the confusion consistency is the key to being able to actually work with any of these systems. Isolating the insanity to the translation between game space and render space is the best way we can actually develop despite the variations. This wraps up our take on a portion of the mess that makes up all of these systems. We hope that our take is sane enough to continue producing great unique games despite all the insanity that needs to be accounted for.

Devon's Take

NQDevon's here: Isaac has been tortured lately implementing some graphics stuff. I've been around this block a few times so I have some intuition about what's up. Isaac likes to say that I can solve problems with hammers. And sometimes, in the moment when I'm programming graphics I give up on caring about what is mathematically correct (or sane) and make the damn thing show the object I want to see where the hell I want to see it.

It's fascinating and amusing to see a relative newcomer to the world of computer graphics react to the status quo here. And personally, the whole 'stretch your hand and point, bending your middle finger' mnemonic for axis directionality in left handed vs right handed systems has never worked well for me. There's simply too many ways I can hold my hand - so I have to look it up.

And of course there's more depth to uncover. What coordinate system should you use when your character is on a vehicle? How do we feel about general relativity and space time?

Most troubling of all for me, is that the right hand rule implies that positive rotation is counterclockwise, which is...counter...to the most common mnemonic we have for rotation. "Watch your Noon" doesn't sound nearly as good as "Watch your Six". While I knew this fact, it seems that I forgot it for the close to 2 years I spent working on VECTORLORD. Oops.


05-23-2020 -- VECTORLORD 1.1 Economy Update
05-01-2020 -- Why All Coordinate Systems Suck
04-22-2020 -- VECTORLORD OST
04-05-2020 -- AntHive
03-10-2020 -- VECTORLORD Visual Style and Supporting Photosensitive Players
03-08-2020 -- NerveQuake Introductions
03-06-2020 -- Transmission
NerveQuake Software Inc.