Special Thanks to Jordan Kohler. Thank you for helping me to solve the expressions and ultimately solving the trigonometry. I look forward to your future work.
This post discusses issues with a set driven key, finding the change in value of where an object was and where it is (called delta, so we’re finding delta), up vectors, expressions, and other issues. Also, this post shows the differences between a direct and indirect expression in Maya, and how to “trick” the expression.
The good news is the tank tread video demo has taken off a little bit, and I’m getting a decent response from the larger community. Unfortunately, there are a couple of unanswered questions. First, what is the up vector object doing? Second, how do I “drive” the tread “forward” on the path, regardless of the orientation of the path. After receiving a email from another CGSociety member, I decided to investigate what I had created much further. Before we get to the email, let me explain the use of up vectors in the Path Constraint tool.
My last video focuses solely on looping a motion path, but I brushed over the structure of the path constraint tool. More specifically, how the up vector prevents the individual treads from flipping.
Each object that is path constrained uses the same up vector object, and each motion path is set to use the World Up Type, “objection rotation up”. The key to this design is to follow a simple rule of rigging, use parent space.
Parent the up vector object to the object translating the curve.
Since the motion paths use “object rotation up”, it can be translated anywhere and parented into any hierarchy, so long as the up vector object in within the parent space of the translating object. The up vector object’s orientation is in control of each tread links orientation. So, if you parent the up vector object to something else, and rotate the curve (or objects controlling the CVs of the curve), the objects that are path constrained will “freak out”. This solution, short of making a surface constraint, is the most stable thing I have been able to make with Maya’s curve and motion path node. The next best thing is a surface constraint, like the Rivet tool, or like Cedric’s research here.
Fundamentally, there isn’t anything complicated about my setup. It’s just a good use of a parent/child space relationship over and over again. My path constraint appears fancy, but only because it automates a series of simple manual tasks. Now that you understand the basics of each tread links rotation, let’s look at the bigger problem outlined in the email I received. It reads:
I saw your tools for path constraints, specifically how you can apply them to build a tank tread. Turns out I’ve been working on a script specifically for making tank treads hehe, but I’m running into a problem that you seemed to have solved, I think. In your video, you show that you can have your tread rotate and still have the links move correctly around the path. I can’t seem to figure out a way to make this work, as you only want to track motion in the “forward” direction, and that direction changes if you rotate the objects. I’m also working with groups (because the parent’s transforms don’t affect the values of the child’s in its local space).
If you’re comfortable sharing your scripting ideas, would you mind explaining how you got that to work? I know you have quite a lot of code that involves calculating up vectors, but I’m just working with a simple case and using motion paths, so I don’t quite use the same methods. However, I figure calculating the forward motion shouldn’t matter.
Alright, let’s examine what they are asking for and how to solve this problem. Let’s start with:
In your video, you show that you can have your tread rotate and still have the links move correctly around the path. I can’t seem to figure out a way to make this work, as you only want to track motion in the “forward” direction, and that direction changes if you rotate the objects.
First, after investigating my tread scene, I discovered I have the same issues. When rotating the tread away from 0°, it gradually slows down to no movement at 90°, and a reversed movement at 180°. After a while I learned the issue is the world space values used to drive the set driven key. In other words, I used the translateX value of the curve translation object to drive the Offset value of the Path Constraint. This is a problem, because as I rotate an object, the channel box still only gives my the world space coordinates of the objects, meaning, it doesn’t respect the local rotation.
The solution: Create a new expression that finds the world space difference (delta) of where the translation object currently is and subtract that from where it was. Then solve for the rotation of the object. Then, accumulate these new values, and use it to drive the Offset value of the Path Constraint.
This introduces new problems. For example, an expression in Maya can be direct or indirect. An indirect expression uses MEL, like a script would use MEL, for instance,
- vector $v = `xform -q -ws -t object1`;
object2.translateX = $v.x;
This is indirect. Consequently, this will not evaluate interactively. The animation needs to be keyframed to see the result.
A direct expression looks like this:
- object2.translateX = object1.translateX;
This will evaluate every frame interactively, and when keyframe.
I’d like to write using the MELscript style, but need it to evaluate interactively.
The solution: Assign a direct expression on the first line of the expression, and write the remaining expression in the indirect method. This forces the expression to execute, and run through all lines of code.
As an expression it might look something like this: (This requires an object named “updater”, and main object name “d1” and helper object name “d2”. The d1 object should have new float attributes added, update, deltaX, deltaY, deltaZ, distanceX, distanceY, and distanceZ. Each delta attribute shows the numeric value of change, and each distance value shows the accumulated change.)
//the updater object is parent constrained to d1 //this is the direct expression, and forces the remaining code to execute d1.update = updater.translateX + updater.translateY + updater.translateZ; //current position vector $v1 = `xform -q -ws -t d1`; vector $v2 = `xform -q -ws -t d2`; //each axeses change float $dx = $v1.x - $v2.x; float $dy = $v1.y - $v2.y; float $dz = $v1.z - $v2.z; d1.deltaX = $dx; d1.deltaY = $dy; d1.deltaZ = $dz; //Trigonometry goes here. d1.distanceX += $dx; d1.distanceY += $dy; d1.distanceZ += $dz; //previous position d2.translateX = $v1.x; d2.translateY = $v1.y; d2.translateZ = $v1.z;
Optionally, without trigonometry, I was able to solve forward and reverse movement based on the local value change of the translating object. This version is oversimplified, but didn’t require trigonometry. The downside is that the tanks tread don’t animate automatically when rotating the entire rig.(This version requires new float attributes, previousZ, invertZ, delta, and distance.)
d1.update = updater.translateX + updater.translateY + updater.translateZ; //current position vector $v1 = `xform -q -ws -t d1`; vector $v2 = `xform -q -ws -t d2`; vector $v3 = $v1 - $v2; float $length = mag($v3); //find object space vector $v1Local = `xform -q -os -t d1`; //change this attribute to drive on x or y float $currentZ = $v1Local.z; float $prevZ = d1.previousZ; float $dZ = $currentZ - $prevZ; float $invertZ = `sign $dZ`; //each axeses change float $dx = $v1.x - $v2.x; float $dy = $v1.y - $v2.y; float $dz = $v1.z - $v2.z; d1.deltaX = $dx; d1.deltaY = $dy; d1.deltaZ = $dz; d1.distanceX += $dx; d1.distanceY += $dy; d1.distanceZ += $dz; //invert direction of value d1.previousZ = $currentZ; d1.invertZ = $invertZ; //distance based d1.delta = $length; d1.distance += ($length * d1.invertZ); //previous position d2.translateX = $v1.x; d2.translateY = $v1.y; d2.translateZ = $v1.z;
Download the example scene for more detail.
Finally, back to the email, last sentence:
I figure calculating the forward motion shouldn’t matter.
So we learned it does matter, when using the set driven key, Maya converts all translation to world space after rotating an object.