Constraining IK Joints With Arbitrary Geometry
The following video demos a system for making geometry based constraints for a CCD solution, based on some of the notes and ideas presented by Jonathan Blow here. In the clip, an arm with 15 constrained ball joints tracks a target placed by the mouse. Each joint is constrained by a polygonal geometry approximating a circle, but any convex 2D shape could have been used.
IK links and constraints can be represented by the following simple structures.
struct Constraint {
std::vector<vec3> vertices;
};
struct Link {
float length;
float flexibility;
quat rotation;
Constraint constraint;
};
After a link’s transforms are set by an iteration of CCD, a link’s rotation
may violate the constraint and must be rotated back to the constraint primitive. In this scheme, the final and first vertexes of a constraint primitive are connected so that a constraint always forms a loop.
void constrain(Link& link) {
std::vector<vec3>& vertices = link.constraint.vertices;
for (size_t i = 0; i < vertices.size(); ++i) {
// Get the next position of the end of this link relative to the constraint
vec3 next = link.rotation * vec3(link.length, 0.0f, 0.0f);
// Project the new position onto a vector perpendicular to
// both this edge of the primitive and the primitive normal.
int j = (i+1)%vertices.size();
vec3 u = vertices[j] - vertices[i];
float insideness = dot(cross(vertices[i], u), next);
if (insideness < 0.0f) {
// Compute the closest point on the constraint boundary.
float a = dot(u, u);
float b = dot(u, next);
float c = dot(next, next);
float d = dot(u, vertices[j]);
float e = dot(next, vertices[j]);
float t = (b * e - c * d) / (a * c - b * b);
vec3 limit = vertices[j] + u * t;
// Rotate back to the limit
link.rotation = quat(next, limit) * link.rotation;
}
}
}
The closest-point math is the result of a pen and paper calculation and could stand a bit of optimization. I’ve left it the way it is to make it a bit easier to see what’s going on.
In the last line, the quaternion constructor quat(next, limit)
is the minimal rotation from the vector next
to limit
, something like what Blow presents near the end of his article.