Sign in to follow this  
Jethro_T

3D Transformations

Recommended Posts

I have a matrix class that allows me to perform rotation about an arbitrary axis. The problem is, I can't seem to figure out how to perform rotations relative to previous rotations. For example, if I rotate 90 degrees about the y-axis, I want the x-axis and z-axis to also rotate 90 degrees and affect future rotations done in this new coordinate space. How can I do this? In my naive attempt I tried to transform all the inputed rotation vectors by the matrix before rotating about the vector, this didn't work though.

Share this post


Link to post
Share on other sites
Composition of rotations corresponds to matrix multiplication. If it doesn't seem to be behaving the way you expect, perhaps you are multiplying the matrices in the wrong order.

If that doesn't help you, please post a concrete example of two rotations that are not being compounded correctly.

Share this post


Link to post
Share on other sites
Here is an example, it translates as expected, rotates about the y-axis as expected but then the x-axis rotation is done as if the y-axis was not rotated at all.

[source]
void doSomething() {
_matrix.loadIdentity();
_matrix.translate(Vector3f(100.0f, 200.0f, 0.0f));
_matrix.rotate(Vector3f(0.0f, 1.0f, 0.0f), 30.0f);
_matrix.rotate(Vector3f(1.0f, 0.0f, 0.0f), 45.0f);
}

class Matrix4x4f {
private:
float _data[16];

public:
void translate(const Vector3f& translation);
void rotate(const Vector3f& axis, float angle);
};

void Matrix4x4f::translate(const Vector3f& translation) {
float x = _data[0] * translation.x + _data[4] * translation.y + _data[8] * translation.z + _data[12];
float y = _data[1] * translation.x + _data[5] * translation.y + _data[9] * translation.z + _data[13];
float z = _data[2] * translation.x + _data[6] * translation.y + _data[10] * translation.z + _data[14];
float w = _data[3] * translation.x + _data[7] * translation.y + _data[11] * translation.z + _data[15];

_data[12] = x;
_data[13] = y;
_data[14] = z;
_data[15] = w;
}

void Matrix4x4f::rotate(const Vector3f& axis, float angle) {
float angleInRadians = BasicMath::toRadians(angle);
float cosAngle = cosf(angleInRadians);
float sinAngle = sinf(angleInRadians);
float cosDifference = 1.0f - cosAngle;

float rotationMatrix[9];
rotationMatrix[0] = cosDifference * axis.x * axis.x + cosAngle;
rotationMatrix[1] = cosDifference * axis.x * axis.y + sinAngle * axis.z;
rotationMatrix[2] = cosDifference * axis.x * axis.z - sinAngle * axis.y;

rotationMatrix[3] = cosDifference * axis.x * axis.y - sinAngle * axis.z;
rotationMatrix[4] = cosDifference * axis.y * axis.y + cosAngle;
rotationMatrix[5] = cosDifference * axis.y * axis.z + sinAngle * axis.x;

rotationMatrix[6] = cosDifference * axis.x * axis.z + sinAngle * axis.y;
rotationMatrix[7] = cosDifference * axis.y * axis.z - sinAngle * axis.x;
rotationMatrix[8] = cosDifference * axis.z * axis.z + cosAngle;

float originalMatrix[9];
originalMatrix[0] = _data[0];
originalMatrix[1] = _data[1];
originalMatrix[2] = _data[2];

originalMatrix[3] = _data[4];
originalMatrix[4] = _data[5];
originalMatrix[5] = _data[6];

originalMatrix[6] = _data[8];
originalMatrix[7] = _data[9];
originalMatrix[8] = _data[10];

_data[0] = originalMatrix[0] * rotationMatrix[0] + originalMatrix[3] * rotationMatrix[1] + originalMatrix[6] * rotationMatrix[2];
_data[1] = originalMatrix[1] * rotationMatrix[0] + originalMatrix[4] * rotationMatrix[1] + originalMatrix[7] * rotationMatrix[2];
_data[2] = originalMatrix[2] * rotationMatrix[0] + originalMatrix[5] * rotationMatrix[1] + originalMatrix[8] * rotationMatrix[2];

_data[4] = originalMatrix[0] * rotationMatrix[3] + originalMatrix[3] * rotationMatrix[4] + originalMatrix[6] * rotationMatrix[5];
_data[5] = originalMatrix[1] * rotationMatrix[3] + originalMatrix[4] * rotationMatrix[4] + originalMatrix[7] * rotationMatrix[5];
_data[6] = originalMatrix[2] * rotationMatrix[3] + originalMatrix[5] * rotationMatrix[4] + originalMatrix[8] * rotationMatrix[5];

_data[8] = originalMatrix[0] * rotationMatrix[6] + originalMatrix[3] * rotationMatrix[7] + originalMatrix[6] * rotationMatrix[8];
_data[9] = originalMatrix[1] * rotationMatrix[6] + originalMatrix[4] * rotationMatrix[7] + originalMatrix[7] * rotationMatrix[8];
_data[10] = originalMatrix[2] * rotationMatrix[6] + originalMatrix[5] * rotationMatrix[7] + originalMatrix[8] * rotationMatrix[8];
}
[/source]

Share this post


Link to post
Share on other sites
I'd seriously recommend breaking up your functions such that matrix multiplication is independent. Then implement your other functions in terms of that matrix multiplication. Keep in mind that matrix multiplication is non-commutative and the order that they are composed makes a difference.

Share this post


Link to post
Share on other sites
The code seems correct to me. The way you are composing the matrices, the operations will happen in the opposite order to what you specified.

I turned your code into something I could run (it would have been nice if you had done this):
[code]#include <iostream>
#include <cmath>

