Need help calculating bone matrices

Started by sabaal, October 03, 2012, 20:35:53

Previous topic - Next topic

sabaal

I've got my mesh skinning engine nearly working; the shader associates each vertex with the correct matrix and transforms them properly. However, since a bone's offset it not applied to its matrix, all rotations pivot around the origin. I have bone offsets available to the code, but I can't figure out hot to apply them in such a way that they affect only the pivot point of the bone, without translating vertices attached to them. Here's my Bone class:

//==============================================================================
private class Bone {
	//----------------------------------------------------------------------
	
	private final Bone       PARENT;
	private final List<Bone> CHILDREN = new ArrayList<Bone>();
	private final String     NAME;
	public  final int        INDEX;
	
	//private boolean  use_physics = false; // For later
	private Vector3f offset;
	
	//----------------------------------------------------------------------
	
	private Bone( Bone parent, String name, int index ) {
		PARENT = parent;
		NAME   = name;
		INDEX  = index;
		if ( PARENT != null ) PARENT.addChild( this );
	}
	
	//----------------------------------------------------------------------
	
	public void addChild( Bone child ) { CHILDREN.add( child ); }
	
	//----------------------------------------------------------------------
	
	public void savePose( Pose p, Stack<Matrix4f> stack, FloatBuffer b ) {
		stack.push( stack.isEmpty() ? new Matrix4f() : new Matrix4f( stack.peek() ));
		
		Matrix4f temp_mat = new Matrix4f();
		Matrix4f.mul( p.get( NAME ), temp_mat, temp_mat );
		
		Matrix4f.mul( stack.peek(), temp_mat, stack.peek() );
		
		b.position( INDEX * 16 );
		stack.peek().store( b );
		
		for ( Bone c : CHILDREN ) { c.savePose( p, stack, b ); }
		
		stack.pop();
	}
	
	//----------------------------------------------------------------------
}
//------------------------------------------------------------------------------


savePose() is the method in question; Pose is a tiny class extending HashMap<String,Matrix4f> which contains a matrix for the rotation data associated with the name of each bone.

This will be the first time I've gotten a rendering engine to work with skinning, and this is pretty much the last hurdle. Google goes completely insane when I ask for code examples, so I've resorted to this. Thanks in advance.

abcdef

It's just about multiplication really, each bone has a parent bone. If the parent bone is null then its a root bone.

If your root bone is A and it has a TransformationA (a rotation and a translation (which represents the length of the bone pointing out at a specific angle)) and a bone that depends on it is B which has a TransformationB then TransformationB*TransformationA gives you the end point of bone B. If you had a further bone C then to get its end position you would need to do  TransformationC*TransformationB*TransformationA. Just continue until you have done all your calculations. You can of course optimise things but the basic principal is there.

sabaal

I think I got what I'm looking for.

Sorry abcdef, reading over my post now I realize it's not really clear what I'm asking for. As you describe, I have a method which traverses the skeleton and multiplies each bone's transform onto its parent's before saving to its position in the buffer. That's savePose() above. The problem was that, since the pose contains only bone rotations, the pivot point for all my transformations is at the origin, not at the base of the bone.

I already have the bone's position relative to its parent, stored in private Vector3f offset in each bone. Trying to apply it with translate() moved the vertices attached to the bone as well as the pivot. That's not what I wanted, because these offsets are all non-zero and will stretch the mesh horribly if used for translation.

About halfway through writing this post, I realized how to solve my problem: I needed to translate once to get the bone's base to the right location, perform my rotation, then translate again to put the vertices back in line. Here's the new savePose() method:

public void savePose( Pose p, Stack<Matrix4f> stack, FloatBuffer b ) {
	stack.push( stack.isEmpty() ? new Matrix4f() : new Matrix4f( stack.peek() ));
	
	stack.peek().translate( offset, stack.peek() );
	offset.negate();
	Matrix4f.mul( stack.peek(), p.get( NAME ), stack.peek() );
	stack.peek().translate( offset, stack.peek() );
	offset.negate();
	
	b.position( INDEX * 16 );
	stack.peek().store( b );
	
	for ( Bone c : CHILDREN ) { c.savePose( p, stack, b ); }
	
	stack.pop();
}


The offset.negate() lines alternate the translation vector between forward and reverse, so the second translate() fixes my vertices.

Some other unrelated bugs aside, everything seems to be working great now. Sorry to waste your time. :P