# Gamemaker 6.1 Direct3D extensions

Orphan page

## The 3D examples given here will run in Gamemaker 7.0 __without requiring
any modification__. Furthermore it looks as if there is every reason to
move to Gamemaker 7 as the system requirements are similar.

I've chosen to bypass the normal 2D mode of GM6.1 and go straight to 3D mode.
This is basically because 3D is a lot more exciting. I've tried to make simple
games with sprites for a long time. I used to believe that I could never learn the
programming techniques for 3D.

Since then I've learned a lot about rotation and started to understand the
Direct3D lighting functions as implemented in Gamemaker. Please note that only a
subset of Direct3D is available to Gamemaker 6.1 programs and some of the omissions
are irritating.

## The care and feeding of Quaternions

This is one of those weird techniques where you probably won't see the need for
it unless __ you actually try it__. In 2D games featuring rotation the direction
variable is usually perfectly adequate but when you are programming in 3D in
Gamemaker you will probably hit a point when you need to store the orientation
of an object and you will find that it is not so easy to work with 3D
orientations stored as angles.As a simple example a racing game set on a racetrack could be implemented
using angles with the main angle being the direction the car is facing and the
steering changing that angle but in an off-road game with slopes the vehicle may
be tipped up far enough that the slope of the terrain influences the steering.
In an angles-based system it is hard to allow for this but with quaternions it
is easy to steer on an alternative axis. In a flight sim the player may
"loop-the-loop" in which case the plane will be flying inverted. An
airliner sim might get away with angles, but a stunt plane will probably need
quaternions.

Quaternions are an extension of complex numbers and as such they obey some
rules of algebra. They can be added, subtracted, multiplied, divided and even
logs, exponents and square roots are possible. For our purposes the add and
subtract operations are trivial and can be ignored.

A Unit Quaternion is a quaternion with a total magnitude of one.

__Quaternions can be used to represent an axis of rotation and the degree of
rotation about the axis__. Multiple rotations can be combined by multiplying the
quaternions and this is where it gets interesting, if an object's orientation is
represented by a quaternion then the object can execute any sequence of turns,
no matter how complicated, and simply multiplying the object's orientation by
each rotation in turn will always yield the correct final orientation.

Orientations can also be represented as matrices, but I find Quaternions
better suited to Gamemaker for the following reasons:

- There is an easy way to apply a quaternion to an object in Gamemaker's
implementation of D3D*
- The quaternion calculation takes fewer operations than a comparable matrix
operation when implemented in GML

There is an important "gotcha" in the current implementation of GM
Studio in that specifying exactly 0,0,0 as a vector causes the current event to
end early, probably due to a divide-by-zero. This happens specifically when there
is no rotation (unit quaternion). This could probably be fixed by testing for
three zeros as the built in "epsilon" should make the comparison big
enough to catch problem cases.

When programming in C those reasons for not using matrices probably will not apply, you will have
more access to Direct3D and should be able to pass the matrix to it directly, and you
may have access to optimised matrix ("vectorized") math libraries that
will be far faster than the same number of calculations written as equations. In
vectorized math the whole calculation can be handed off to a coprocessor as one
action.

A Quaternion can be used to rotate a vector, this is how I implement a flying
camera, I start with a vector pointing in the minus x direction and rotate it
and put the result into the "set_perspective" command, I also take a z
vector, rotate that and use the result as the "up" direction.

Since the same operations turn up again and again I've written scripts to
carry out some of these operations. The script is documented in the page "Quaternions"
and can be downloaded here. The script is
a work-in-progress but I am confident that the parts I've written so far work consistently.
I will try not to change the function of the parts I've already written, though
I do intend to add functions and replace some of the longer calculations with
shortened multiplied-out forms. I will put comments in the code to document it
and I will try to update the description page.

Since writing it I have found a small error in the random orientation
function. I believe the version on here is fixed.

## Other Scripts:

A model maker for a smooth ring donut/torus:

donut.gml

