Math - simple-entertainment/simplicity GitHub Wiki
Yay for maths! Yay for maths that's already done for us! We use maths lots in games so lets try to make it easy :)
Because we don't like magic numbers. The only constant defined so far is MathConstants::PI
.
To simplify the use of random numbers we've provided a few functions:
bool randomBool = getRandomBool(); // True or false? Nobody knows!
bool randomBoolWithChance = getRandomBool(0.2f); // 20% chance it will be true.
float randomFloat = getRandomFloat(0.0f, 1.0f); // A random floating point value in the range [0,1]
int randomInt = getRandomInt(0, 10); // A random integer value in the range [0,10]
Gosh darn it my floating point values are sooo close but not quite equal, what ever will I do?
float a = 10.00000001f;
float b = 10.000000011f;
bool closeEnough = near(a, b); // True!
And now I've got vectors that have the same problem :(
Vector3 a(10.00000001f, 10.00000001f, 10.00000001f);
Vector3 b(10.000000011f, 10.000000011f, 10.000000011f);
bool closeEnough = near(a, b); // True!
Vectors are implemented as templates so you can create a vector of any size with any data type.
Vector<float, 3> vector; // A bit annoying to have to type '<float, 3>' all the time though.
The most common vectors for use in games are 2, 3 and 4 elements long and contain floating point values so we've provided some handy typedefs. Vector2
, Vector3
and Vector4
should be all you need 95% of the time.
Vectors can be constructed in a number of ways:
Vector3 a; // Note that the default constructor results in the contents of the vector being undefined.
Vector2 b(0.0f, 0.0f);
Vector3 c(0.0f, 0.0f, 0.0f);
Vector4 d(0.0f, 0.0f, 0.0f, 1.0f);
Vector2 e(0.0f, 0.0f, 0.0f, 1.0f); // Oops! Since it is a template this is possible! Don't do it!
float data[4] = {0.0f, 0.0f, 0.0f, 1.0f};
Vector4 f(data);
std::array<float, 4> dataArray;
// Fill dataArray...
Vector4 g(dataArray);
Vector4 h(g); // Or: Vector h = g;
Vector4 i(c, 1.0f); // For converting Vector3 to Vector4.
Lots of the operators have been overloaded and should be pretty straightforward to use.
Vector3 a(1.0f, 1.0f, 1.0f);
Vector3 b;
b = a; // Copy assignment.
if (a == b) // Equality.
{
// ...
}
else if (a != b) // Inequality.
{
// ...
}
// Arithmetic - this can be done for /, + and - as well.
a *= 5.0f;
a *= b;
Vector3 c = a * 5.0f;
Vector3 c = a * b;
// Element access.
float a0 = a[0];
a[0] = 5.0f;
// Textual representation.
std::ostream stream = // Some stream...
stream << a;
Functions are also provided for more readable element access:
Vector4 colorVector(0.0f, 0.0f, 0.0f, 1.0f);
float red = colorVector.R();
float green = colorVector.G();
float blue = colorVector.B();
float alpha = colorVector.A();
colorVector.R() = 5.0f;
Vector4 positionVector(0.0f, 0.0f, 0.0f, 1.0f);
float x = positionVector.X();
float y = positionVector.Y();
float z = positionVector.Z();
float w = positionVector.W();
positionVector.X() = 5.0f;
When interfacing with 3rd party libraries, particularly C libraries, it is nice to have access to the raw data contained in the vector:
Vector3 vector(0.0f, 0.0f, 0.0f);
float* rawData = vector.getData();
// Manipulate the raw data...
vector.setData(rawData);
There are also some general purpose functions that will hopefully look familiar to someone who has used vectors before:
Vector3 a(1.0f, 0.0f, 0.0f);
Vector3 b(0.0f, 1.0f, 0.0f);
float dot = dotProduct(a, b);
float magnitude = a.getMagnitude();
float magnitudeSquared = a.getMagnitudeSquared(); // Faster than getMagnitude().
a.negate();
a.normalize();
Vector2 a(1.0f, 0.0f);
rotate(a, MathConstants::PI * 0.5f); // Rotate 90 degrees.
Vector3 b(1.0f, 0.0f, 0.0f);
Vector3 c(0.0f, 1.0f, 0.0f);
Vector3 cross = crossProduct(b, c);
float angle = getAngleBetween(b, c);
float angle2 = getAngleBetweenNormalized(b, c); // Assumes a and b are unit length vectors, faster than getAngleBetween(b, c)
Vector3 projection = getProjection(b, c); // Projects a onto b.
float scalarProjection = getScalarProjection(b, c);
float proximity = getProximity(b, c);
Vector4 d(1.0f, 1.0f, 1.0f, 2.0f) // Not homogenized!
homogenize(d);
Matrices are column-major, they are implemented as templates so you can create a matrix of any size with any data type.
Matrix<float, 4, 4> vector; // A bit annoying to have to type '<float, 4, 4>' all the time though.
The most common matrices for use in games are 3 by 3 or 4 by 4 and contain floating point values so we've provided some handy typedefs. Matrix33
and Matrix44
should be all you need 95% of the time.
Matrices can be constructed in a number of ways:
Matrix33 a; // Note that the default constructor results in the contents of the matrix being undefined.
float data[9] = {...};
Matrix33 b(data);
std::array<float, 9> dataArray;
// Fill dataArray...
Matrix33 c(dataArray);
Matrix33 d(c); // Or: Matrix33 d = c;
Some of the operators have been overloaded and should be pretty straightforward to use.
Matrix33 a(/* some data... */);
Matrix33 b;
b = a; // Copy assignment.
if (a == b) // Equality.
{
// ...
}
else if (a != b) // Inequality.
{
// ...
}
// Arithmetic.
a *= b;
Matrix33 c = a * b;
// Arithmetic with vectors.
Vector3 c = a * Vector3(1.0f, 2.0f, 3.0f);
Vector3 d = Vector3(1.0f, 2.0f, 3.0f) * a;
// Element access.
float a0 = a[0];
a[0] = 5.0f;
// Textual representation.
std::ostream stream = // Some stream...
stream << a;
When interfacing with 3rd party libraries, particularly C libraries, it is nice to have access to the raw data contained in the matrix:
Matrix33 matrix(/* some data... */);
float* rawData = matrix.getData();
// Manipulate the raw data...
matrix.setData(rawData);
There are also some general purpose functions that will hopefully look familiar to someone who has used matrices before:
Matrix33 matrix(/* some data... */);
float determinant = matrix.getDeterminant();
matrix.invert();
matrix.setIdentity();
matrix.transpose();
Matrix33 rotation(/* some data... */);
Matrix44 transformation(/* some data... */);
// Component access.
Vector3 out3 = getOut3(transformation);
Vector4 out4 = getOut4(transformation);
Vector3 position3 = getPosition3(transformation);
Vector4 position4 = getPosition4(transformation);
Vector3 right3 = getRight3(transformation);
Vector4 right4 = getRight4(transformation);
Vector3 up3 = getUp3(transformation);
Vector4 up4 = getUp4(transformation);
// Manipulation.
rotate(rotation, MathConstants::PI * 0.5f, Vector3(0.0f, 1.0f, 0.0f)) // Rotate 90 degrees about the y axis.
rotate(transformation, MathConstants::PI * 0.5f, Vector3(0.0f, 1.0f, 0.0f)) // The axis can be a Vector3 or a Vector4 here.
scale(transformation, Vector3(2.0f, 2.0f, 2.0f)); // Double the scale on all axis.
setPosition(transformation, Vector3(1.0f, 1.0f, 1.0f)) // The position can be a Vector3 or a Vector4 here.
setScale(transformation, Vector3(2.0f, 2.0f, 2.0f)); // 2x scale on all axis.
translate(transformation, Vector3(1.0f, 1.0f, 1.0f)) // The translation can be a Vector3 or a Vector4 here.
Use the Distance::distanceBetween(...)
functions to find the distance between two models.
There are a few different types of functions in the Intersection
namespace.
This function determines if one model contains another model:
bool isContained = Intersection::contains(a, b, relative);
// Where:
// a is the 'container' model.
// b is the 'containee' model.
// relative is the position (and possibly orientation) of b relative to a.
This function has various overloads. Some take different types for relative
depending on whether orientation is supported. Some do not take the relative
argument at all and assume that both models are described in the same space. An overload that takes two Model
s is also provided for situations where the types of the models are not known.
This function retrieves the intersection of two models:
Intersection::getIntersection(...);
The different overloads of this function are quite unique so I recommend reading the API documentation for them.
This function determines if one model intersects another model:
bool areIntersecting = Intersection::intersect(a, b, relative);
// Where:
// a is the first model.
// b is the second model.
// relative is the position (and possibly orientation) of b relative to a.
This function has various overloads. Some take different types for relative
depending on whether orientation is supported. Some do not take the relative
argument at all and assume that both models are described in the same space. An overload that takes two Model
s is also provided for situations where the types of the models are not known. There are also a few special cases which differ to the description above, see the API documentation for more information.
This library is still under construction but it currently provides a couple of functions for bezier interpolation. See the API documentation for the Interpolation
namespace.