Vector2 Rotate(Vector2 point, double angle)
{
double rad = angle * (Math.PI / 180); //First, convert from degrees to radians.
double sin = Math.Sin(rad); //Then, get the sine and
double cos = Math.Cos(rad); //cosine of this angle.
return new Vector2 //Finally, calculate the rotated point.
{
X = point.X * cos - point.Y * sin,
Y = point.X * sin + point.Y * cos
};
}
19 September 2017
Math - rotate a 2D point
30 August 2017
Math – Calculate the distance between two points in 3D space
public float Distance(Vector3 a, Vector3 b) => (float)Math.Sqrt(Math.Pow(a.X - b.X, 2) + Math.Pow(a.Y - b.Y, 2) + Math.Pow(a.Z - b.Z, 2));
09 April 2017
Math – Calculate a transformation matrix
Suppose you have a translation, a rotation and a scaling matrix; how do you combine them into one transformation matrix — also model matrix — used to render some object? The order in which they are combined is also important. The following method will first rotate and scale the object, then move it. Rotation is the most complex transformation, and is done with quaternions.
//"Position", "Scale" and "Rotation" are all Vector3's.
Quaternion rot = null;
if (Rotation != null)
{
var rotX = new Quaternion(Rotation.X, 1, 0, 0);
var rotY = new Quaternion(Rotation.Y, 0, 1, 0);
var rotZ = new Quaternion(Rotation.Z, 0, 0, 1);
rot = rotX * rotY * rotZ;
}
var model = new Matrix();
model.Rotate(rot);
model.Scale(Scale);
model.Translate(Position);
The Matrix class was also mentioned in the post about quaternions. You can find the Matrix.Rotate method there as well. Compared to that, translation and scaling are pretty simple:
//An overload of this method takes a Vector3, and checks it for NULL.
public void Translate(float x, float y, float z)
{
M[0, 3] += x;
M[1, 3] += y;
M[2, 3] += z;
}
//Also has an overload that takes a Vector3.
public void Scale(float x, float y, float z)
{
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
M[i, j] *= x;
M[i, j] *= y;
M[i, j] *= z;
}
}
}
11 March 2017
Windows API - Retrieve the size of the screen
//Retrieve the window size.
IntPtr windowHandle = GetDesktopWindow();
Rect windowSize;
GetWindowRect(windowHandle, out windowSize);
//DLL import methods.
[DllImport("user32", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr windowHandle, out Rect rectangle);
//Rect(angle) struct.
public struct Rect
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
19 February 2017
Math - Calculate a look-at matrix for OpenGL
The final piece of the puzzle to get my basic OpenGL scene up and running was the camera or look-at matrix. It is calculated from a position vector and a direction vector; the look-at vector.
The position and direction vectors
Here's how the position and direction vectors are calculated. The input comes from the mouse and keyboard. Note that I do use some extension methods to make life easier. Delta time is a time-based value to get the same speed on slow and fast computers.
rotationHorizontal += window.MouseMovement.X * 0.1f;
rotationHorizontal = rotationHorizontal.Circular(0, 359);
rotationVertical += window.MouseMovement.Y * 0.1f;
rotationVertical = rotationVertical.Constrain(-89, 89);
float h = rotationHorizontal.ToRadians();
float v = rotationVertical.ToRadians();
float hs = h.Sin();
float hc = h.Cos();
float vs = v.Sin();
float vc = v.Cos();
float speed = 0.005f * deltaTime;
if (window.KeyDown(Key.Up))
{
position.X -= hc * speed;
position.Z -= hs * speed;
}
if (window.KeyDown(Key.Down))
{
position.X += hc * speed;
position.Z += hs * speed;
}
if (window.KeyDown(Key.Left))
{
position.X -= hs * speed;
position.Z += hc * speed;
}
if (window.KeyDown(Key.Right))
{
position.X += hs * speed;
position.Z -= hc * speed;
}
var direction = new Vector(vc * hc, vs, vc * hs);
var camera = new Matrix(position, direction);
The look-at matrix
This one is again implemented as a constructor of the Matrix class. It uses some basic vector operations; the implementations of which can easily be found online.
public Matrix(Vector position, Vector direction)
{
var up = new Vector(0, 1, 0);
var zAxis = direction.Normalized();
var xAxis = Vector.CrossProduct(up.Normalized(), zAxis).Normalized();
var yAxis = Vector.CrossProduct(zAxis, xAxis);
var translation = new Matrix();
translation.Translate(position.Negated());
var rotation = new Matrix();
rotation.M[0, 0] = xAxis.X;
rotation.M[0, 1] = xAxis.Y;
rotation.M[0, 2] = xAxis.Z;
rotation.M[1, 0] = yAxis.X;
rotation.M[1, 1] = yAxis.Y;
rotation.M[1, 2] = yAxis.Z;
rotation.M[2, 0] = zAxis.X;
rotation.M[2, 1] = zAxis.Y;
rotation.M[2, 2] = zAxis.Z;
var lookAt = rotation * translation;
SetMatrix(lookAt.M);
}
See also
Simplified method to create a look-at matrixCalculate a perspective projection matrix for OpenGL
09 February 2017
Math - Calculate a perspective projection matrix for OpenGL
Another method that took a while to find was the one that calculates a perspective projection matrix from a given field of view, aspect ratio and near and far plane to work with OpenGL. This constructor for a Matrix object does it though:
public Matrix(float fov, float aspect, float near, float far)
{
Values = new float[4, 4];
float scale = (float)Math.Tan(fov.ToRadians() * 0.5f) * near;
float right = aspect * scale;
float left = -right;
float top = scale;
float bottom = -top;
Values[0, 0] = 2f * near / (right - left);
Values[1, 1] = 2f * near / (top - bottom);
Values[0, 2] = (right + left) / (right - left);
Values[1, 2] = (top + bottom) / (top - bottom);
Values[2, 2] = -(far + near) / (far - near);
Values[3, 2] = -1f;
Values[2, 3] = -2f * far * near / (far - near);
}
Note that this class uses row-major order, while OpenGL uses column-major order. When I pass my matrices to the glUniformMatrix4fv method, I set it's transpose parameter to True.
See also
Calculate a look-at matrix for OpenGL04 February 2017
Math – Using quaternions to create a rotation matrix
The best way I found to create a rotation matrix with all three axes is with quaternions. The following code is C#:
Initialize
The Quaternion class has four properties: W, X, Y and Z. "W" is the angle, and the other represent the axis. I will only use unit quaternions, meaning the rotation is around one of the three axes only. You initialize an identity quaternion by setting them to (1, 0, 0, 0). I create one for each rotation like so:
var rotx = new Quaternion(Rotation.X, 1, 0, 0); var roty = new Quaternion(Rotation.Y, 0, 1, 0); var rotz = new Quaternion(Rotation.Z, 0, 0, 1);
The quaternion's constructor initializes those arguments as:
double r = (angle * (Math.PI / 180)); W = (float)Math.Cos(r / 2); double s = Math.Sin(r / 2); X = (float)(x * s); Y = (float)(y * s); Z = (float)(z * s);
Multiply
Next, we combine them all into one quaternion:
var rot = rotx * roty * rotz;
The method that multiplies two quaternions is defined as follows:
public static Quaternion operator *(Quaternion a, Quaternion b)
{
var c = new Quaternion();
c.W = a.W * b.W - a.X * b.X - a.Y * b.Y - a.Z * b.Z;
c.X = a.W * b.X + a.X * b.W + a.Y * b.Z - a.Z * b.Y;
c.Y = a.W * b.Y - a.X * b.Z + a.Y * b.W + a.Z * b.X;
c.Z = a.W * b.Z + a.X * b.Y - a.Y * b.X + a.Z * b.W;
return c;
}
Rotate
To get the matrix I needed to pass to OpenGL, I created another class called "Matrix", which is a 4x4 matrix containing a 16 element array. Then a method takes the quaternion and adds the rotation to this matrix. It took a while to find a way to do this that worked with OpenGL, but I finally found it in the .net framework.
var matrix = new Matrix();
matrix.Rotate(rot); //"rot" being the quaternion from before.
public void Rotate(Quaternion rotation)
{
float x2 = rotation.X + rotation.X;
float y2 = rotation.Y + rotation.Y;
float z2 = rotation.Z + rotation.Z;
float xx = rotation.X * x2;
float xy = rotation.X * y2;
float xz = rotation.X * z2;
float yy = rotation.Y * y2;
float yz = rotation.Y * z2;
float zz = rotation.Z * z2;
float wx = rotation.W * x2;
float wy = rotation.W * y2;
float wz = rotation.W * z2;
Values[0, 0] = 1.0f - (yy + zz);
Values[0, 1] = xy + wz;
Values[0, 2] = xz - wy;
Values[1, 0] = xy - wz;
Values[1, 1] = 1.0f - (xx + zz);
Values[1, 2] = yz + wx;
Values[2, 0] = xz + wy;
Values[2, 1] = yz - wx;
Values[2, 2] = 1.0f - (xx + yy);
}