Vectors in 3D

In Chapter 8 we discussed vectors and their interpretation in 2D. As you might have guessed, all the things we discussed there hold in 3D space as well. All we do is add one more coordinate to our vector, namely the z-coordinate.

The operations we looked at with vectors in 2D can be easily transferred to 3D space. We specify vectors in 3D with a statement like this:

Addition in 3D is carried out as follows:

c = a + b = (a.x, a.y, b.z) + (b.x, b.y, b.z) = (a.x + b.x, a.y + b.y, a.z + b.z) Subtraction works exactly the same way:

c = a - b = (a.x, a.y, b.z) - (b.x, b.y, b.z) = (a.x - b.x, a.y - b.y, a.z - b.z)

Multiplying a vector by a scalar works like this:

a' = a x scalar = (a.x x scalar, a.y x scalar, a.z x scalar)

Measuring the length of a vector in 3D is also quite simple; we just add the z-coordinate to the Pythagorean equation:

And based on this we can also normalize our vectors to unit length again: a' = (a.x / |a|, a.y / |a|, a.z / |a|)

All the interpretations of vectors we talked about in Chapter 8 hold in 3D as well:

Positions are just denoted by a normal vector's x-, y- and z-coordinate.

Velocities and accelerations can also be represented as 3D vectors. Each component then represents a certain quantity of the attribute on one axis, such as meters per second in case of velocity or meters per second per second for acceleration.

We can represent directions (or axes) as simple 3D unit vectors. We did that in Chapter 8 when we used the rotation facilities of OpenGL ES.

We can measure distances by subtracting the starting vector from the end vector and measuring the resulting vector's length.

One more operation that can be rather useful is rotating a 3D vector around a 3D axis. We used this principle via the OpenGL ES glRotatef() method earlier. However, we can't use it to rotate one of the vectors that we'll use to store positions or directions of our game objects, because it only works on vertices we submit to the GPU. Luckily there's a Matrix class in the Android API that allows us to emulate what OpenGL ES

does on the GPU. Let's write a Vector3 class that implements all of these features. Listing 11-1 shows you the code, which I'll again explain along the way.

Listing 11-1. Vector3.java, a Vector in 3D

package com.badlogic.androidgames.framework.math;

import android.opengl.Matrix; import android.util.FloatMath;

public class Vector3 {

private static final float[] matrix = new float[l6]; private static final float[] inVec = new float[4]; private static final float[] outVec = new float[4]; public float x, y, z;

The class starts with a couple of private static final float arrays. We'll need them later on when we implement the new rotate() method of our Vector3 class. Just remember that the matrix member has 16 elements and the inVec and outVec each have 4 elements.

The x, y and z members defined next should be self-explanatory. They store the actual components of the vector:

public Vector3(float x, float y, float z) { this.x = x; this.y = y; this.z = z;

public Vector3(Vector3 other) { this.x = other.x; this.y = other.y; this.z = other.z;

public Vector3 cpy() {

public Vector3 set(float x, float y, float z) { this.x = x; this.y = y; this.z = z; return this;

public Vector3 set(Vector3 other) { this.x = other.x; this.y = other.y; this.z = other.z; return this;

Like Vector2, our Vector3 class has a couple of constructors and setters and a cpy() method, so we can easily clone vectors or set them from components calculated in our program.

public Vector3 add(float x, float y, float z) { this.x += x; this.y += y; this.z += z; return this;

public Vector3 add(Vector3 other) { this.x += other.x; this.y += other.y; this.z += other.z; return this;

public Vector3 sub(float x, float y, float z) { this.x -= x; this.y -= y; this.z -= z; return this;

public Vector3 sub(Vector3 other) { this.x -= other.x; this.y -= other.y; this.z -= other.z; return this;

public Vector3 mul(float scalar) { this.x *= scalar; this.y *= scalar; this.z *= scalar; return this;

The various add(), sub() and mul() methods are just an extension of what we had in our Vector2 class with an additional z-coordinate. They implement what we discussed a few pages ago. Straightforward, right?

public float len() {

public Vector3 nor() { float len = len(); if (len != 0) { this.x /= len; this.y /= len; this.z /= len;

return this;

The len() and nor() methods are also essentially the same as in the Vector2 class. All we do is incorporate the new z-coordinate into the calculations.

public Vector3 rotate(float angle, float axisX, float axisY, float axisZ) { inVec[0] = x; inVec[l] = y; inVec[2] = z; inVec[3] = 1;

Matrix.setIdentityM(matrix, 0);

Matrix.rotateM(matrix, 0, angle, axisX, axisY, axisZ); Matrix.multiplyMV(outVec, 0, matrix, 0, inVec, 0); x = outVec[0]; y = outVec[l]; z = outVec[2]; return this;

And here's our new rotate() method. As indicated earlier, it makes use of Android's Matrix class. The Matrix class basically consists of a couple of static methods, like Matrix.setIdentityM() or Matrix.rotateM(). These operate on float arrays, like the ones we defined earlier. A matrix is stored as 16 float values, and a vector is expected to have four elements. I won't go into detail about the inner workings of the class; all we need is a way to emulate the matrix capabilities of OpenGL ES on the Java side, and that's exactly what this class offers us. All the methods work on a matrix and operate in exactly the same way as glRotatef(), glTranslatef() or glIdentityf() in OpenGL ES.

The method starts off setting the vector's components to the inVec array we defined earlier. Next, we call Matrix.setIdentityM() on the matrix member of our class. This will "clear" the matrix. With OpenGL ES we used glIdentityf() to do the same thing with matrices residing on the GPU. Next we call Matrix.rotateM(). It takes the float array holding the matrix, an offset into that array, the angle we want to rotate by in degrees, and the (unit length) axis we want to rotate around. This method is equivalent to glRotatef(). It will multiply the given matrix by a rotation matrix. Finally we call Matrix.multiplyMV(), which will multiply our vector stored in inVec by the matrix. This applies all the transformations stored in the matrix to the vector. The result will be output in outVec. The rest of the method just grabs the resulting new components from the outVec array and stores them in the Vector3's members.

NOTE You can use the Matrix class to do a lot more than just rotating vectors. It operates in exactly the same way as OpenGL ES in its effects on the passed-in matrix.

public float dist(Vector3 other) { float distX = this.x - other.x; float distY = this.y - other.y; float distZ = this.z - other.z;

return FloatMath.sqrt(distX * distX + distY * distY + distZ * distZ);

public float dist(float x, float y, float z) { float distX = this.x - x; float distY = this.y - y;

return FloatMath.sqrt(distX * distX + distY * distY + distZ * distZ);

public float distSquared(Vector3 other) { float distX = this.x - other.x; float distY = this.y - other.y; float distZ = this.z - other.z;

return distX * distX + distY * distY + distZ * distZ;

public float distSquared(float x, float y, float z) { float distX = this.x - x; float distY = this.y - y; float distZ = this.z - z;

return distX * distX + distY * distY + distZ * distZ;

Finally we have the usual dist() and distSquared() methods to calculate the distance between two vectors in 3D.

Note that I left out the angle() method from Vector2. While it is possible to measure the angle between two vectors in 3D it's not giving us an angle in the range 0 to 360. Usually we get away with just evaluating the angle between two vectors in the x/y, z/y and x/z plane by using only two components of each vector and applying the Vector2.angle() method. We won't need this functionality for our last game, so we'll return to the topic at that point.

I think you'll agree that we don't need an explicit example of using this class. We can just invoke it the way we did with the Vector2 class in Chapter 8. On to the next topic: lighting in OpenGL ES.

0 0

Post a comment