# combine two translation/rotation/scale triplets without matrices

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));
}

• 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? Jan 20, 2019 at 14:07
• Oh, I had not thought about that. Thank you very much. I will stay with just uniform scales. Jan 20, 2019 at 14:37

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;
}