make_donut_model(radius,radius2,steps,steps2)

radius is the radius of the torus radius2 is the radius of the
cross-section so that the total outside radius=radius+radius2 and the radius
of the hole=radius-radius2 steps=the number of segments round the torus (try
20) steps2=the number of steps through the hole (try 10)

returns a model ID

The model is generated as a series of triangle_strips so the vertex count is
almost half that of the same model expressed as a triangle_list form. The model has been tweaked so the texture
wraps onto the torus in a similar way to the basic sphere. When the texture is
applied to the torus the top and bottom edges join up. Tile-able textures work
well.

## More complex than complex numbers

If we start with the idea that **i** is the square root of minus one, and
then define **j** as another root of -1 where **i** times **j** is
equal to minus
**j** times **i** (don't question this it just works) then we can take two
complex numbers (A+**i**B) and (C+**i**D) and combine them as (A+**i**B)+**j**(C+**i**D)
or multiplied out (A+**i**B+**j**C+**ij**D). For convenience of
notation we define **k**=**ij** giving us (__A+__**i**B+**j**C+**k**D)
which is the way quaternions are often written, though in my own notes I usually
abbreviate it to (A,B,C,D), and frequently refer to the four parts as
q0,q1,q2,q3.

Given the rules that **i**.**i**=-1 and **j**.**j**=-1 and **i**.**j**=-**j**.**i**=**k**
we can derive a set of rules for multiplication.

We start with determining how the complex parts **i**, **j**, **k**
multiply:

**i**.**j**=**k** (given)

**j**.**i**=-**i**.**j**=-**k**

**j**.**k**=**j**.**i**.**j**=(-**i**.**j**).**j**=-**i**.**j**.**j**=-**i**.(-1)=**i**

**k**.**j**=**i**.**j**.**j**=**i**.(-1)=-**i**

**k**.**i**=**i**.**j**.**j**=-**i**.**i**.**j**=-(-1).**j**=**j**

**i**.**k**=**i**.**i**.**j**=-**j**

This now allows us to multiply (q0+**i**q1+**j**q2+**k**q3) by (r0+**i**r1+**j**r2+**k**r3)

=(q0(r0+**i**r1+**j**r2+**k**r3)+**i**q1(r0+**i**r1+**j**r2+**k**r3)+**j**q2(r0+**i**r1+**j**r2+**k**r3)+**k**q3(r0+**i**r1+**j**r2+**k**r3))

=(q0(r0+**i**r1+**j**r2+**k**r3)+**i**q1r0+**ii**q1r1+**ij**q1r2+**ik**q1r3+**j**q2r0+**ji**q2r1+**jj**q2r2+**jk**q2r3+**k**q3r0+**ki**q3r1+**kj**q3r2+**kk**q3r3)

=(q0(r0+**i**r1+**j**r2+**k**r3)+r0(**i**q1+**j**q2+**k**q3)-q1r1-q2r2-q3r3+**i**(q2r3-q3r2)+**j**(q3r1-q1r3)+**k**(q1r2-q2r1))

**You do **__ not need to understand this__ to use the scripts. Much of
the above algebra was derived using Mathcad.

Further to the above: a Quaternion has a magnitude and a conjugate. A
conjugate is a quaternion where the signs of the i,j,k parts are flipped, so (A+**i**B+**j**C+**k**D)
becomes (A-**i**B-**j**C-**k**D).

Quaternions can be divided, so a multiplication can be "undone". To
divide simply multiply by the inverse.

Multiplying a quaternion by it's conjugate gives the square of it's
magnitude. The magnitude of a unit quaternion is always one, though in reality
after your computer has processed it enough times it may start to drift due to
rounding errors. Quaternions can be inverted, the inverse of a quaternion is the
conjugate over the square of the magnitude.

For unit quaternions the square of the magnitude is one so the inverse is the
same as the conjugate, so in our 3D environment we can undo a rotation by multiplying by the conjugate
of the rotation.