Calculating Wheel Rotation
Don't just find the distance traveled; find the local difference between two frames:
float $finAmount = ( (-(($theDist / $userCircum) * 360)) + $difValue);
The most common approach to calculating wheel rotation, which is also similar to the one I use, is a postscript that bakes rotation per frame based on the distance traveled from the last frame. However, this process has certain drawbacks; for example, the wheel going straight up (such as being raised on an elevator) or fishtailing sideways. In these instances, the wheel is not moving forward, so would not need to rotate. The solution that I've found is a bit more involved than just judging distance per frame; it involves judging the difference per frame instead (Fig.01).
Fig.01 - Wheel rotation example movie
Calculating the Orientation Distance Traveled
Once your wheel is animated traveling through space, you create a duplicate of your wheel on the current frame and another one frame ahead. Once these two duplicates are created, parent the first duplicate wheel under the second. The resulting tz, or corresponding translate axis, value of the first duplicated wheel is the difference in distance that will be used to rotate the wheel. Using this approach, you can determine the distance traveled with appropriate direction as well as reverse (Fig.02).
Though this process may seem confusing, you can see it working in this example:
• Make a wheel (cylinder) at origin (name it "wheelOrg")
• Duplicate wheelOrg, and name it "wheelDiff"
• For forward motion:
- Set wheelDiff.tz to 10
- Parent wheelOrg to wheelDiff
- The tz of wheelOrg is now -10
• For reverse motion:
- unparent wheelOrg from wheelDiff
- Set tz of wheelOrg to -10
- Parent wheelOrg to wheelDiff
- The tz of wheelOrg is 10
From this example, if you were only using the distance between the two points (or frames), you'd get the same value of 10. However, using the "difference" method, you get a value that will indicate direction changes. Repeating this example by plugging in a value of 10 in Translate Y of wheelDiff, you'll see that even though the wheel movds, the rotation would not be calculated because it didn't move on its 'forward' axis.
Finding the Circumference of the Wheel
Finding the circumference of a wheel is pretty straight forward (Fig.03):
• Create a default polyCylinder
• Align the cylinder to the wheel, but don't scale it, only use position and rotation
• Using the INPUTS of polyCylinder1, adjust the radius amount until they're the same size
• Use that radius amount in the following equation to find the circumference:
c = 2 * 3.14159 * radius Amount
Using the Distance Traveled Value to Drive Wheel Rotation
Having calculated the proper distance traveled, you'll need to gather the current rotation value of the wheel:
float $curAmount = `getAttr Wheel_Spin.rx`;
Now that you have the circumference, proper distance amount, and current rotation value, you can plug them into this equation:
float $finAmount = ( (-(($theDist / $userCircum) * 360)) + $curAmount);
What this equation is saying is: take the distance traveled, divide it by the circumference, multiply it by 360 (to put it into degrees), inverse it, then add it to the existing rotation amount. From here you'd set the $finAmount to the rotation channel, then set a keyframe:
setAttr Wheel_Spin.rx $finAmount;
After that, you'd loop through all the frames, repeating this process. Since that would be pretty labor intensive, here's a MEL script to do it for you Download (Fig.04).
To use the script, go to Window > General Editors > Script Editor. In the Script Editor, go to File > Source Script and then run the following command:
Once the window opens, define the wheel object by selecting it, then pressing the < button. Fill in the remaining fields, set the timeline to the desired length and hit the Calculate button. If it all works out correctly, the script should run through the timeline and bake out the values.
Tips and Tricks
1. If you plan to use this process for a car, you'll need to calculate a new value per wheel, as each wheel travels slightly different amounts per frame. A slight modification to the script would allow you to do this without having to go through the timeline for each wheel.
2. Finding the circumference can be tedious and unnecessary to do each time you wish to run the script. If you find the circumference once, create a new channel on the wheel control and put the circumference value on that channel. Again, a small modification to the script to look for that channel and use the value could save you additional time and streamline the process.
3. When setting up a rig for a car, keep autoWheelSpin (or whatever channel you wish to drive the wheel rotation) as a separate channel. This would allow an animator to override or remove it if the shot calls for it. The creation of two channels: autoWheelSpin and manualWheelSpin would be added together to allow animators explicit control over the wheel.
4. For this process to work, you'll need the wheel transform to actual move per frame, not deform, as the calculation is still based off the pivot. If wheel is deforming, use the wheel control instead to find the distance.
Overall, calculating realistic wheel rotation is tricky, since a wheel has roughly four states: stopped, rolling, speeding up and cruising. Obviously, if the car is stopped, finding the rotation isn't necessary. Speeding up and cruising rotation values may need to be adjusted regardless, if the blur on the wheel starts to "wagon wheel" (spinning so fast the motion blur makes it look like its spinning slowly backwards) and becomes distracting. So that leaves rolling, a fairly rare shot in production. If you have a rolling shot, this process would be used (Fig.05).
Another factor is speed and scale. Speed is not always accurate in 3D, usually because the scene or assets aren't built to real world units or travel realistic distances.
When all's said and done, if your animation requires a precise rotation, the above approach should get you there. If it doesn't, than eyeballing the rotation values should be enough to keep you moving forward.