Matrix Transformations

So I am having a little trouble trying to get used to using matrices for scaling, rotation and translation in 2D space.

Usually I can just open the source and have a look over the code and see how it works, however given my limited knowledge of matrices (I am learning, slowly), then the code looks like complete gibberish!

So the problem:

I have my Box2D body and a fixture attached, since a lot of things will be dependent on the position of the body, such as Sprites or particles, I am copying the data of the body to a Transform class. The class looks like this. Now call me out straight away if something does not look right:


public class TransformComponent extends Component {
	public Matrix4 transform = new Matrix4();
	public Matrix4 scale = new Matrix4();
	public Matrix4 rotation = new Matrix4();
	public Matrix4 translation = new Matrix4();
	
	public TransformComponent(Vector2 position, float angle) {
		translation.setTranslation(position.x, position.y, 0);
		rotation.setToRotationRad(new Vector3(position.x, position.y, 1), angle);
		scale.setToScaling(new Vector3(1, 1, 1));
		transform = scale.mul(scale).mul(rotation).mul(translation);
	}
}

So I create each matrix and multiply them to get the final transform. Is this correct? If so, then onto the actual problem.

Here is the code that updates the entities transform component with that of the body:


TransformComponent t;
PhysicsComponent p;
t.rotation.setToRotationRad(new Vector3(p.getBody().getPosition().x, p.getBody().getPosition().y, 1), p.getBody().getAngle());
t.scale.setToScaling(1, 1, 1);
t.translation.setToTranslation(p.getBody().getPosition().x, p.getBody().getPosition().y, 1);
t.transform = t.rotation.mul(t.scale).mul(t.rotation).mul(t.translation);

This is called every frame, after the physics step and before drawing.

Here is the fantastic drawing code that you have probably seen a million times:


         DrawableComponent d;
         TransformComponent t;
         SizeComponent s;
			batch.draw(d.getSprite(), t.getTranslation().x, t.getTranslation().y, d.getSprite().getWidth() / 2, d.getSprite().getHeight() / 2, s.getWidth(), s
							.getHeight(), t.transform.getScaleX(), t.transform.getScaleY(), t.transform.getRotation(new Quaternion()).x);

First:

It does not rotate, the fixture hits the ground which is at an angle and rolls over, the sprite stays straight up.

Second:

The scaling does not work, at all. Any value other than 1 in any of the components of the vector results in nothing showing up.

Can someone kindly point me in the right direction for this? How can I achieve what I require? Knowing how to do this will really open my mind from the mathematics perspective, it will give me a good boost getting this working! :D.

Cheers.

EDIT: Fixed multiply order

EDIT: Just realised that setRotationRad actually rotates around the point, not changing the angle. Fail.

EDIT: No idea why I though this was a good idea scale.mul(scale) but heh, not the problem anyway :(.

EDIT SOLUTION/DIFFERENT APPROACH:

So I got a little tired messing around with this and decided to see if LibGDX’s SpriteBatch had a method that allowed me to directly pass in a Transform matrix and have it handle it all internally. Turns out it does not, however it has this nice little class called Affine2, which is a 3x3 Matrix.

So I changed my Transform class to this instead:

public class TransformComponent extends Component {
	public Affine2 transform = new Affine2();
	
	public TransformComponent(Vector2 position, float degrees) {
		updateTransform(position, degrees, new Vector2(1, 1));
	}
	
	public void updateTransform(Vector2 position, float radians, Vector2 scale){
		transform.setToTrnRotRadScl(position, radians, scale);
	}
	

}

Then I can just call the batches method like so:


batch.draw(d.getSprite(), s.getWidth(), s.getHeight(), t.transform);

Nice and simple. However, there is one issue.

Affine2 has 2 different methods for doing this.


Affine.setToTrnRotScl(Vector2, float, Vector2)
Affine2.setToTrnRotRadScl(Vector2, float, Vector2)

The one with Rad between Rot and Scl takes radians as an argument, while the other takes degrees. I tried the degrees one first and converted by Box2D body angle using:

body.getAngle() * MathUtils.radDeg

This just didn’t work, the source code looks fine to me but this causes some weird scaling issue. The sprite scales up as it gets away from the world origin. Even although it works fine for the radians method.

Just something quick because I have to go, but I was fairly sure you had to multiply trans * rot * scale to get the final transform. Order matters in matrix multiplication. I’m not the best with this stuff, but I gotta run.

As far as I know, the following should be correct:

ModelViewProjection = Projection * View * (Rotation * Translation * Scale)

Since you only need the model, its this:

Model = Rotation * Translation * Scale

So…


t.rotation.setToRotationRad(new Vector3(p.getBody().getPosition().x, p.getBody().getPosition().y, 1), p.getBody().getAngle());
t.scale.setToScaling(1, 1, 1);
t.translation.setToTranslation(p.getBody().getPosition().x, p.getBody().getPosition().y, 1);

t.transform.set(t.rotation);
t.transform.mul(t.translation);
t.transform.mul(t.scale);

This should work and if it doesn’t something else is wrong.
Have you tried to display the rotation/translation/scale as lines and text on your object?
That usually helps with debugging this sort of thing.

EDIT: What math-library are you using?

I fixed this problem, let me update the OP with the solution.

@Longor1996 I am using LibGDX, apologies forgot to tag.