The answer is this:
Jacobian needs to be calculated globally. For each joint in global coordinates rotation axis needs to be calculated as a cross product between joint-to-end and joint-to-target. The vector that goes into the Jacobian position field is a cross product between joint-to-end and rotation axis - it is a vector along which movement needs to be made (wikipedia under "angular velocity" names it: cross-radial component).
// For each joint (except end-effector) JointPosition = ToWorld(jointChainID[j]).Translation(); JointToEnd = EndPosition - JointPosition; JointToTarget = TargetPosition - JointPosition; // Calculate Rotation Axis RotationAxis = JointToEnd.Cross(JointToTarget); RotationAxis.Normalize(); // Cross-Radial Vector Result = JointToEnd.Cross(RotationAxis); // Jacobian column entry jacobian(0, j) = Result.x; jacobian(1, j) = Result.y; jacobian(2, j) = Result.z; jacobian(3, j) = RotationAxis.x; jacobian(4, j) = RotationAxis.y; jacobian(5, j) = RotationAxis.z;The result
dQcontains rotation angles for each joint alongRotationAxis(in global coordinates). Then I apply changes locally (this is weird, as I apply global changes to local joint coordinates, but somehow this works):// Apply changes to local skinning matrices // For each joint (except end-effector) jointID = JointChainID[j]; gTransform = Matrix::CreateFromAxisAngle(cache.RotationAxes[j], ToRadians(dQ(j))); SkinningLocal[jointID] = gTransform * SkinningLocal[jointID];