Matrix multiplication differs in 0.9?

Hi

I have been been working on refactoring a project from 0.8.5 to 0.9. Everything works except the matrix multiplication seems to differ from the old one.

My question is therefore, when you refactor this:

	mShapeToView.setToIdentity();
	mShapeToView.translate(Vec3f(0.5 * mViewSize));
	mShapeToView.scale(Vec3f(mViewScale, mViewScale, 1.0f));
	mShapeToView.translate(Vec3f(-mShapeOffset));
    mViewToShape = mShapeToView.affineInverted();

to this 0.9, shouldn’t it look like this?

mShapeToView = mat4(1); //Identity matrix
mShapeToView *= glm::translate(vec3(0.5f * mViewSize,0.0f));
mShapeToView *= glm::scale(vec3(mViewScale, mViewScale, 0.0f));
mShapeToView *= glm::translate(vec3(-mShapeOffset,0.0f));
mViewToShape = glm::affineInverse(mShapeToView);

That and and in my draw()
before it was:
gl::multModelView(mShapeToView);

now it’s:
gl::multModelMatrix(mShapeToView);

After having looked through the code so many times that I’ve lost track of the total number of times, I’m thinking the problem must lie somewhere in my matrix transformation, I’m just not sure where.
I do know that glm uses column major instead of row major but I believe that, that shouldn’t be part of the problem here.

You can find the project here:

if you want to look at the code, the matrix transformation happens in
TransformUI.cpp line 34:
and my push/pop from line 203 to 225.

Hi,

I can’t remember what the correct order of operations was when Cinder was still using its own Matrix44 class. What I do know, is that Cinder has now adopted the GLM library, which uses column major notation. The order in which you do transformations is, therefor, the following:

mat4 modelMatrix; // Creates an identity matrix.
modelMatrix *= glm::translate( vec3( 1.0f, 2.0f, 5.0f ) );
modelMatrix *= glm::scale( vec3( 2.0f ) );
modelMatrix *= glm::toMat4( glm::angleAxis( glm::radians( 30.0f ), vec3( 0, 1, 0 ) ) );

This will first rotate, then scale and finally translate the node. Another example is creating a combination of the model and view matrices:

mat4 modelView = viewMatrix * modelMatrix;

Does that help?

-Paul

Hi Paul

Thanks for answering, unfortunately that doesn’t help much. I know that GLM uses column major whereas matrix44f used row major. I have refactored that and it works according to the old code, it’s just when I zoom or am in a zoomed state as you can see in the gif below, it’s like the draggable anchor itself follows the movement of the mouse where the visual dots translate a lot more than they should. My suspicion is that the matrix does something wrong although I can’t say for sure. I find GLM a tad harder to debug too compared to matrix44f because it hides its values in so many tabs.

1 Like

Indeed, debugging GLM is a bit harder, mostly because it closely mimics the GLSL data structures. But I am pretty sure GLM’s code base contains fewer errors than Cinder’s old Matrix44.

Your GIF helps in understanding what might be wrong. To me it looks like an incorrect implementation of the drag functionality. Might be an incorrect offset or coordinate system conversion. Perhaps you applied scaling and forget to take it into account when dragging? Have you tried glm::inverse instead of glm::affineInverse? Perhaps you can share the code responsible for dragging the selected points? Edit: doh, you already shared that code. I’ll have a look.

-Paul

Hmm yes while it’s possible that the problem lies in the dragging functionality, I can’t see what should be wrong. I have been debugging the old code vs the new code and I seem to get similar results.

It’s likely more a problem with the coordinate system conversion but I’ve tried all kinds of combinations and looked through the glm documentation and nothing seem to differ anymore as far as I can see. As you have taken a look at the code yourself now, do you have an idea of what could be the culprit?

OK, so I managed to get your code up and running. I had to fix a few things (removed using namespace from the headers, removed the CinderExtensions.h include, upgraded to toolset v120 and fixed several warnings and compiler errors).

Most of the code stayed the same, with the exception of your VecExt class, which I presume is defined in the missing CinderExtensions.h. I replaced the code that uses VecExt with the GLM equivalent. For instance:

VecExt<float> vecExt;
vec3 vSrc = vecExt.transformVec( targetToSource, vec3( x, y, 1 ) );

became:

vec3 vSrc = targetToSource * vec3( x, y, 1 );

I was able to drag&drop an image file, set it to Bilinear projection and select one of the four edges. I could move the edge by dragging it without any problems when the “Zoom” slider was all the way to the left. When zoomed in, I was no longer able to select an edge or vertex, indicating that your coordinate system conversion was incorrect.

I tracked the error and it appears to be the use of glm::affineInverse() where you should really use glm::inverse() instead (so my initial guess was spot on, haha).

Also, just be careful when transforming positions using a mat4: make sure the w-coordinate is set to 1. For instance:

vec3 transformPoint = vec3( mViewToShape * vec4( pos, 0, 1 ) );

-Paul

1 Like

Oh weird, I actually tried that multiple times but then nothing responded at all. Hmm it may have been a combination of my custom functions plus that then. Well I’m certainly glad it’s been solved :smiley:
Also … the reason I used those custom function was because i didn’t know how you would do them in glm.

Thanks, you managed to clear up my mind on multiple things :wink:

In related news… I just came across a problem with glm::affineInverse(). I discovered that there is a bug in the (old) version of GLM that Cinder 0.9.0 uses (which is GLM 0.9.6.3). The bug occurs when there is non-unity scaling in the matrix, so this might have been part of your problem above. The bug was fixed in GLM 0.9.7.2 (see commit details here). As mentioned by Paul, if that’s what you were seeing, you should get the right result using glm::inverse() instead of glm::affineInverse(), although with a newer version of GLM I think it should work with either (as long as your transforms truly are affine, e.g. no shearing, perspective, etc).

2 Likes