I've been digging straight down into the math involved in calculating the angle of a slope (for the purposes of determining whether Rynn will walk or slide on any given terrain triangle), and I have to say this is most bizarre.
But first, let's take a loot at the math involved before jumping to any conclusions.
Also, a note about the axis labelling convention used: the math involved assumes an XZY axis order (where Y is the "3D vertical", or "up/down" axis - the one along which the gravity is acting), the same as used by Drakan.
No idea why they didn't use the much more obvious (and sane) XYZ axis order, though.
Let's start off with a triangle. This one, to be specific:
Presumably, there are many ways to tackle this, mathematically speaking. But arguably the simplest way is to use the surface normal vector for this purpose: the Y component of the (normalized) normal vector is simply the cosine of the vector's angle from vertical.
Taking the arccos() of the Y component gives us the angle of the slope from horizontal (in radians).
So let's take a look at what is involved in calculating the normal vector:
Well, that's not good at all!
But wait! We don't need to calculate the normal vector for an arbitrary
triangle in 3D space.
For this purpose, the XZ coordinates of the vertices always lie on a regular grid (1 lu = 2048 wu apart).
Also, we don't care about the vector's XZ orientation either, only its angle from vertical (the Y axis, in this case).
So let's substitute the XZ coordinates of the vertices with fixed values, and see if that helps:
Note that obviously I've assumed the wrong order of the vertices, since the normal vector now points the wrong way. But this is easily fixed by just inverting the sign on the Y component - and we don't care about the XZ components anyway.
Also just for fun, let's see how it looks like when rescaled to wu instead of lu.
So let's take a look at the final form of this equation:
That's very much tractable now
Note that I've gotten rid of the arccos(), the square root and
the constants - they have no bearing on the nature of the result, only acting as a "scale factor" which can be factored out.
This is possible, because we don't care about the value of the angle in degrees (or radians) - we just need a value that is a monotonic function of the normal vector's angle from vertical, so that this value can be compared against a fixed constant.
But actually, we can do better still... let's go back to the non-simplified form from before, and carve it down to the essential part:
(y1 - y2)² + (y1 - y3)²
This is much better, since it involves only 3 additions/subtractions and 2 squarings, instead of 4 additions, 2 multiplications and 3 squarings.
There's also another advantage to doing it this way... using heights in wu (which are integers) allows foregoing the use of floating point numbers entirely: the result will always be an integer if given integer arguments!
Really, it doesn't get any simpler than that
And now, we get to the truly bizarre part...
Since we now know how to calculate the angle of the slope, let's see if we can determine the basis for the walk/slide cutoff.
We could make an a priori
assumption that it was done by calculating (y1 - y2)² + (y1 - y3)² with wu heights as arguments (for performance reasons - fewest operations, and no floats), but that's probably giving the devs too much credit.
Instead, let's explore the various possibilities in turn.
First up, what's the limit of a walkable triangle?
Without beating around the bush, I'll just give the answer straight away.
Assuming a triangle with the vertices labelled as shown above (and heights in wu), the steepest possible walkable triangle has the values:
- y1 = 956
- y2 = 1896
- y3 = 0
Making it any steeper (eg. y1 = 958, or y2 = 1898) makes it unwalkable.
So this corresponds to an angle from horizontal of 33.21074 degrees (0.5796368 radians) for the steepest walkable triangle, and 33.21127 degrees (0.579646 radians) for the least steep unwalkable one.
Plainly speaking, these values are nuts. I can't even begin to imagine what the underlying value was that was placed in the source code. It's not related to any rational fraction involving pi - the first close match is 12 * pi / 65, but even then it falls just outside the range of interest.
If we consider the (y1 - y2)² + (y1 - y3)² form, then the cutoff lies somewhere between 1797536 and 1797608.
That's not even correspond to any sane rational fraction based on the powers of 2.
No idea how they came up with these values, it seems totally arbitrary.
I find it hard to believe that someone on the dev team just randomly decided these numbers, yet I can find no rational
explanation for them