struct Vector3f {
float x, y, z;
Vector3f(float x, float y, float z) : x(x), y(y), z(z) {
}
};

struct Matrix4x4f {
float data[16];

void translate(const Vector3f& translation);
void rotate(const Vector3f& axis, float angle);
void loadIdentity();
};

void Matrix4x4f::translate(const Vector3f& translation) {
float x = data[0] * translation.x + data[4] * translation.y + data[8] * translation.z + data[12];
float y = data[1] * translation.x + data[5] * translation.y + data[9] * translation.z + data[13];
float z = data[2] * translation.x + data[6] * translation.y + data[10] * translation.z + data[14];
float w = data[3] * translation.x + data[7] * translation.y + data[11] * translation.z + data[15];

data[12] = x;
data[13] = y;
data[14] = z;
data[15] = w;
}

static const float radians_per_degree = std::atan(1.0f)/45.0f;

void Matrix4x4f::rotate(const Vector3f& axis, float angle) {
float angleInRadians = angle*radians_per_degree;
float cosAngle = std::cos(angleInRadians);
float sinAngle = std::sin(angleInRadians);
float cosDifference = 1.0f - cosAngle;

float rotationMatrix[9];
rotationMatrix[0] = cosDifference * axis.x * axis.x + cosAngle;
rotationMatrix[1] = cosDifference * axis.x * axis.y + sinAngle * axis.z;
rotationMatrix[2] = cosDifference * axis.x * axis.z - sinAngle * axis.y;

rotationMatrix[3] = cosDifference * axis.x * axis.y - sinAngle * axis.z;
rotationMatrix[4] = cosDifference * axis.y * axis.y + cosAngle;
rotationMatrix[5] = cosDifference * axis.y * axis.z + sinAngle * axis.x;

rotationMatrix[6] = cosDifference * axis.x * axis.z + sinAngle * axis.y;
rotationMatrix[7] = cosDifference * axis.y * axis.z - sinAngle * axis.x;
rotationMatrix[8] = cosDifference * axis.z * axis.z + cosAngle;

float originalMatrix[9];
originalMatrix[0] = data[0];
originalMatrix[1] = data[1];
originalMatrix[2] = data[2];

originalMatrix[3] = data[4];
originalMatrix[4] = data[5];
originalMatrix[5] = data[6];

originalMatrix[6] = data[8];
originalMatrix[7] = data[9];
originalMatrix[8] = data[10];

data[0] = originalMatrix[0] * rotationMatrix[0] + originalMatrix[3] * rotationMatrix[1] + originalMatrix[6] * rotationMatrix[2];
data[1] = originalMatrix[1] * rotationMatrix[0] + originalMatrix[4] * rotationMatrix[1] + originalMatrix[7] * rotationMatrix[2];
data[2] = originalMatrix[2] * rotationMatrix[0] + originalMatrix[5] * rotationMatrix[1] + originalMatrix[8] * rotationMatrix[2];

data[4] = originalMatrix[0] * rotationMatrix[3] + originalMatrix[3] * rotationMatrix[4] + originalMatrix[6] * rotationMatrix[5];
data[5] = originalMatrix[1] * rotationMatrix[3] + originalMatrix[4] * rotationMatrix[4] + originalMatrix[7] * rotationMatrix[5];
data[6] = originalMatrix[2] * rotationMatrix[3] + originalMatrix[5] * rotationMatrix[4] + originalMatrix[8] * rotationMatrix[5];

data[8] = originalMatrix[0] * rotationMatrix[6] + originalMatrix[3] * rotationMatrix[7] + originalMatrix[6] * rotationMatrix[8];
data[9] = originalMatrix[1] * rotationMatrix[6] + originalMatrix[4] * rotationMatrix[7] + originalMatrix[7] * rotationMatrix[8];
data[10] = originalMatrix[2] * rotationMatrix[6] + originalMatrix[5] * rotationMatrix[7] + originalMatrix[8] * rotationMatrix[8];
}

void Matrix4x4f::loadIdentity() {
for (int i=0; i<4; ++i) {
for (int j=0; j<4; ++j)
data[4*i+j] = (i==j);
}
}

Vector3f apply_to_point(Matrix4x4f const &m, Vector3f const &p) {
float v[4];
v[0] = p.x;
v[1] = p.y;
v[2] = p.z;
v[3] = 1.0f;
float r[4];
for (int i=0; i<4; ++i) {
float sum = 0.0f;
for (int j=0; j<4; ++j)
sum += m.data[i+4*j]*v[j];
r[i] = sum;
}
return Vector3f(r[0],r[1],r[2]);
}

// Maps the point (1,2,3) through matrix m and prints the result
void display(Matrix4x4f const &m) {
Vector3f p(1.0f, 2.0f, 3.0f);
Vector3f r = apply_to_point(m,p);
std::cout << '(' << r.x << ',' << r.y << ',' << r.z << ")\n\n";
}

int main() {
Matrix4x4f matrix;
matrix.loadIdentity();
display(matrix);

matrix.translate(Vector3f(100.0f, 200.0f, 0.0f));
display(matrix);

matrix.rotate(Vector3f(0.0f, 1.0f, 0.0f), 30.0f);
display(matrix);

matrix.rotate(Vector3f(1.0f, 0.0f, 0.0f), 45.0f);
display(matrix);
}

[/code]

Share this post


Link to post
Share on other sites
Thanks for the advice guys and thanks for taking the time to write all that code Alvaro... sorry I didn't post a runnable sample. Anyway, it turns out that it's definitely the order of my transformations that was screwing me up. I'll have to fix my multiplication. Thanks again.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this