Mathematics, the bedrock of game coding

Equation

I used to be pretty poor at math. It's not that I couldn't understand it, it's just that I'm a very visual kind of guy, and visualising the mechanics behind mathematics can sometimes be difficult or even impossible at times. I had to try very hard to understand exactly what was going on behind all of those numbers and symbols, and indeed still do to a smaller extent.

Mathematics is core to the whole discipline of 3D games programming, so when entering the games industry I really did have to learn how to level up, and fast. It was a sheer vertical rock face of a learning curve, but eventually I managed to beat it. Only by dogged persistence, and not with natural talent, sadly.

It struck me one day just how many mathematical algorithms get replicated throughout many different source files. All of that discrete math, retyped time and time again, with each repetition introducing more and more risk of error. I already had a math library at the time, for things like vector and matrix operations, so I then decided to expand upon it quite dramatically, to cover a lot of the more general and even esoteric math that we use in game programming. Over the last few years this math library has become worth its weight in gold. Containing a wealth of experience, I wouldn't be without it when tackling any project with a mathematical element.

I'm going to share this library [here] as many programmers I feel would find this a real benefit and save them a lot of hard work. It's been designed with flexibility and understanding in mind, not raw performance, so bear that in mind when using it. That said, it's very easy to pick out functions requiring optimisation in an analysis run and to address those for yourself. Normally a bit of inline assembly or use of intrinsics is all you need and better you do that related to your particular target platform. These days though it's just not required as compilers do such an excellent job of optimisation.

The libmaths maths library is for general maths use, with a specific slant toward 3D geometry and ray / line casting. In every-day use you will want to include "libmaths.h" followed by any other particular class headers that you will be requiring.

This library is built upon the IEEE 754 single-precision floating point standard, via the F32 type. Double precision floating point types are just not used as they offer precision that just isn't needed in all but the rarest of occasions and they are still very slow (in comparison to single-precision) on contemporary hardware.

Many functions that you would use in <math.h> are overridden in the library for reasons of speed. Function names have been changed to avoid conflict however, so you need to be wary of that. For instance, where you would normally use sin or sinf, now use Sin as this will more likely than not be faster than the standard equivalents. Other functions like this include; Cos, CosSin, SinCos, Tan, Asin, Acos, Atan, Atan2, Exp, Sqrt, InvSqrt, Abs, Floor, Ceil, Round, Fmod, Sgn and Zsgn. There are further more esoteric functions, so check out libmaths.h for more details.

In addition, there are also some fixed point data types that you can use for performing integer arithmetic with fractional parts. This is a templated class that can be used in a multitude of ways, but the standard 8.8, 4.12, 1.15 and 16.16 formats are supported out-of-the-box with the Fixed8p8, Fixed4p12, Fixed1p15 and Fixed16p16 classes. Use them as you would a normal integer or float, as all the standard operators are provided in these classes.

Random numbers are supported with the Random (very fast) and MTRandom (slower, but more equally random over a given range) classes. The MTRandom class is built upon the Mersenne Twister algorithm and should be used if you want truly random numbers with a high level of unpredictability, for everything else just use the Random class.

The library contains various classes that work in both 2D and 3D. Before we can talk about those however, we need to take a look at coordinate systems.

There is support for 2D polar coordinates and 3D spherical coordinates in addition to the normal vector coordinates.

The Cartesian coordinate system for 2D assumes that:

+X = Horizontal
+Y = Vertical

This is known as right-handed orientation.

The cartesian coordinate system for 3D assumes that:

+X = Right
+Y = Up
+Z = Backwards

3DS Max uses this 3D coordinate system and most of the maths library is also designed around that basis. Like 2D, this is also known as right-handed orientation. OpenGL also uses the right-handed orientation.

Coordinate systems

Direct3D however, uses the left-handed orientation, which assumes that:

+X = Right
+Y = Up
+Z = Forwards

Check the following links for explanations of 3D orientations and coordinate systems: [Cartesian coordinate systems] and [Left / right coordinate systems]

