Success!

After five or six weeks of evenings and weekends, and at least as many iterations, the front leg is rigged, and available on my GitHub page.

The leg is driven by a familiar IK/FK control scheme. What’s under the hood may be unfamiliar to some. You see, there’s neither joints nor IK handles in the leg rig. Instead, there is math – trigonometry to be precise. You can use the Law of Cosines to solve the angles of a simple two bone IK chain. For more details, refer to season 01 of Raffaele Fragapane’s Cult of Rig video series.

The IK/FK setup produces four transform matrices – hip, knee, ankle, foot – that need to drive the fourteen mechanical parts of the leg. Each part represents a single axle of rotation. In my scene I referred to the rotations as Axle, Hinge, and Swivel.

My first few attempts, I approached solving the leg in a top down manner, solving each piece in turn from the hip down to the foot. This would work up until a point – usually the ankle. Eventually I got a “working” rig by cheating and making a piece in the ankle “stretch”. In a bit of premature celebration, I posted a GIF to Twitter showing off the results. Five minutes later, when moving another control, everything broke.

I scrapped the solvers and started fresh. This time, I discovered that I have made a faulty assumption about a piece in the lower limb, which gave me another hinge rotation. With a nudge from Jørn-Harald Paulsen, one of the few people I know of to successfully rig this cat, I also came to the conclusion that since the ankle has no piston components (and therefor cannot “stretch”) that I should solve from the bottom up.

It took a couple iterations of the bottom up approach to yield a working leg. Along the way I half remembered, half discovered how to project a vector so it intersects a plane. This technique isn’t used in the final iteration, but it was useful for getting stable up vectors for some rotations. I also figured out a trick with aimConstraints for L shaped axles – more on that in another post.

Eventually, I got it working. Every FK works in all three axes, and any combination thereof. The only flipping I am aware of is in the knee FK control when rotateY is greater than +/- 180 degrees, and the ankle FK control when rotateX is less than -90 degrees, but both of those are model as they cause components to collide. Plus, the ankle issue can be fixed by flipped the aimVector on the constraint, which I can fix by adding a toggle on the control, or trying to add something to detect the flip and correct for it.

Moving forward, I can either mirror the setup to the right leg, or start working on the rear leg. The rear leg looks like it’s the same mechanisms as the front, so either way, it’s just duplicating the setup. Or I could work on the spine and/or neck.

Either way, the challenge I’m going to face going forward is scene organization. I’ve been doing all my work in the Node Editor, putting each isolated rotation in its own tab. This keeps the pieces simple and easy to reason about. The largest graph – the IK “solver” – is 59 nodes. Most of the rotation components are 20 to 30 nodes. But this means I have almost twenty tabs open in the Node Editor for just the front leg. Since I can’t group tabs into folders, I’ll rely on the bookmarks feature to save tabs. However, you can’t organize your Node Editor bookmarks. I don’t think the product designers ever envisioned someone using the Node Editor in this way.

I’m really interested in your mention of intersecting a vector with a plane – were you able to do this with only the maths nodes? I’ve never cracked how to solve for it, and the best alternative I’ve found is curveIntersect nodes, for which I feel shame every day.

LikeLike

My solution to intersecting a vector with a plane involves using a transform matrix as a stand in for the plane. Given a point and a ray vector, the point of intersection with either the YZ, XZ, or XY plane is found by re-sizing the ray vector so it’s length is equal to the X, Y, or Z component of the start point respectively. This calculation must be done in local space; I use the vectorProduct node to get the point and ray vector local to the plane (matrix).

Hope that helps!

LikeLike