?

Log in

Dewi Morgan

DarkBasic Math and Physics tips.

DarkBasic Math and Physics tips.

Previous Entry Share Next Entry
defaultShades
I wrote these at: forum.thegamecreators.com.
Because of this, they are very DarkBasic centred.
I'm archiving them here in case they get lost.

PHYSICS

Whether you use a physics engine or not, you will eventually need to know about the equations of motion.

Time to get jiggy with the maths. Don't worry, I won't assume you can "rearrange an equation", and I won't use the maths/physics terms like "s=ut+1/2at^2", because clearly mathematicians wouldn't recognise a decent variable name if it slapped them in the face.


Pick Your Units:

To begin with, figure out what your basic units are. In Dark Physics, they appear to be the standard SI units, but how they map to the world I do not know. But under this system, earth's gravity is 9.8m/s/s.

In my own stuff, I use one world unit = 1cm, which gives nice "round" power-of-two numbers like 1ft ~32units, which is a good number for max step height, min crawl, and body width, and makes map designing easy. My unit of time is the millisecond, because that's what I get from the timer() command, and that's what all sleep commands take. It also means I can store time in an int instead of a float. Under this system, earth's gravity is 0.00098cm/ms/ms.

But, knowing the acceleration, how do you figure out the velocity of something? Or the force being applied by gravity on an object? Or... well, whatever?


Time:

You'll almost never work out time. Time should be one of the things you *know*. And the most important time you need to know is the "tick length" - the amount of time it took to draw the previous frame, which is also the length of time that you will be assuming has passed until the frame you are about to draw.

In the equations below, I've put "TickLength" where normally I'd put "Time", for this reason. The equations work for any length of time, but generally the unit of time you're fussed about is the tick length, which will be a number of milliseconds.

Easiest way to know your tick length is to have something like this at the top of your main loop:

Global TickLength
last_tick=timer()-1
Do
  time_now = timer()
  TickLength = time_now - last_tick
  last_tick = time_now


... and on to the rest of your main loop.



However, the above may cause some problems: a better way to do it is as follows, a few extra lines that should significantly improve your code's stability.

` Need to define as DWord so they don't go negative after a few days
` See http://forum.thegamecreators.com/?m=forum_view&t=125657&b=1
Global TickLength as DWord
last_tick as DWord
time_now as DWord
last_tick=timer()-1
Do
  ` Ideally, use IanM's HiTimer() instead.
  ` See BatVink's comment following this post.
  time_now = timer()
  TickLength = time_now - last_tick
  last_tick = time_now
  ` If TickLength is -ve, timer() wrapped. Happens once every few days of computer uptime, but can cause *drastic* problems.
  if TickLength < 0 then TickLength = (4294967295-last_tick)+time_now
  ` Stop the physics going *totally* nuts if you pause the game for an hour... also protect yourself against the above going wrong.
  if TickLength > 250 then TickLength = 250


... and on to the rest of your main loop.


Distance:

Distance is handy for all sorts of commands. Sometimes you can know it directly, by having two things that you know the distance between. You can get the distance between two objects using:

