Jump to content
  • Advertisement
Sign in to follow this  
krusable

matrices and transformations

This topic is 1016 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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.

Edited by krusable

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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.

Edited by Alberth

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!