matrices and transformations

Started by
1 comment, last by Alberth 8 years, 1 month ago

Hello everyone,

I am trying to implement my own transformations in my engine. I got both translations and scaling to work but I can't seem to get rotation to work. I am new to matrices and transformations as a whole and this is my first attempt at them.

These are my 2d vector and 3x3 matrix structs and their multiplication operator functions.


union V2f32 {
    struct {
        f32 x;
        f32 y;
    };

    struct {
        f32 w;
        f32 h;
    };
};

struct Matrix {
    f32 a1, a2, a3;
    f32 b1, b2, b3;
    f32 c1, c2, c3;
};

inline V2f32 operator*(V2f32 lhs, Matrix rhs) {
    V2f32 temp = {(lhs.x * rhs.a1) + (lhs.y * rhs.a2) + (1 * rhs.a3),
                  (lhs.x * rhs.b1) + (lhs.y * rhs.b2) + (1 * rhs.b3)};
 
    return temp;
}

inline void operator*=(V2f32& lhs, Matrix rhs) {
    lhs.x = (lhs.x * rhs.a1) + (lhs.y * rhs.a2) + (1 * rhs.a3);
    lhs.y = (lhs.x * rhs.b1) + (lhs.y * rhs.b2) + (1 * rhs.b3);
}

inline Matrix operator*(Matrix lhs, Matrix rhs) {
    Matrix temp = {(lhs.a1 * rhs.a1) + (lhs.b1 * rhs.a2) + (lhs.c1 * rhs.a3),
                   (lhs.a1 * rhs.b1) + (lhs.b1 * rhs.b2) + (lhs.c1 * rhs.b3),
                   (lhs.a1 * rhs.c1) + (lhs.b1 * rhs.c2) + (lhs.c1 * rhs.c3),
                   
                   (lhs.a2 * rhs.a1) + (lhs.b2 * rhs.a2) + (lhs.c2 * rhs.a3),
                   (lhs.a2 * rhs.b1) + (lhs.b2 * rhs.b2) + (lhs.c2 * rhs.b3),
                   (lhs.a2 * rhs.c1) + (lhs.b2 * rhs.c2) + (lhs.c2 * rhs.c3),
                   
                   (lhs.a3 * rhs.a1) + (lhs.b3 * rhs.a2) + (lhs.c3 * rhs.a3),
                   (lhs.a3 * rhs.b1) + (lhs.b3 * rhs.b2) + (lhs.c3 * rhs.b3),
                   (lhs.a3 * rhs.c1) + (lhs.b3 * rhs.c2) + (lhs.c3 * rhs.c3)};
    return temp;
}

Here is the code for a rectangle I want to rotate 45 degrees.


        {
            V2f32 size = {32.0f, 32.0f};
            V2f32 pos = {50.0f, 100.0f};
            V2f32 vertices[4] = {{-size.x / 2, -size.y / 2},
                                 { size.x / 2, -size.y / 2},
                                 { size.x / 2,  size.y / 2},
                                 {-size.x / 2,  size.y / 2}};

            Matrix translation = {1.0f, 0.0f, pos.x,
                                  0.0f, 1.0f, pos.y,
                                  0.0f, 0.0f, 1.0f};

            f32 theta = 45 * (PI / 180);
            Matrix rotation = {cos(theta), -sin(theta), 0.0f,
                               sin(theta),  cos(theta), 0.0f,
                               0.0f,        0.0f,       1.0f};
            Matrix scale = {1.0f, 0.0f, 0.0f,
                            0.0f, 1.0f, 0.0f,
                            0.0f, 0.0f, 1.0f};

            Matrix transform = translation * rotation * scale;

            V4f32 colour = {1.0f, 0.0f, 0.0f, 1.0f};
            PushRect(&renderer, &transform, verticies, &colour, true);
        }

Renderer is an object that contains all the data needed for my renderer to render stuff. The last argument of PushRect(...) is filled and determines if the rectangle is filled or not.

This is a snippet from PushRect(...) which applies the transform on all the vertices, and sets the min and max value and uses those to find the center position and size of the rectangle for rendering.


    V2f32 min, max;

    for(i32 i = 0; i < 4; i++) {
        vertices[i] *= *transform;

        if (i == 0) {
            min = vertices[i];
            max = vertices[i];
        }

        if(vertices[i].x < min.x) {
            min.x = vertices[i].x;
        }
        else if(vertices[i].x > max.x) {
            max.x = vertices[i].x;
        }

        if(vertices[i].y < min.y) {
            min.y = vertices[i].y;
        }
        else if(verticies[i].y > max.y) {
            max.y = vertices[i].y;
        }
    }

    V2f32 size = max - min;
    V2f32 pos = min + (size / 2);

My code doesn't render the rotated rectangle yet it should render a filled axis aligned rectangle around the rotated rectangle, but it doesn't even do that. It renders the rectangle off the screen at (-35.35, 81.06) but should be rendering it at (100, 50). What am I doing wrong? I'm very confused because the translations and scaling work so why doesn't rotation work?

-- Mathew

Edit: Formatting.

Advertisement

Hey krusable,

If I am not wrong, the issue might be because of how you are applying the transformations.

The order of matrix multiplication is not commutative i.e:

A * B is NOT equal to B * A.

When you wish to apply scaling, rotation and translation (in that order) then the transformation must be as such:


vertices[i] = *transform * vertices[i];

as opposed to:


vertices[i] *= *transform;

Think of it as making sure that the multiplication happens in the order that you intend for it to. This way, scaling is applied first, followed by rotation and then translation.

To extend on the point made by Sai Narayan, rotation always rotates relative to the origin, rotating a line (100, 0)-(100, 10) in 2D by 90 degrees will end up at (0, 100)-(-10, 100), rather than (100, 0)-(90, 0).

In general, you always first translate the shape to the right spot relative to the origin, then rotate, and translate back.

This topic is closed to new replies.

Advertisement