0
\$\begingroup\$

I'd like to know if it's possible to calculate a child's world translation, rotation and scale individually. I'm used to seeing devs resorting to matrix multiplication and then decomposition, like so:

// In the child's scene component
glm::mat4 const& childWorldMatrix = m_parent->GetWorldMatrix() * m_localTransform.GetMatrix();
glm::decompose(childWorldMatrix, worldScale, worldRotation, worldTranslation, skew, perspective);

But I was wondering if we could do it individually to avoid decomposition. This is my code but it's not correct, especially the rotation part which I believe to be "scaled".

glm::mat4 const& parentWorldMatrix = m_parent->GetWorldMatrix();
m_worldTransform.SetTranslation(parentWorldMatrix * glm::vec4(m_localTransform.GetTranslation(), 1.0f));
m_worldTransform.SetRotation(glm::toQuat(parentWorldMatrix * glm::toMat4(m_localTransform.GetRotation())));
m_worldTransform.SetScale(parentWorldMatrix * glm::vec4(m_localTransform.GetScale(), 1.0f));

The translation and scale getters/setters return/set a glm::vec3 while the rotation uses glm::quat.

What would be correct way to calculate the world components individually?
Is matrix decomposition the preferred approach?

PS: I do have access to the individual components of the parent's world transform (world translation, world rotation and world scale), not just the matrix.

\$\endgroup\$
0
\$\begingroup\$

What you are doing here looks wrong to me, even just from the fact that the .SetTranslation and .SetScale are both essentially the same operation (you multiply the parentWorldMatrix to a vec4 for both when they are very distinct things).

In order to do what you want, I think you'd have to take each component of the m_localTransform and handle it appropriately using the m_parent's corresponding transform components with an exception for Translation.

m_wordTransform.SetRotation(glm::toQuat(parentWorldTransform.GetRotation() * m_localTransform.GetRotation()));
m_wordTransform.SetScale(glm::vec4(parentWorldTransform.GetScale() * m_localTransform.GetScale(), 1.0f));
m_wordTransform.SetTranslation(parentWorldTransform.GetTranslation() * parentWorldTransform.GetRotation() * parentWorldTransform.GetScale() * m_localTransform.GetTranslation());

Note: I think the glm code is not proper, some parts should be converted to vectors and others to whole matrices, not sure what the getters are returning.

The first two are simple to understand, the child component's world scale will be its own scale multiplied by the parent's scale. Same with rotation, multiplying the two rotation matrices generates the combined matrix that contains the local rotation of the child which is then further rotated by the parent rotation value. The trick here is with the translation of the child. Translation is actually affected by the other transformations, both rotation and scale. This is explained in linear algebra textbooks, if you want me to go further I can.

The point is that if you have a translation of \$y=1\$ and you apply a scale transform onto it, now your new \$y\$ position will be \$y=2\$ which means that the object has "moved" just because it was rescaled. For this reason, to properly generate the translation part of the worldMatrix you need to multiply the local translation with all of the parent's components and specifically in the order \$TranslateRotateScale\$ or \$TRS\$ for short, if you are multiplying from the left (i.e. applying scale first then rotation then finally the parent's own translation). You must not multiply the local translation vector with the whole parent transform! This generates wrong values.

\$\endgroup\$
4
  • \$\begingroup\$ I thought of that but I didn't go through with. Plus, I think there are some issues with your code as well. 1. On the translation code, can you just add them? What about the parent's rotation? 2. The scale should be a multiplication not an addition. And again, shouldn't the parent's rotation affect it as well? \$\endgroup\$ Oct 15 at 12:08
  • \$\begingroup\$ About the scale, you are right, I copy pasted the code from the translation part and failed to change it. Regarding translation, as you can see from the code I'm retrieving the parentWorldTransform.GetTranslation() which I'm guessing your library will return a vec3 type object which has the + operator overloaded to do proper addition between vec3s. The parent's rotation doesn't come into play since we specifically use the translation part of the parent transform. Does this make sense? \$\endgroup\$
    – PentaKon
    Oct 15 at 12:19
  • 1
    \$\begingroup\$ I'm still not convinced on the translation part. Imagine a right handed coordinate system with Y up. The parent has a rotation of -90° on the Z axis. The child has a translation of 1 unit in the Y axis. You'd expect the child to be 1 unit in the X axis in world coordinates due its parent's rotation but with your code it would still be 1 unit in the Y axis. \$\endgroup\$ Oct 16 at 8:27
  • \$\begingroup\$ The example you gave woke me up. I think my updated answer is correct. \$\endgroup\$
    – PentaKon
    Oct 18 at 10:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.