0
\$\begingroup\$

Let t1/2, r1/2 and s1/2 be two sets of transformations. Translations are vec3, rotations are quaternions and scales are vec3. Lets assume that all common operations are defined (overloaded operators in c++).

How do I combine (in what order) the two sets of transformations into a third set t3, r3 and s3, such that mat4(t1, r1, s1) * mat4(t2, r2, s2) == mat4(t3, r3, s3), without actually converting to matrices?

Mat4 is composed as M = T * R * S. (OpenGL conventions, if it makes any difference)

Thanks


My original code:

transform transform::operator * (const transform &other) const
{
    transform r;
    r.orientation = orientation * other.orientation;
    r.scale = scale * other.scale;
    r.position = position + (other.position * orientation) * scale;
    return r;
}

this used to work when scale was just one number (uniform scale). But it does not work with non-uniform scale. I guess that one of the scales have to be rotated, but I am failing to find the correct way.


This is how I test it:

for (uint32 round = 0; round < 10; round++)
{
    vec3 pa = randomDirection3() * randomRange(-5, 20);
    vec3 pb = randomDirection3() * randomRange(-5, 20);
    quat oa = randomDirectionQuat();
    quat ob = randomDirectionQuat();
    vec3 sa = randomRange3(real(0.1), 10);
    vec3 sb = randomRange3(real(0.1), 10);
    transform ta(pa, oa, sa), tb(pb, ob, sb);
    test(mat4(ta * tb), mat4(ta) * mat4(tb));
}
\$\endgroup\$
2
  • \$\begingroup\$ This is not possible in the general case when you have both non-90-degree rotations on the "first" transformation and non-uniform scale on the second transformation. This can result in a squash or stretch being applied diagonal to the coordinate axes, which a vec3 of axis-aligned scale values is insufficient to describe. See discussion in this Q&A for more details. Would you be open to adding a second quaternion to your transformation representation to store the basis for scaling? \$\endgroup\$
    – DMGregory
    Jan 20, 2019 at 14:07
  • \$\begingroup\$ Oh, I had not thought about that. Thank you very much. I will stay with just uniform scales. \$\endgroup\$
    – Tomas
    Jan 20, 2019 at 14:37

1 Answer 1

1
\$\begingroup\$

I believe this is how you do it:

transform transform::operator * (const transform &other) const
{
    // mat1 = T1 * R1 * S1; mat2 = T2 * R2 * S2
    // mat = mat1 * mat2; mat*v = mat1 * mat2 * v
    // assuming "this" is mat1, and other is mat2
    // alternatively "this" can be considered parent, and other child in a node hierarchy
    transform r;
    // R = R1 * R2
    r.orientation = orientation * other.orientation;
    // Note: I don't know how to implement inverse of quat in your lib
    // S = R2^-1 * (S1 * (R2 * S2))
    r.scale = inverse(other.orientation) * (scale * (other.orientation * other.scale));
    // T = T1 * (R1 * (S1 * T2))
    r.position = position + (orientation * (scale * other.position));
    return r;
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .