Order of multiplication when using pc.Mat4().mul2

I’m having trouble getting my Mat4 multiplications to work correctly.

Basically what I’m doing is building a fence using hardware instancing.
So each board has its own rotation quad, positon vec3 and scale vec3.

What I do next is the same steps which you have in your example for hardware instancing, I’m creating a matrix, and using setTRS to properly position each instance.

// boards is just an array of all matrix data for my boards.
var instances = boards.length;
var matrices = new Float32Array(instances * 16);
var matrixIndex = 0;
var matrix = new pc.Mat4();

boards.forEach((board) => {
    matrix.setTRS(board.TRS.pos, board.TRS.rot, board.TRS.scale);
    for (let m = 0; m < 16; m++) matrices[matrixIndex++] = matrix.data[m];
} 

// And then I create a vertexBuffer and loop over meshinstances -
// just as in your example. This part works fine.

An illustration of the result is basically:
res1

My problem arises when I want to be able to rotate this fence.
Sometimes, for example. the fence might need to have this rotation:

My problem is that when I use matrix multiplication to achieve this, it is either rotated along some “origin” which is far away causing the entire fence to be “translated” far away but in a correctly rotationed manner.
Or every individual board is rotated on its local axis which isnt right either.

I’ve appended the forEach with the matrix multiplication and I hope you might be able to see where I’m going wrong. As I’m not sure exactly in what order the mul2() operation works.
Basically my goal is to revert the translation in the matrix, rotate the boards 90 degree along the Y axis, and then revert the translation via an inverse matrix.

var instances = boards.length;
var matrices = new Float32Array(instances * 16);
var matrixIndex = 0;
var matrix = new pc.Mat4();

boards.forEach((board) => {
    matrix.setTRS(board.TRS.pos, board.TRS.rot, board.TRS.scale);
    
    var rotationMatrix = new pc.Mat4().setFromEulerAngles(0,90,0);
    var negativePositionVector = board.TRS.pos.clone().scale(-1);
    var translationMat = new pc.Mat4().setTRS(
        negativePositionVector, 
        new pc.Quat(), 
        new pc.vec3(1,1,1)
    );
    var inverseTranslationMat = translationMat.clone().invert();
    var rotatedMatrix = new pc.Mat4().mul2(inverseTranslationMat, rotationMatrix);
    rotatedMatrix.mul2(rotatedMatrix, transMatrix);
    matrix.mul2(rotatedMatrix, matrix);


    for (let m = 0; m < 16; m++) matrices[matrixIndex++] = matrix.data[m];
} 

I’m assuming the mathematical order should now be the original inverseTranslationMat -> RotationMat -> translationMat -> orignalMatrix
Am I wrong ?

TL;DR
If I want to make a translation -> rotation -> revertTranslation.
In which order do I have to use pc.Mat4().mul2() in order to get it right ?

1 Like

Hi @maror757,

Calling @mvaligursky, he may know more on the subject.

I’d approach it the way a hierarchy in the Editor does it.
Build a parent matrix using setTRS, which defines position and rotation of the section of the fence.
and the build a matrix for each individual piece of the section, again using setTRS, where position and rotation would be relative to its parent. So likely no rotation (as section rotates) and only relative position offsets. The final matrix would then be (copy & paste from GraphNode._sync)

worldTransform.mulAffine2(_parent.worldTransform, localTransform);
2 Likes

That sounds like a smarter approach. I’ll try that!
That also answers my question of the order of multiplication of the arguments in a mul2() method.
The first argument will be the first matrix transformation, and the second argument will be the second transformation. Makes sense :slight_smile:

It worked much better. The fence still ended up a bit off-center after I performed its local transform. But it appeared to be because I focused the camera based on a bounding box calculation which was no longer applicable.

Thanks for the help @mvaligursky @Leonidas .

2 Likes