5
votes

Using C++/OpenGL.

As an example, zooming camera at cursor position:

Vector3 target = GetScreenToWorldPosition(in_position);

Matrix4 s(MATRIX4_IDENTITY);
s.SetScale(0.12648); /// Arbitrary value for simplicity sake.

Matrix4 t(MATRIX4_IDENTITY);
t.SetTranslation(target);

Matrix4 tMinus(MATRIX4_IDENTITY);
tMinus.SetTranslation(-target);

Camera *camera = GetCurrentCamera();
Matrix4 matrix = camera->GetWorldMatrix();
matrix *= t * s * tMinus;

l_camera->SetScale(1, 1, 1);
l_camera->SetTranslation(?);
l_camera->SetRotation(?);

Is there a way to reset the scale while keeping the absolute translation/rotation?

Matrix4:
float m[16];

Row-Major:
m[00], m[01], m[02], m[03]
m[04], m[05], m[06], m[07]
m[08], m[09], m[10], m[11]
m[12], m[13], m[14], m[15]

m[00], m[04], m[08] = Cross vector
m[01], m[05], m[09] = Up vector
m[02], m[06], m[10] = Normal vector
m[12], m[13], m[14] = Translation vector

Based on rabbid76 suggestion:

Vector3 l_scale;

l_scale.x = sqrt(m[0] * m[0] + m[4] * m[4] + m[8] * m[8]);
l_scale.y = sqrt(m[1] * m[1] + m[5] * m[5] + m[9] * m[9]);
l_scale.z = sqrt(m[2] * m[2] + m[6] * m[6] + m[10] * m[10]);

m[0] /= l_scale.x;
m[4] /= l_scale.y;
m[8] /= l_scale.z;

m[1] /= l_scale.x;
m[5] /= l_scale.y;
m[9] /= l_scale.z;

m[2] /= l_scale.x;
m[6] /= l_scale.y;
m[10] /= l_scale.z;

It looks promising but so far, what used to be zooming in is now zooming out and vise-versa. Plus, there is some wobbling effects which indicates that something is not quite right.

2
Which data type is Matrix4? - Rabbid76
Is the issue solved? - Rabbid76
Like I've mentioned above. The transformations are not quite the same after resetting the scale. See anything out of the ordinary? - neosettler
Why dou you scale the translation? I didn't do that in my answer. - Rabbid76
Because I do not care about the matrix as being the final result but only its components, translation, rotation and scale. Basically, we should be able to create another matrix from these components with the scale being 1,1,1 and have the same world translation and rotation. - neosettler

2 Answers

3
votes

Knowing the matrix, you can calculate the scale for each axis. In the following cm is the matrix:

float scaleX = sqrt(cm[0][0]*cm[0][0] + cm[0][1]*cm[0][1] + cm[0][2]*cm[0][2]);
float scaleY = sqrt(cm[1][0]*cm[1][0] + cm[1][1]*cm[1][1] + cm[1][2]*cm[1][2]);
float scaleZ = sqrt(cm[2][0]*cm[2][0] + cm[2][1]*cm[2][1] + cm[2][2]*cm[2][2]);

If you want to "reset" the scale while keeping the absolute translation and rotation, you need to normalize the axis. The length of a normalized vector (Unit vector) is 1:

for (int i = 0; i < 3; ++i)
{
    cm[0][i] /= scaleX;
    cm[1][i] /= scaleY;
    cm[2][i] /= scaleZ;
}

If the scale for the 3-axes is identical, the result for scaleX, scaleY, scaleZ will also be identically. Hence, you can tweak the code and only calculate one scale.


Don't change the translation of the matrix. The fields 12-14 contains the absolute translation. If you change it, the cube "moves".

The matrix multiplication is not commutative. If you have 2 scale matrices and a translation matrix and multiply them in the following order:

m = scale2 * translate * scale1

Then translate is scaled with scale2, but not with scale1. Hence, you cannot reset the translation scaling, as this information will be lost. The matrix does not save the history of its creation.

0
votes

You can store the transformation as 3 vectors/matrices for translation, rotation and scale and create a matrix from them when needed:

void CreateMatrix(Matrix4 translation, Matrix4 rotation, Matrix4 scale, Matrix4& out) {
    out = scale * rotation * translation;
}

void Translate(Matrix4 translate, Matrix4& translation, Matrix4& rotation, Matrix4& scale) {
    translation *= translate;
}

void Rotate(Matrix4 rotate, Matrix4& translation, Matrix4& rotation, Matrix4& scale) {
    translation *= rotate;
    scale *= rotate;
    rotation *= rotate;
}

void Scale(Matrix4 scaling, Matrix4& translation, Matrix4& rotation, Matrix4& scale) {
    translation *= scaling;
    scale *= scaling;
}

To reset the scale, set the scale matrix to identity / the scale vector to zero.