JointTransform - Fish-In-A-Suit/Conquest GitHub Wiki

This class represents the local bone-space transform (in relation to parent joint) of a joint at a certain keyframe during an animation. This includes the position and rotation of the joint (bone-space; for the root joint it's relative to the model's origin, since root joint has no parent). The transform refers to the position and rotation of that joint. position is of type Vector3f, where rotation is represented as a Quaternion (much easier to interpolate between quaternion rotations than Euler rotations).

package animation;

import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector3f;

public class JointTransform {
	private final Vector3f position;
	private final Quaternion rotation;

	public JointTransform(Vector3f position, Quaternion rotation) {
		this.position = position;
		this.rotation = rotation;
	}

	protected Matrix4f getLocalTransform() {
		Matrix4f matrix = new Matrix4f();
		matrix.translate(position);
		Matrix4f.mul(matrix, rotation.toRotationMatrix(), matrix);
		return matrix;
	}

	protected static JointTransform interpolate(JointTransform frameA, JointTransform frameB, float progression) {
		Vector3f pos = interpolate(frameA.position, frameB.position, progression);
		Quaternion rot = Quaternion.interpolate(frameA.rotation, frameB.rotation, progression);
		return new JointTransform(pos, rot);
	}

	private static Vector3f interpolate(Vector3f start, Vector3f end, float progression) {
		float x = start.x + (end.x - start.x) * progression;
		float y = start.y + (end.y - start.y) * progression;
		float z = start.z + (end.z - start.z) * progression;
		return new Vector3f(x, y, z);
	}

}

The method getLocalTransform() returns the bone-space transform of the joint as a transformation matrix by translating an identity matrix using position and then applying the rotation. Rotration is applied by first converting the quaternion (rotation) into a rotation matrix, which is then multiplied with the transform matrix.

Method interpolate(JointTransform frameA, JointTransform frameB, float progression) can interpolate between two transforms based on the progression value. The result is a new transform which is part way between the two original transforms. The translation can simply be linearly interpolated, but the rotation interpolation is slightly more complex, using a method called SLERP to spherically-linearly interpolate between 2 quaternions. This gives a much better result than trying to linearly interpolate between Euler roations. Note that progression is a number between 0 and 1 indicating how far between the two transforms to interpolate. A progression value of 0 would return a transform equal to frameA, a value of 1 would return a transform equal to frameB. Everything else gives a transform somewhere in between the two.

Private method (used by the above method) interpolate(Vector3f start, Vector3f end, float progression) linearly interpolates between two positions based on progression value.