Sorry your browser is not supported!

You are using an outdated browser that does not support modern web technologies, in order to use this site please update to a new browser.

Browsers supported include Chrome, FireFox, Safari, Opera, Internet Explorer 10+ or Microsoft Edge.

Dark Physics & Dark A.I. & Dark Dynamix / A small physics tutorial.

Author
Message
Dewi Morgan
16
Years of Service
User Offline
Joined: 16th Jan 2008
Location: London, UK
Posted: 29th Feb 2008 05:03 Edited at: 11th Mar 2008 18:40
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:


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.



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.

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.
BatVink
Moderator
21
Years of Service
User Offline
Joined: 4th Apr 2003
Location: Gods own County, UK
Posted: 29th Feb 2008 10:52
Some very useful stuff there, all in one place.

One small problem with DB Pro is that it only seems to be accurate to within about 4 milliseconds. It doesn't sound like much, but for calculations like this, it can mess you up over time.

IanM's Matrix Utilities have a command - HITIMER(). If you substitute this for any refernces to TIMER(), all of the above beecomes far more reliable.

Quote: "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.
"


This is an interesting one, because it's something I'm considering at the moment. In the main, I think you're right. However, if you want to add some realism and unpredictability, it could be useful to have an "acceleration graph" that plots acceleration over speed. It's a very crude method of employing gear-changes (in an auotomatic car), so that a car doesn't simply acclerate in a linear fashion.
Dewi Morgan
16
Years of Service
User Offline
Joined: 16th Jan 2008
Location: London, UK
Posted: 29th Feb 2008 11:13 Edited at: 29th Feb 2008 11:14
I think that changing acceleration over time will only really become noticeable with force feedback devices. In a car, you can *feel* it in your back, the acceleration swelling up and pushing harder into your back. I am not so convinced that it would matter in a computer game. But if it would be noticeable, even a little, then it may be worth doing. I suspect that vehicle games are the ones where this would be important: pedestrian games rarely accelerate enough for it to be noticed.

Thanks for the HiTimer() tip, too - I've updated my own code, and the above post.

Yet another game programmer
BatVink
Moderator
21
Years of Service
User Offline
Joined: 4th Apr 2003
Location: Gods own County, UK
Posted: 29th Feb 2008 13:47
I think the acceleration is more of a visual thing, and an AI skill-level addition. Think of a pack of cars leaving the starting line. If they have different acceleration curves, you will see them moving up through the gears and shifting positions in the pack. They will also drive differently through curves that are too sharp to take at full speed.

So it's a visual thing, and probably down to preference. The application I'm working on is far more visual than your average racing game. And it is a crude method that probably doesn't fit in with your real-life physics advice!
KISTech
16
Years of Service
User Offline
Joined: 8th Feb 2008
Location: Aloha, Oregon
Posted: 29th Feb 2008 19:38
Just for giggles, here's a function I've used over the years just to calculate position around a circle. I've used them, and still use them, for crude circular orbits of planets.



A very long time ago I knew the missing peice to calculate the Y position if there was a tilt to the orbit. There was also another peice to this that I lost a while back that allowed you to make the orbit eliptical rather than a perfect circle. Maybe one of you math wizards can help fill in the blanks..


Don't think, just code.
Dewi Morgan
16
Years of Service
User Offline
Joined: 16th Jan 2008
Location: London, UK
Posted: 1st Mar 2008 08:36 Edited at: 9th Mar 2008 12:25
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:

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!


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.
Dewi Morgan
16
Years of Service
User Offline
Joined: 16th Jan 2008
Location: London, UK
Posted: 1st Mar 2008 08:45
Your homework, should you choose to accept it, is to take KISTech's code, and rewrite both as Trig and as Quat solutions, then see which is faster!

I'm writing these tutorials partly as an aide memoire to myself as I learn. If you find mistakes, errors or stupidities, PLEASE let me know: not only will you save other people lots of work if they believe me, you will also save ME lots of work!

I have also tried to be quite honest about how little I know. If you can fill in one of these holes, please do!

These tutorials, like everything I post here, are in the public domain and may be used as you like.

Yet another game programmer
sindore
19
Years of Service
User Offline
Joined: 2nd Jul 2004
Location: Bedfordshire, UK
Posted: 2nd Mar 2008 11:09 Edited at: 18th May 2008 14:52
sorry about the spam.

soul sucking devils, twisted body of the damed, slivering slim drips from ever poor, sin licking at your ears, and the smell stinging your eyes, and if you don't like it, get out of my kitchen!....
Pixel Perfect
17
Years of Service
User Offline
Joined: 21st Feb 2007
Location: UK
Posted: 2nd Mar 2008 15:48
@sindore
Please stop spamming other people's threads .. it's rude and childish

@Dewi Morgan
Quote: "I'm writing these tutorials partly as an aide memoire to myself as I learn. "

Thanks for sharing these with us, it's always handy to have a concise reference and I've learnt a few new things too. Good work!

No matter how good your code is, someone will improve on it
jason p sage
16
Years of Service
User Offline
Joined: 10th Jun 2007
Location: Ellington, CT USA
Posted: 5th Mar 2008 01:58
WOW Dewi! GREAT Stuff my MAN!!! Like you - I DO NOT KNOW EVERYTHING - and I forget things - and this thread...WOW - Good Stuff in ONE LOCATION! sharing how to use VECTORS to our benefit especialyl because I think there is A LOT of power hidden in those 3dmaths... and you are scratching the surface and I think its AWESOME!

KISTech
16
Years of Service
User Offline
Joined: 8th Feb 2008
Location: Aloha, Oregon
Posted: 5th Mar 2008 05:19
Agreed. I've referred back to this thread several times in just the last few days.

Good stuff !!


It's a big universe, and they are out there somewhere.
draknir_
17
Years of Service
User Offline
Joined: 19th Oct 2006
Location: Netherlands
Posted: 8th Mar 2008 16:20
Great thread, alot of useful stuff here. I know most of that stuff (at least im supposed to ) but I never heard of the jerk, yank, snap, crackle and pop, thats very interesting.
StevetS
19
Years of Service
User Offline
Joined: 19th May 2004
Location:
Posted: 8th Mar 2008 20:36
This is a good tutorial - a nice reminder and useful.

Esp like the info on the intricacies of DBP timing and how it affects the physics over time. Wasn't aware of the 4ms issue.

This goes into my scrapbook of stuff to 'learn, know and use'.

Cheers M8.

Dewi Morgan
16
Years of Service
User Offline
Joined: 16th Jan 2008
Location: London, UK
Posted: 11th Mar 2008 18:43
I updated the timer code in the original post again to deal with the once-every-few-days wrapping of timer(), per http://forum.thegamecreators.com/?m=forum_view&t=125657&b=1&p=0

I suspect that with IanM's HiTimer, wrapping will occur more frequently?

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.

Login to post a reply

Server time is: 2024-04-23 15:33:03
Your offset time is: 2024-04-23 15:33:03