Sign in to follow this  
aeroxr1

Rotate a bone, mathematical issues :(

Recommended Posts

Hi smile.png
 
I have a 3d rigged model and I'm trying to move his legs. I'm using jpct-ae and bones, but I think that my problems are in my mathematical approach :/
 
The steps to follow to rotate a bone are the following ?
 
1- we have to move from global pose to local pose (multiplying for the inverse bind pose)
2- after that we can apply rotation (or traslation) 
3- and after finally I have to move to global right (multiplying for the bind pose) 
 
Or I haven't understood ?
 
Because my results are wrong :
 
[url=http://imageshack.com/f/p9da95Fwp]da95Fw.png[/URL]
[url=http://imageshack.com/f/eydEVbMvp]dEVbMv.png[/URL]
[url=http://imageshack.com/f/pdnOOu3kp]nOOu3k.png[/URL]
 
wacko.png
 
for completeness this is the code that I used
currentPose = modello.get(0).getSkeletonPose();
skeletonDebugger = new SkeletonDebugger(currentPose,10f, (short)0);
skeletonDebugger.addToWorld(world);
skeletonDebugger.setVisibility(showskeleton);
Legrotation();
MemoryHelper.compact(); 
protected void Legrotation()
{
Logger.log("Legrotation"); 
final Joint LUpperArm  = currentPose.getSkeleton().findJointByName("Vincent L Thigh");
final Joint parentJoint = currentPose.getSkeleton().getJoint(LUpperArm.getParentIndex());
final Matrix global= new Matrix(LUpperArm.getBindPose()); 
float angle=(float)Math.toRadians(-90);
global.rotateZ(angle);
global.matMul(parentJoint.getInverseBindPose());
currentPose.getLocal(LUpperArm.getIndex()).setTo(global);
currentPose.updateTransforms();
skeletonDebugger.update(currentPose);
modello.applySkeletonPose();
modello.applyAnimation();


}
Thanks a lot smile.png
Edited by aeroxr1

Share this post


Link to post
Share on other sites

Now I apply this code and it seems works, but I don't know why -.-"

		protected void rotazionecoscia()
		{
			Logger.log("rotazionebraccio"); 
			
			final Joint LUpperArm  = currentPose.getSkeleton().findJointByName("Vincent L Thigh");
			final Joint parentJoint = currentPose.getSkeleton().getJoint(LUpperArm.getParentIndex());
			
			final Matrix global= new Matrix(LUpperArm.getBindPose()); 
			float angle=(float)Math.toRadians(-90);
			global.matMul(parentJoint.getInverseBindPose());
			global.rotateY(angle);
			currentPose.getLocal(LUpperArm.getIndex()).setTo(global);
			currentPose.updateTransforms();
			skeletonDebugger.update(currentPose);
			modello.applySkeletonPose();
			modello.applyAnimation();
			
		}

Share this post


Link to post
Share on other sites

It seems that you've changed two important things. The first one was the order of global.rotate and global.matMul calls, which may by crucial since matrix multiplication is not commutative. The second one was the axis of rotation (changed from global.rotateZ to global.rotateY), which may be connected with the correctness of orientation of 3D coordinate system that you've used.

Share this post


Link to post
Share on other sites

Hello.

One thing to note is if you get the leg transform its part of the hierarchy of bones.

So what happend to the bones above will affect this bone.

 

I don't think its working the leg got longer by that image by the looks of it.

Share this post


Link to post
Share on other sites

Hi smile.png
Thanks to the answer ;) I have found a code's working, but I would understand what the author do ;)

This is the code :

 

	public void transformJointOnPivot(Joint joint, Matrix mat) {
		Matrix bind = getJointBindMatrix(joint);
 
		SimpleVector pos = bind.getTranslation();
 
		// set the matrix to the origin
		pos.scalarMul(-1);
		bind.translate(pos);
 
		// apply the transformation (this may translate it as well of course)
		bind.matMul(mat);
 
		// place it back where it was
		pos.scalarMul(-1);
		bind.translate(pos);
 
		// TODO see if we need to do anything if the parent is not in its bind
		// position
		mulMatrixByInverse(getJoint(joint.getParentIndex()), bind);
		pose.getLocal(getJointIndex(joint)).setTo(bind);
	}

mulMatricByInverse do bind=bind * joint inverse bind pose matrix.

 

pose.getlocal(joint),setTo(bind) se te location trasform matrix relative of joint's parent to bind.

 

 

getLocal(int index) 
          Returns the joint transform in local space (relative to its parent).

 

 

But I don't understand why we have to do in this mode for moving the joints ..
 
1- Why we have to applicate the rotation after we set the matrix to the origin ? 
2- why we multiply this created matrix for the joint's parent's inverse bind pose matrix and not for the joint's inverse bind pose ?
3- why we set the local trasformation matrix to the created matrix ?
 
I think that I don't understand the math behind this trasformation :/
Edited by aeroxr1

Share this post


Link to post
Share on other sites

You must move the reference frame to the origin because applying a rotation matrix results in a rotation about the world origin. Consider: if you have a ball 3 feet away from the origin. If you apply a rotation in world-space, the ball will orbit the origin at a distance of 3 feet. Not only has it been rotated, but it's also been translated - it's not longer in the position it was before the rotation. If you first move the ball to the origin and apply a rotation about the origin, the ball rotates but does not translate.

 

In your code, you start with the bone's bind pose matrix - it's position in world-space. That matrix is comprised of the parent's world-space matrix (bind pose) times the bone's local matrix. That is, the bind pose moves the bone into it's parent's space, then moves the bone to it's position and orientation with respect to its parent.

 

To apply a rotation, you first translate the bone to the origin, and when the rotation is applied, the bone is rotated about it's axis but is not translated. The resulting matrix is now:

 

You start with a matrix (you call it bind) = bone-bind-pose = parent-bind * bone-local.

 

If you then translate it to the origin and apply the rotation, the matrix that results is:

 

Equation A. bind = parent-bind * bone-local * translate-to-origin * rotation.

 

In the hierarchy, each bone's local transform is with respect to it's parent. This line of code:

mulMatrixByInverse(getJoint(joint.getParentIndex()), bind);

results in:

 

matrix = parent-inverse-bind * bind.

 

If you substitute the equation for bind given in Equation A in the matrix equation immediately above the result is:

 

bind = parent-inverse-bind * parent-bind * bone-local * translate-to-origin * rotation. Note the emphasis.

 

Any matrix multiplied by its inverse is the identity matrix. So, parent-inverse-bind * parent-bind = Identity-matrix, so:

 

bind = Identity-matrix * bone-local * translate-to-origin * rotation.

 

Multiplication of any matrix by the Identity matrix results in that same matrix - i.e., multiplication by the Identity matrix has no effect.

 

So, bind = bone-local * translate-to-origin * rotation, and that's the new desired local orientation of the bone. It's then stored as the local matrix for the bone.

Edited by Buckeye

Share this post


Link to post
Share on other sites

Thanks for your time :) 

 

I have two question :

 

 

 

You must move the reference frame to the origin because applying a rotation matrix results in a rotation about the world origin. Consider: if you have a ball 3 feet away from the origin. If you apply a rotation in world-space, the ball will orbit the origin at a distance of 3 feet. Not only has it been rotated, but it's also been translated - it's not longer in the position it was before the rotation. If you first move the ball to the origin and apply a rotation about the origin, the ball rotates but does not translate.

 

I don't understand why before of the rotation do we have to traslate the ball into the origin.

Is not the same thing ? If the ball is 3 feet far away from the origin, and after the rotation the ball will be rotated and 3 feet far away from the origin, is not what we want to do ?  

 

 

bind = parent-bind * bone-local * translate-to-origin * rotation.

 

In the hierarchy, each bone's local transform is with respect to it's parent. This line of code:

mulMatrixByInverse(getJoint(joint.getParentIndex()), bind);

results in:

 

matrix = parent-inverse-bind * bind.

 

I don't understand the meaning of the last equation . The multiplication with parent-inverse-bind brings the bind matrix into the space of joints parent, right ?
But was not matrix bind already in joints parente space ?
 
Sorry for my bad english. I'm also trying to learn english =P 

Share this post


Link to post
Share on other sites

after the rotation the ball will be rotated and 3 feet far away from the origin, is not what we want to do ?

 

No. If the purpose of your code is to rotate a bone without translating it with respect to its parent. The following 2 rotations are not the same.

 

origin_rotation.png

 

 

 


The multiplication with parent-inverse-bind brings the bind matrix into the space of joints parent, right ?

 

You use the words "bind matrix" which isn't really correct. Your statement might be:

 

The multiplication with parent-inverse-bind brings the calculated matrix into the space of joints parent,..

 

But that's still not correct. The calculated matrix (before the matMulByInverse) calculates a new local matrix for the bone which mathematically still includes the parent's world orientation. I think you have confused yourself by calling it bind. You start with the bind matrix:

Matrix bind = getJointBindMatrix(joint);

But, after you translate it:

bind.translate(pos);

it is no longer the bone's bind matrix, it is parent-bind * bone-local * translate.

 

Although you call it bind, it is not the bone's bind matrix, and it still includes the world orientation of the parent bone.

 

To transform it into the bone's local space, mathematically, you must "remove" the world orientation of the parent bone by multiplying that matrix by the parent-inverse-bind. AND.. that multiplication must be done in the order given in the mulMatrixByInverse function. That order results in parent-inverse-bind * parent-bind as a portion of the calculation. That portion of the calculation is equivalent to the Identity matrix and, thereby, results in the "removal" of the parent's world orientation.

 

So, no, it does not "bring the bind matrix into the space of joints parent." it transforms the parent-bind matrix to form an Identity matrix.

 

It is a feature of matrices that operations such as scale, translate and rotation can be concatenated in a single matrix. It is often the case that the best way to understand a sequence of matrix multiplications is to write them out in a full sequence of multiplications, rather than trying to analyze each matrix that is formed within that sequence.

Edited by Buckeye

Share this post


Link to post
Share on other sites

Thanks !

You have been crystal clear smile.png

 

p.s : I want see If I really understand : the result would be the same if I multiplied inverse bind pose * "bind" before applying translation and rotation right ?

Edited by aeroxr1

Share this post


Link to post
Share on other sites

the result would be the same if I multiplied inverse bind pose * "bind" before applying translation and rotation right ?

 

I can't be sure which matrices you mean, or where in the sequence you would do that. It likely depends on whether that occurs before or after you've extracted the translation from the matrix.

 

In any case, did you try it? It would take maybe 15 seconds to change the code, another 15 seconds to compile and run it. It's been 30 minutes since you've asked the question and you could've determined the answer yourself 29-1/2 minutes ago.  happy.png

Edited by Buckeye

Share this post


Link to post
Share on other sites
mulMatrixByInverse(getJoint(joint.getParentIndex()), bind);

results in:

 

matrix = parent-inverse-bind * bind.

 

bind = parent-inverse-bind * parent-bind * bone-local * translate-to-origin * rotation. Note the emphasis.

 

bind = Identity-matrix * bone-local * translate-to-origin * rotation.

 

 

 

The problem is that "mulMatrixByInverse" function does this :

	private void mulMatrixByInverse(Joint ref, Matrix mat) {
		if (ref != null) {
			mat.matMul(ref.getInverseBindPose());
		}
	}

Thus the result is matrix = bind * parent-inverse-bind  and not matrix = parent-inverse-bind * bind .

We have tried to invert the matrices inside the function above and the result is wrong, the model's leg moves in the wrong way. 

Share this post


Link to post
Share on other sites

It appears that the various matrix multiplication functions implement multiplication in right-to-left order, rather than left-to-right as I had assumed. The mathematical principle remains the same.

 

So..

 

bind = rotation * translate-to-origin * bone-local * parent-bind

 

Following mulMatrixByInverse

 

bind = rotation * translate-to-origin * bone-local * parent-bind * parent-inverse-bind

 

With regard to generating the Identity matrix, the order par-bind * par-inv-bind or par-inv-bind * par-bind is immaterial. The end result is the modified local transform for the bone.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this