TRIGONOMETRY:
Trigonometry is annoying stuff, and it's sloooow. In general, vectors are way, way faster, and you should use them instead. So skip this section and go on to the next section. Yes Vectors are that cool. Do not use trig.
Still, sometimes trig's useful, so I'll include a section on it. You don't need to know anything about trig to use these, though. What you do need to know, like with all these commands, is what you know, and what you want to find out.
So your variables are:
Angle - for example, the angle to which a gun has been turned.
Distance - the straight-line distance between the gun and its target. What mathemagicians call the "hypotenuse", because they enjoy mispronouncing the names large African animals.
X and
Y - these are the grid distances that you want to convert to or from. Usually in world units, but you might have, say, X as how far you are travelling forward and Y as how far you are strafing. Doesn't matter, so long as the two are are at right angles to eachother. If you are comparing two points, the x and y positions are measured from one point relative to the other, not relative to the world origin.
Adjacent - this will be either your x or y value. Which one it is, depends on where your angle is measured from. If the angle is measured from the X axis, then it's X, otherwise it's Y. If that makes no sense, draw the triangle on paper, with the gun, the target, and lines for the X and Y and distance. Then mark the corner that you know the angle for. If the X part of the triangle is touching that mark, then the adjacent is your X value. Otherwise it's your Y value.
Opposite - This will be the other one of X or Y.
If you know at least some of these values, you can work out the others. For the 2D Stuff I'm assuming X# & Y#, but could be any two coords.
2D Distance between two points:
Distance# = sqrt((X1#-X2) * (X1#-X2) + (Y1#-Y2) * (Y1#-Y2))
DistanceSquared# = ((X1#-X2) * (X1#-X2) + (Y1#-Y2) * (Y1#-Y2))
2D Distance from origin to one point:
Distance# = sqrt(X#*X# + Y#*Y#)
DistanceSquared# = (X#*X# + Y#*Y#)
3D Distance between two points:
Distance# = sqrt((X1#-X2) * (X1#-X2) + (Y1#-Y2) * (Y1#-Y2) + (Z1#-Z2) * (Z1#-Z2))
DistanceSquared# = ((X1#-X2) * (X1#-X2) + (Y1#-Y2) * (Y1#-Y2) + (Z1#-Z2) * (Z1#-Z2))
3D Distance from origin to one point:
Distance# = sqrt(X#*X# + Y#*Y# + Z#*Z#)
DistanceSquared# = (X#*X# + Y#*Y# + Z#*Z#)
2D Angles and stuff:
Distance# = Opposite# / Sin(Angle#)
Distance# = Adjacent# / Cos(Angle#)
Angle# = ATanFull(X#, Y#)
Angle# = ASin(Opposite# / Distance#)
Angle# = ACos(Adjacent# / Distance#)
Angle# = ATan(Opposite# / Adjacent#)
Adjacent# = Distance# * Cos(Angle#)
Adjacent# = Opposite# / Tan(Angle#)
Opposite# = Distance# * Sin(Angle#)
Opposite# = Adjacent# * Tan(Angle#)
Rotate a point about the origin (with Distance as the radius):
NewX# = cos(Angle#) * Distance# - sin(Angle#) * OldY#
NewY# = sin(Angle#) * Distance# + cos(Angle#) * OldY#
If you want to rotate about some other point, you can subtract the Y position of that point from OldY# beforehand, and add the X and Y positions of the point back to NewX# and NewY# respectively once you've generated them. Or you can just use these:
Rotating point A around point B
A.NewX# = (A.X#-B.X#) * cos(Angle#) - (A.Y#-B.Y#) * sin(Angle#) + B.X#
A.NewY# = (A.Y#-B.Y#) * cos(Angle#) + (A.X#-B.X#) * sin(Angle#) + B.Y#
3D Angles and stuff:
If you're going to mess with angles and stuff in 3D, there are two ways you can do it.
The first way is to do the trig stuff twice, once for, say, X and Y, and then again for Y and Z. That's twice as slow as doing it for 2D, and that was more than slow enough!
The second way is to use
vectors.
VECTORS:
What are vectors? Basically, vectors are just sets of coordinates, like you're used to using already: (x,y) or (x,y,z). These are Vector3s (X, Y, and Z) and Vector2s (just X and Y), too.
Vector4s are just like Vector3s, but have an extra flag "w", to say whether they are describing a point in space (flag = 0) or a movement or direction (flag=1). So they store (x,y,z,w).
Part of the appeal of vectors is that you can have multiple "coordinate systems" - like, say, the world coordinates, the player coordinates, the camera coordinates, the screen coordinates... and easily convert and rotate between them all.
The commands
You don't need to understand these yet, I'm just listing them here so you can get an idea of what's available. Skim on past.
Creation
ThrowAwayValue = MAKE VECTOR4(VectorID)
ThrowAwayValue = DELETE VECTOR4(VectorID)
SET VECTOR4 VectorResult, X#, Y#, Z#, W# - Set a Vector4 to initial values.
COPY VECTOR4 VectorResult, VectorSource - Copies one into another
Maths
ADD VECTOR4 VectorResult, VectorA, VectorB - Adds two together
SUBTRACT VECTOR4 VectorResult, VectorA, VectorB - Adds two together
DIVIDE VECTOR4 VectorResult, Value# - Divides everything in a vector by a single number.
MULTIPLY VECTOR4 VectorResult, Value# - Multiplies everything in a vector by a single number (not by another vector as the documentation claims!)
SCALE VECTOR4 VectorResult, VectorSource, Value# - Appears to be identical to the above multiply, but may be more "correct" for Vector4s, since it probably won't multiply the 4th term.
Querying
Boolean = IS EQUAL VECTOR4(Vector4A, Vector4B) - 1 if both are identical.
Length# = LENGTH VECTOR4(Vector4ID) - Get the length of the Vector4
LengthSquared# = SQUARED LENGTH VECTOR4(Vector) - Get the squared length of the Vector4, which is faster and often enough for eg, spherical collision.
W# = W VECTOR4(Vector4ID) - Return that semi-meaningless fourth number.
X# = X VECTOR4(Vector4ID) - Return the X size of the Vector.
Y# = Y VECTOR4(Vector4ID) - Return the Y size of the Vector.
Z# = Z VECTOR4(Vector4ID) - Return the Z size of the Vector.
Misc stuff
LINEAR INTERPOLATE VECTOR4 VectorResult, VectorA, VectorB, SValue - Interpolate between 2 Vector4s.
NORMALIZE VECTOR4 VectorResult, VectorSource - Turn a movement vector into a "unit" direction vector.
Vector3-only stuff:
SET VECTOR3 TO CAMERA POSITION
SET VECTOR3 TO CAMERA ROTATION
SET VECTOR3 TO LIGHT POSITION
SET VECTOR3 TO LIGHT ROTATION
SET VECTOR3 TO MATRIX POSITION
SET VECTOR3 TO PARTICLES POSITION
SET VECTOR3 TO PARTICLES ROTATION
TRANSFORM COORDS VECTOR3
TRANSFORM NORMALS VECTOR3
CROSS PRODUCT VECTOR3
DOT PRODUCT VECTOR3
Stuff I don't understand, so is probably rather cool:
BCC VECTOR4 Vector4Result, VectorA, VectorB, VectorC, FValue, GBValue - "BaryCentricCoordinates vector". No idea.
CATMULLROM VECTOR4 Vector4Result, VectorA, VectorB, VectorC, VectorD, Value - "catmull rom interpolation". Something to do with Splines?
HERMITE VECTOR4 Vector4Result, VectorA, VectorB, VectorC, VectorD, SValue - "hermite spline interpolation."
MAXIMIZE VECTOR4 VectorResult, VectorA, VectorB - No idea, possibly find largest
MINIMIZE VECTOR4 VectorResult, VectorA, VectorB - No idea, possibly find smallest
Position vectors:
If you've ever stored the X, Y, and Z position of something, a point in space, then you've used a position vector. A proper vector is just like a user-defined type that stores those X, Y and Z values in a single variable. These are really easy.
In a Vector4, the last element of a position vector is 0, but otherwise, it's just the same.
Convert X, Y and Z location to position vector:
SET VECTOR4 ResultVector, x#, y#, z#, 0
Convert back:
x# = X Vector4 MyVector
y# = Y Vector4 MyVector
z# = Z Vector4 MyVector
Movement vectors:
If you've ever moved something by an X, Y, and Z amount, you've used a movement vector. Movement vectors (aka "displacement vectors", or "distance vectors") store how far in each direction that you are going.
In a Vector4, the last element of a position vector is 1, but otherwise, it's just the same.
Convert X, Y and Z displacement to position vector:
SET VECTOR4 ResultVector, x#, y#, z#, 1
Convert back:
x# = X Vector4 MyVector
y# = Y Vector4 MyVector
z# = Z Vector4 MyVector
Convert an angle and a distance to a 2D movement vector:
This uses trig functions. Surely there's a better way? But if so, I don't know it.
SET VECTOR2 Distance * cos(angle), distance * sin(angle)
Convert back:
Angle# = ATAN((Y VECTOR2 MyVector)/(X VECTOR2 MyVector))
and for the length:
Length# = Length Vector2(Myvector)
or:
LengthSquared# = SQUARED LENGTH VECTOR2(Myvector)
Direction vectors:
Direction vectors are the equivalent of angles in trig. They just say what direction you're pointing, but not how far. They're really handy, though, since you can then use them to work out other stuff. What they store is three numbers from 0 to 1, saying how much of the movement goes in each direction X, Y, or Z. This is really handy, because you just multiply each term by a distance, and you get a movement vector - no trig!
They are also known as "unit vectors", because they are really just movement vectors with a straight-line length of 1. Which, if you think about it, explains how they work. Not that you needed to know that.
Convert an angle to a 2D direction vector:
SET VECTOR2 COS(angle), SIN(angle)
Convert back:
Angle# = ATAN((Y VECTOR2 MyVector)/(X VECTOR2 MyVector))
Convert a movement or position vector into a direction vector and a length:
This process is known as "Normalizing", which explains the name of the builtin command to do it. You can't turn the vector (0,0,0) into a direction vector, but you can with any other one.
NORMALIZE VECTOR4 MyDirectionVector, MyMovementVector
Length# = LENGTH VECTOR4(MyMovementVector)
` or
LengthSquared# = SQUARED LENGTH VECTOR4(MyMovementVector)
Convert back:
SCALE VECTOR4 MyMovementVector, MyDirectionVector, Length#
Find the angle between two direction (or movement) vectors
This looks fiddly but is at least one trig function shorter than we'd need, doing it with trig.
Angle# = ACOS (DOT PRODUCT VECTOR3(VectorA, VectorB) / LENGTH VECTOR3(VectorA) * LENGTH VECTOR3(VectorB))
Find how far a vector goes in a direction vector:
We've already found how to find how far they go in x, y, and z, but what about arbitrary directions? How do we find how far a movement or position vector goes, in the direction pointed at by a direction vector?
With trig this would be agonisingly painful. With Vectors, it's just:
Distance = DOT PRODUCT VECTOR3(MyDirectionVector, MyMovementVector)
Now, what's cool about vectors is that you can do cool stuff really fast with them. Sure, you had to do trig to generate them, but you only had to do that
once, and now you can do anything you like, without using any trig until the time comes to convert them back.
Moving something somewhere:
Add the movement vector to the position vector (or vice versa)... and the result is the position vector for where you moved it to.
ADD VECTOR3 VectorResult, VectorA, VectorB
Finding the distance between two points:
Subtract one position vector from another, and you get the movement vector that describes how to move between them.
SUBTRACT VECTOR3 VectorResult, VectorA, VectorB
Find a point somewhere on a line between two position vectors:
Now this alone is worth the entry fee!
LINEAR INTERPOLATE VECTOR4 VectorResult, VectorA, VectorB, DistanceFromA
Rotating a vector:
There's something else we need to use, to rotate our Vector4s. And these are "Matrix4"s.
MATRIX4S
Matrix4s are 4x4 arrays, at least under the hood. They are nothing at all to do with the "Matrix" type: they're just named confusingly similarly. I find that confusing! Fortunately, they are also called "Quaternions", so I'll call them "Quat"s. Because I'm lazy.
What they store, is
tons of stuff! They are like a rotation vector, position vector, and a movement vector, all rolled into one: they store the rotation in each direction, the position in that direction, and how long the vector is in that direction. All in one variable.
But that's not all. Multiplying one of them by a vector, or by another Quat, makes magic happen. Yup. Seriously. Magic. Well OK, it's probably not magic, but I don't understand it, and don't need to. I hope.
You don't need to know what numbers the Quats store: just think of each one as a black box with an input (which will be another Quat, or a Vector) and an output (which will be whatever the input was, modified).
The commands
Again, you don't need to understand these yet, I'm just listing them here so you can get an idea of what's available. Skim on past.
ADD MATRIX4 QuatResult, QuatA, QuatB - Adds two of them together.
MULTIPLY MATRIX4 QuatResult, QuatA, QuatB - Multiplies two of them together.
SUBTRACT MATRIX4 QuatResult, QuatA, QuatB - Subtracts one from the other.
DIVIDE MATRIX4 QuatID, Value - Divides everything in a Quat by a single number (not by another Quat as you'd expect given the other commands!). Use "scale matrix" instead unless you know what you're doing.
ThrowAwayValue = MAKE MATRIX4(QuatID) - Create the Quat.
COPY MATRIX4 QuatResult, QuatSource - Copies a Quat
ThrowAwayValue = DELETE MATRIX4 QuatID - Free the memory for the Quat
PROJECTION MATRIX4 QuatID - Overwrites the Quat with the Projection Quat.
SET IDENTITY MATRIX4 - Overwrites the Quat with the Identity Quat.
VIEW MATRIX4 - Overwrites the Quat with the View Quat.
WORLD MATRIX4 - Overwrites the Quat with the World Quat.
Boolean = IS EQUAL MATRIX4(QuatA, QuatB) - 1 if both are identical.
Boolean = IS IDENTITY MATRIX4(QuatID) - 1 if it's an "identity" Quat.
ROTATE X MATRIX4 QuatID, Angle# - Rotate the Quat around that angle.
ROTATE Y MATRIX4 QuatID, Angle# - (same)
ROTATE Z MATRIX4 QuatID, Angle# - (same)
ROTATE YPR MATRIX4 Yaw#, Pitch#, Roll# - Rotate the Quat by three angles.
SCALE MATRIX4 QuatID, X#, Y#, Z# - Scale the Quat in three directions.
TRANSLATE MATRIX4 QuatID, X#, Y#, Z# - Move the Quat in three directions.
TRANSPOSE MATRIX4 QuatResult, QuatSource - Flip the Quat along the main diagonal (which starts at the top left).
TRANSFORM VECTOR4 VectorResult, VectorSource, QuatSource - do the input output thing.
Stuff I don't understand, so is probably rather cool:
ThrowAwayValue = INVERSE MATRIX4(QuatResult, QuatSource) - Create the "inverse" of the Quat - can be a slow operation. And I don't know what you'd use it for?
PROJECT VECTOR3 VectorResult, VectorSource, QuatProjection, QuatView, QuatWorld
BUILD FOV LHMATRIX4
BUILD FOV RHMATRIX4
BUILD LOOKAT LHMATRIX4
BUILD LOOKAT RHMATRIX4
BUILD ORTHO LHMATRIX4
BUILD ORTHO RHMATRIX4
BUILD PERSPECTIVE LHMATRIX4
BUILD PERSPECTIVE RHMATRIX4
BUILD REFLECTION MATRIX4
BUILD ROTATION AXIS MATRIX4
Building the black boxes
To use our Quats, we need to make ourself a sort of toolkit of them, with one for every purpose: one to rotate a vector, one to move it, and so on.
The null Quat
When you create a new Quat, it's filled with zeroes. This is the "null" Quat and is almost completely useless, apart from cancelling stuff out, a bit like the number 0 in maths.
The identity Quat
This is the simplest useful Quat, equivalent to "1". It's your starting point. To make one, do this:
SET IDENTITY MATRIX4 IdentityQuat
The translation Quat
If you want a Quat that will move a vector by X#, Y#, and Z#, then this is your friend.
SET IDENTITY MATRIX4 TranslateQuat
TRANSLATE MATRIX4 TranslateQuat, X#, Y#, Z#
We've taken the identity Quat and moved ("translated") it. Any Vector we apply this to now, will be moved by the same amount.
The scaling Quat
If you want a Quat that will make stuff bigger or smaller, this is your tool!
SET IDENTITY MATRIX4 ScaleQuat
SCALE MATRIX4 ScaleQuat, X#, Y#, Z#
We've taken the identity Quat and moved ("translated") it. Any Vector we apply this to now, will be scaled by the same amount. Doesn't sound so hot until we realise that this can be used to scale, for example, vertex positions to change object sizes (OK I know, "SCALE OBJECT"... still.)
The rotation Quat
This is the one you came here for though! A Quat that will rotate stuff!
SET IDENTITY MATRIX4 RotateQuat
ROTATE YPR MATRIX4 RotateQuat, Yaw#, Pitch#, Roll#
Noticing a pattern here? These things are easy to make! You could use the other "ROTATE MATRIX" things if you only wanted to rotate in one of x/y/z, of course.
The projection Quat
I know nothing about these
I suspect they are related to the commands I don't know.
Applying the effects
The easy (slow) way
To move an object somewhere, turn it, and move it again, you might do:
` There is no "Find Free Matrix4" function in IanM's stuff that I can find. Shame.
QuatTranslater2 = 1
ThrowAwayValue = MAKE VECTOR4(IdentityQuat)
VectorTmp1 = Find Free Vector()
ThrowAwayValue = MAKE MATRIX4(QuatTranslater1)
COPY MATRIX4 QuatTranslater1, IdentityQuat
TRANSLATE MATRIX4 QuatTranslater1, X1#, Y1#, Z1#
TRANSFORM VECTOR4 VectorTmp, VectorStart, QuatTranslater1
QuatRotater = 2
ThrowAwayValue = MAKE MATRIX4(QuatRotater)
VectorTmp2 = Find Free Vector()
ThrowAwayValue = MAKE VECTOR4(VectorTmp2)
COPY MATRIX4 QuatRotater, IdentityQuat
ROTATE YPR MATRIX4 QuatRotater, Yaw#, Pitch#, Roll#
TRANSFORM VECTOR4 VectorTmp2, VectorTmp, QuatRotater
QuatTranslater2 = 3
ThrowAwayValue = MAKE MATRIX4(QuatTranslater2)
VectorResult = Find Free Vector()
ThrowAwayValue = MAKE VECTOR4(VectorResult)
COPY MATRIX4 QuatTranslater2, IdentityQuat
TRANSLATE MATRIX4 QuatTranslater2, X2#, Y2#, Z2#
TRANSFORM VECTOR4 VectorResult, VectorTmp2, QuatTranslater2
That's easy enough to understand I guess: you build each Quat in turn, the translation, the rotation, then another translation, and apply them in turn to your VectorStart to get your VectorResult.
But it's slow, and you have to create three vectors and three Quats just to handle it.
No good!
The easier (and faster!) way
So long as you apply the effects to your Quat in the same order you'd have applied them to the Vector, you can "daisychain" the effects!
Quat = 1
ThrowAwayValue = MAKE MATRIX4(Quat)
VectorResult = Find Free Vector()
ThrowAwayValue = MAKE VECTOR4(VectorResult)
SET IDENTITY MATRIX4 Quat
TRANSLATE MATRIX4 Quat, X1#, Y1#, Z1#
ROTATE YPR MATRIX4 Quat, Yaw#, Pitch#, Roll#
TRANSLATE MATRIX4 Quat, X2#, Y2#, Z2#
TRANSFORM VECTOR4 VectorResult, VectorStart, Quat
That's a LOT less CPU work!
Quaternions and Gimbal lock
Why are we using Vector4s and Matrix4s? Why are there no Matrix3s?
Because Matrix4s are also known as Quaternions. And Quaternions rotating stuff in ways that mostly dodge one of the problems that have plagued us since the beginning of time: Gimbal Lock!
Just as physical gimbal lock (http://en.wikipedia.org/wiki/Gimbal_lock) can be resolved by adding another gimbal and ensuring that it keeps a large gap between the two other axes, so the extra column and row in the Matrix4 table let us happily rotate our objects without them locking up.
FURTHER READING:
Avoid Wikipedia, they get all mathematical at you. Try this one instead:
http://www.devmaster.net/wiki/Transformation_matrices
Yet another programmer - http://www.thudgame.com/mmo
All my posts are Public Domain unless I say otherwise.
You may use all my code and ideas as you see fit.