Peter Gilmour
Peter Gilmour

Reputation: 97

3x3 Matrix Rotation with non uniform scaling

I am having some trouble dealing with matrix transforms. For the test I have a chain of 10 squares, each parented to the previous square. I can then apply a rotation to each square and get this result...

No scaling applied

This is correct, exactly what I want. Now here is the same thing with the 8th square scaled uniformly.

Uniform scaling applied

Again, this is perfect. The children are inheriting their parents transforms. Now the problem comes when I apply a non-uniform scale to the 8th square.

enter image description here

This picture is a subtle example, but I hope you guys get the idea. The last square is not touching the previous squares corner. It also may not be apparent but the last two squares are actually not the correct dimensions. This becomes more apparent if I apply more rotation.

Here is the code where I construct the matrix.

maths::mat3 DisplayObject::getGlobalTransform() {

    maths::mat3 output(1.0f);
    output *= maths::mat3::Rotate(rotation);
    output *= maths::mat3::Scale(scale);
    output *= maths::mat3::Translate(position);

    if(parent != nullptr){
        output *= parent->getGlobalTransform();
    }

    return output;

}

Any help or ideas will be greatly appreciated!

EDIT: Here is how I am applying rotations and scales.

mat3 mat3::Rotate(float angle)
{
    mat3 result(1.0f);

    float r = toRadians(angle);
    result.elements[0] = cos(r);
    result.elements[1] = -sin(r);
    result.elements[3] = sin(r);
    result.elements[4] = cos(r);

    return result;
}

mat3 mat3::Scale(const vec2& scale)
{
    mat3 result(1.0f);
    result.elements[0] = scale.x;
    result.elements[4] = scale.y;
    return result;
}

... and here is how I am getting them out again...

float mat3::GetRotation() const
{
    return atan2(elements[1], elements[0]);
}

vec2 mat3::GetScale() const
{   
    return vec2(rows[0].Magnitude(), rows[1].Magnitude());
}

Upvotes: 2

Views: 697

Answers (1)

Orace
Orace

Reputation: 8359

I tried this:

#include<GL/glut.h>
#include<math.h>
#define PI 3.14159265
#define M 50.0f

void display() {
    glClear(GL_COLOR_BUFFER_BIT);

    for (int i = 0; i < M; i++)
    {
        glTranslatef(1, 1, 0);
        glRotatef(25.0f, 0, 0, 1);
        glScalef(1.2, 1, 0);

        glColor3f(1.0f-i/M, i/M, 0.0f);

        glBegin(GL_QUADS);
        glVertex2f(0.0, 0.0);
        glVertex2f(0.0, 1.0);
        glVertex2f(1.0, 1.0);
        glVertex2f(1.0, 0.0);
        glEnd();
    }

    glFlush();
}

void myinit() {
    glClearColor(1.0, 1.0, 1.0, 1.0);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 250.0, 0.0, 250.0);
    glTranslatef(125, 125, 0);
}

void main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(501, 501);
    glutInitWindowPosition(0, 0);
    glutCreateWindow("59531834");
    glutDisplayFunc(display);

    myinit();
    glutMainLoop();
}

And it give the expected result:

Spiral of touching rectangles

The correct matrix order seams to be PTRS : Parent, Translate, Rotate, Scale.

Edit: PTSR works to.

So try this:

maths::mat3 DisplayObject::getGlobalTransform() {

    maths::mat3 output(1.0f);

    if(parent != nullptr){
        output *= parent->getGlobalTransform();
    }

    output *= maths::mat3::Translate(position);
    output *= maths::mat3::Scale(scale);
    output *= maths::mat3::Rotate(rotation);

    return output;
}

Upvotes: 2

Related Questions