In 1D: (objects which have the same value for two coordinates, in this case Y and Z)
Distance = (Object1.X# - Object2.X#)

In 2D: (objects which have the same value for two coordinates, in this case Z)
XDistance# = Object1.X# - Object2.X#
YDistance# = Object1.Y# - Object2.Y#
DistanceSquared = (XDistance#*XDistance#) + (YDistance#*YDistance#)
Distance = sqrt(DistanceSquared)

In 3D:
XDistance = Object1.X# - Object2.X#
YDistance = Object1.Y# - Object2.Y#
ZDistance = Object1.Z# - Object2.Z#
DistanceSquared = (XDistance#*XDistance#) + (YDistance#*YDistance#) + (ZDistance#*ZDistance#)
Distance = sqrt(DistanceSquared)

Since knowing the squared version of some variables like this is "cheaper" (by one relatively expensive SQRT call) than knowing the unsquared value, it's often useful to store the squared version and compare to that, rather than using SQRT.

For spherical collision detection, all you need is whether they have collided, so instead of using:
if SQRT(x*x + y*y) < Diameter ` Expensive!
use:
if (x*x + y*y) < (Diameter*Diameter) ` So cheap!


Linear Motion:

So stuff is moving in a straight line. Good. You have a whole menu of different ways that you can work stuff out then. Pick the one that has the term you want to find out on the left, and the stuff you know on the right. If there are none that fit the bill, you probably need to find ways of finding at least some of those things from your physics engine.

Constant velocity:
StartSpeed = Distance / (0.5 * TickLength) - EndSpeed
EndSpeed = Distance / (0.5 * TickLength) - StartSpeed
Distance = 0.5 * (StartSpeed + EndSpeed) * TickLength
TickLength = Distance / (0.5 * (StartSpeed + EndSpeed))

Accelerating:
StartSpeed = EndSpeed - Acceleration * TickLength
StartSpeed = (Distance / TickLength) - (0.5 * Acceleration * TickLength)
StartSpeedSquared = EndSpeedSquared - (2 * Acceleration * Distance)

EndSpeed = StartSpeed + Acceleration * TickLength
EndSpeed = (Distance / TickLength) + (0.5 * Acceleration * TickLength)
EndSpeedSquared = StartSpeedSquared + (2 * Acceleration * Distance)

Acceleration = (EndSpeed - StartSpeed)/ TickLength
Acceleration = Distance - (StartSpeed * TickLength) / (0.5 * TickLength * TickLength)
Acceleration = (EndSpeedSquared - StartSpeedSquared) / 2 * Distance
Acceleration = ( (EndSpeed * TickLength) - Distance ) / (0.5 * TickLength * TickLength)

Distance = (EndSpeedSquared - StartSpeedSquared) / 2 * Acceleration
Distance = (EndSpeed * TickLength) - (0.5 * Acceleration * TickLength * TickLength)
Distance = (StartSpeed * TickLength) + (0.5 * Acceleration * TickLength * TickLength)

TickLength = (EndSpeed - StartSpeed)/ Acceleration


Force:

Some physics engines like to know about force, too. Force is nice and easy!
Force is measured in Newtons, or in kgm/s^2.

Force = Mass * Acceleration
Acceleration = Force / Mass
Mass = Force / Acceleration

As an aside, gravity is NOT a constant force, but a constant *acceleration*. The force from gravity is, from the above (of you're using metres and seconds as your basic units):

Force = Mass * 9.8


Rotation:

Spinning stuff is fun... right? Right? Not if you ask a maths guy: they start babbling about "omega^2=(omega0)^2+2aDeltaPhi" and you have to hit them to make them shut up. So here's some rotational stuff in proper, programmer terms.

The angular equivalents are much like the linear ones, except instead of metres per second, speed is measured in degrees per second (or whatever your chosen units are: radians per millisecond will also work, just be consistent!). But if you're using degrees and seconds, then:

StartAngle and EndAngle are measured in "degrees".
StartSpeed and EndSpeed are measured in "degrees per second".
Acceleration is measured in "degrees per second per second"

Constant speed:
TickLength = 2 * (EndAngle - StartAngle) / (StartSpeed + EndSpeed)
StartAngle = EndAngle - 0.5 * (StartSpeed + EndSpeed) * TickLength
EndAngle = StartAngle + 0.5 * (StartSpeed + EndSpeed) * TickLength
StartSpeed = (EndAngle - StartAngle) / ( 0.5 * TickLength ) - EndSpeed
EndSpeed = (EndAngle - StartAngle) / ( 0.5 * TickLength ) - StartSpeed

Accelerating:
TickLength = EndSpeed - StartSpeed / Acceleration

StartAngle = EndAngle - StartSpeed * TickLength - 0.5 * Acceleration * TickLength * TickLength
StartAngle = EndAngle - EndSpeed * TickLength + 0.5 * Acceleration * TickLength * TickLength
StartAngle = (EndSpeed * EndSpeed - StartSpeed * StartSpeed) / (2 * Acceleration) - EndAngle

EndAngle = StartAngle + StartSpeed * TickLength + 0.5 * Acceleration * TickLength * TickLength
EndAngle = StartAngle + EndSpeed * TickLength - 0.5 * Acceleration * TickLength * TickLength
EndAngle = (EndSpeed * EndSpeed - StartSpeed * StartSpeed) / (2 * Acceleration) + StartAngle

StartSpeed = (EndAngle - StartAngle - 0.5 * Acceleration * TickLength * TickLength ) / TickLength
StartSpeed = EndSpeed - Acceleration * TickLength
StartSpeedSquared = EndSpeedSquared - 2 * Acceleration * (EndAngle - StartAngle)

EndSpeed = ( EndAngle - StartAngle + 0.5 * Acceleration * TickLength * TickLength ) / TickLength
EndSpeed = StartSpeed + Acceleration * TickLength
EndSpeedSquared = StartSpeedSquared + 2 * Acceleration * (EndAngle - StartAngle)

Acceleration = (EndAngle - StartAngle - StartSpeed * TickLength ) / 0.5 * TickLength * TickLength
Acceleration = (StartAngle - EndAngle + EndSpeed * TickLength) / (0.5 * TickLength * TickLength)
Acceleration = (EndSpeed * EndSpeed - StartSpeed * StartSpeed) / (2 * (EndAngle - StartAngle))
Acceleration = EndSpeed - StartSpeed / TickLength


Stuff You Really Don't Need To Know:

Gravity:

Generally you can treat gravity the same as any other constant acceleration, at 9.8m/s^2. If you want to play with spaceships and planet, though, then you need something a little more complex. For a start you need to set yourself a "gravitational constant" called G#... it's a constant, the value is always the same, but you'll need to pick the one that uses your favoured units.

G# = 6.674 * 10^-11 m^3/kg s^2
G# = 6.674 * 10^-11 N m^2/kg^-2
G# = 6.674 * 10^-8 cm^3/g s^2

Acceleration = (G# * PlanetMass)/(OrbitRadius*OrbitRadius)
(OrbitRadius is measured from the centre of the planet, not the surface!)
ForceBetweenTwoObjects = (G# * Object1Mass * Object2Mass)/(OrbitRadius*OrbitRadius)

If you really want to go bonkers with orbits, look up "Perurbation analysis": it lets you do stuff like project orbits long into the future, etc.

Relativity:

Relativistic equations are actually a lot less scary than they look, but the chances of you ever wanting to use them in a game are pretty much zero.

Still, if you wanted relativistic effects, you'd just store yourself some Lorentzy goodness in a variable like so:
Lorentz# = 1/SQRT(1-(SpeedSquared/LightSpeedSquared))

Then using that:
Energy = Lorentz# * Mass * LightSpeedSquared
(Einstein's e=mc^2)
Momentum = Lorentz# * Mass * Speed
Force = (Mass * Acceleration * Lorentz#) + Lorentz# * Lorentz# * Lorentz# * Mass * Acceleration * SpeedSquared / LightSpeedSquared

Not so tricky as all that is it? But for every normal purpose, Lorenz# is 1.000... and anything divided by the speed of light squared is 0, so the above becomes the familiar F=ma.


Yank, Jerk, Jounce, Snap, Crackle and Pop:

Generally in games it's enough for acceleration to be applied in full, so when they hit "forward", their car starts accelerating as fast as it can. That's the same as applying 100% of the force of the engine in one go, as if someone had stamped on the accelerator infinitely fast. Players tend to like this, and not doing it can feel "slow", "unresponsive", and "mushy". So you should not need to worry about change in acceleration over time.

In fact, if players are not in a vehicle, they generally like it if, when they start walking, they are immediately moving at their top speed, without even considering acceleration. Equally, they want to be able to look where they aim with the mouse, immediately, rather than using curveangle to slow the camera.

In the real world, engineers care about the "rate of change of acceleration", known as the "Jerk", which is measured in metres per second per second per second (m/s^3). Just like mass times acceleration is called force, mass times jerk has a name, too: Yank. And the reason that engineers care is that a high yank can cause whiplash, and can increase wear on moving parts.

Also, in really sensitive stuff, like the Hubble space telescope, they cared about the rate at which the jerk changed, and they called that rate the "Jounce" or "Snap" rate, measured in m/s^4. There are even higher things, called "Crackle" (m/s^5) and "Pop" (m/s^6). Because while mathematicians like terse variable names, physicists like silly ones.

But in general, yeah, you don't even need to know that these ideas exist. Not for anything short of a very accurate Hubble simulation game, anyway.


Other Funky Stuff:

The strong and weak nuclear forces, electromagnetic forces, and electrostatic forces: none likely to be useful in games.


Stuff I probably SHOULD add to this article:

Friction, momentum, tension/pressure (hydraulic and springs), torque, impulse, work, centripetal force, normal force... no point adding stuff on these unless people found this article at all useful, though. If you didn't, sorry for wasting your time with it.

Also, there will be bugs and brainfarts and typos in this - please let me know if you spot any.




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
Powered by LiveJournal.com