For general work with coordinates of varying dimensions, there are a number of vector classes, including; Vector2, Vector3, Vector3Lite and Vector4. For most 2D work you will want to use Vector2. For most 3D work you will want to use Vector3 or Vector3Lite - the only difference between these classes is the presence of a 4th component in Vector3. This 4th component is not used in any internal calculations, but is instead used to pad the size of the vector to 16 bytes to allow for easy SIMD implementation at a later date. Vector3Lite has only 3 components as you would expect. Try to use Vector3 where you can to allow for the potential of future performance improvements. Vector4 is relatively unused but can be very useful for 3D calculations using homogenous coordinates with 4x4 matrices.

To accompany these various vector classes, there are also a number of matrix classes to allow you to transform them. Matrix33 is a 3x3 matrix used to transform 2D vectors with 2D rotational (and scaling) and translational components as part of the matrix. Matrix22Sub33 is a 2x2 matrix that typically holds just 2D rotational (and scaling) part of the transformation. Similarly, Matrix44 is a 4x4 matrix used to transform 3D and 4D vectors with 3D rotational (and scaling) and translational components as part of the matrix. Matrix33Sub44 is a 3x3 matrix that typically holds just 3D rotational (and scaling) part of the transformation.

For 2D work you would use Matrix33 and Matrix22Sub33. For 3D work, you would use the equivalent Matrix44 and Matrix33Sub44 classes.

Quaternions are supported alongside the standard matrices, in the form of Quaternion, for rotating 3D vectors. You would use a quaternion for reasons of space (they take up 16 bytes as opposed to the 36 bytes in a 3x3 matrix), but also because you can combine several rotations more easily using quaternions than matrices. One last advantage is that interpolating between quaternions can be done very accurately, whereas attempting to do the same using matrices can result in problems of spherical correctness (they just don't interpolate smoothly) and can even fail if the angles interpolated are almost or exactly opposite. Indeed, because of these problems, interpolating 3D rotations in the matrix classes will resort to building equivalent quaternions and interpolating those instead before returning a matrix representing the interpolated quaternion. This conversion back and forth can be a little slow, but it is accurate and guaranteed to work.

So that covers the basics of vectors and transformations. Lots of vectors, lots of matrices and even quaternions to boot.

Planar components, in the form of planes and 3D triangles are supported via the Plane and Triangle3 classes. As part of this group of components, though not strictly planar, is the Triangle2 class, which is of course a 2D triangle.

Linear components, like planar components, also come in 2D and 3D flavours. These consist of rays and line segments, in the form of the Ray2, Ray3, LineSegment2 and LineSegment3 classes. Rays have a single point of origin and an infinitely long direction vector. Line segments are very similar to rays, but instead of having an infinite direction vector, they instead of a direction with finite length. Rays and line segments are used extensively in intersection detection, being able to detect and return intersections with a number of different types of primitives, such as triangles, planes, spheres etc.

Curved components basically consists of a variety of spline classes. The splines are based on the Bezier spline, B spline and Catmull Rom spline. They come in 2D, 3D and even 4D flavours, based on a common templated class for each variety of spline. For instance, The 3D Bezier Spline is called BezierSplineVector3 whereas the 2D Catmull Rom spline is called CatmullRomSplineVector2. Each of these spline classes is capable of interpolating both a position and a normal along a spline.

Areas and volumes are the last critical component of the maths library. There are various geometry primitives for 2D rectangles (Rectangle) and circles (Circle), and 3D boxes (Box), spheres (Sphere), capsules (Capsule), cylinders (Cylinder) and viewing frustums (Frustum). Many of these primitives are able to detect intersections with other primitives, but more importantly, you are able to detect intersections with rays and line segments using them. You can also do simpler things with them, such as detecting if a point resides inside or outside of the area or volume, or even determining how far a point is from an area or volume. Of all the areas of the maths library, this is probably the least developed and will be fleshed out in the future.

So by all means go ahead and dip a toe in, just load up the Visual Studio project file and take a look at the header files of interest. Either use it as a black box or perhaps even rip out any useful code for inclusion within your own libraries. I'll continue to update it over time and will update the download on this site at suitable points. Until then, enjoy.

[Posted 10/18/2019]

This comment will be permanently deleted. Are you sure?

This comment will be accepted and published openly. Are you sure?

Submitting comment, please wait ...

Your comment could not be submitted to the server at this time. Please try again later.

Applying for subscription, please wait ...

Your subscription could not be processed on the server at this time. Please try again later.