0
\$\begingroup\$

I'm trying to implement a parent child relationship between Sprites and draw them to the screen. I'm calculating transforms by chaining transform matrices.

I'm using code from here:

    private void Draw(Texture2D texture, Matrix transform)
    {
        Vector2 p, s;
        float r;
        DecomposeMatrix(ref transform, out p, out r, out s);
        spriteBatch.Draw(texture, p, null, Color.White, r, new Vector2(texture.Width / 2, texture.Height / 2), s, SpriteEffects.None, 0.0f);
    }

    private static Matrix GetMatrix(Vector2 position, Vector2 scale, float rotation)
    {
        return Matrix.CreateScale(scale.X, scale.Y, 1f) * Matrix.CreateRotationZ(rotation) * Matrix.CreateTranslation(position.X, position.Y, 0f);
    }

    private static void DecomposeMatrix(ref Matrix matrix, out Vector2 position, out float rotation, out Vector2 scale)
    {
        Vector3 position3, scale3;
        Quaternion rotationQ;
        matrix.Decompose(out scale3, out rotationQ, out position3);
        Vector2 direction = Vector2.Transform(Vector2.UnitX, rotationQ);
        rotation = (float)Math.Atan2(direction.Y, direction.X);
        position = new Vector2(position3.X, position3.Y);
        scale = new Vector2(scale3.X, scale3.Y);
    }

And I'm drawing things like this:

        Matrix parentT = GetMatrix(parentPosition, parentScale, parentRotation);
        Matrix childT = GetMatrix(childPosition, childScale, childRotation);
        childT = childT * parentT;
        Draw(parent, parentT);
        Draw(child, childT);

This seems to be working fine except when the parent has a different scale than [1, 1]. For example, when the scale is [2, 1] and the child is rotating - the child gets scaled along the world X axis which causes it to look deformed:

Actual

The result I actually want is:

Desired

I still want the child to be affected by the parent's scale while taking the rotation into the account. I guess I could temper with the scale and apply it manually but that doesn't feel right. How to achieve this behaviour using matrices?

\$\endgroup\$
2
\$\begingroup\$

Change your Methods:

First you need to scale, so your model is still at the origin, but now has the right dimension. Then rotate it so the center still lies on the origin and the side are now rotated. Then translate the object to the place you want.

what you want to do is the following:

matrix child = scaleChild * scaleParent * rotateChild * translateChild * rotateParent * translateParent;
\$\endgroup\$
  • \$\begingroup\$ Funny thing is - I assumed this behaviour was something that also happens in real frameworks and engines. Just went to check in Unity and the same deformation occurs. At least this agrees with all tutorials about matrices where they say to just multiply parent's matrix with child matrix. \$\endgroup\$ – loodakrawa Feb 16 '16 at 13:09
  • \$\begingroup\$ Yes but the use case of such matrix multiplications is different. Engines want you to move the child to its final Position w.r.t the parent and then merge them to be one object. U have some independent rotations of your child. But i know this Problem. Easiest way would be to divide scaling as a prepass and the multiply all rotations and translations from parent \$\endgroup\$ – kaiser Feb 17 '16 at 5:58

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.