Download createTreadsOnCurve.mel.
//******************************************************************************************************************* // File name: createTreadsOnCurve.mel; // Author: Tony Kaap 05 May 2006 // // Copyright belongs the author. Permission to use and modify is freely given. // This is for educational use. Commercial use is freely allowed, but at your own risk. // // // createTreadsOnCurve // This script easily and flexibly attaches evenly-spaced copies of input geometry to an input // curve, and animates them flowing along the curve. Its primary purpose is to create tracks // for tracked vehicles like tanks, tractors, and off-road crawlers. // // This is the actual work function, but the helper functions at the bottom allow you to call // the function with one fewer parameter to remember. // // $aim == 1 -create aim constraints // $aim == 0 -do not create aim constraints proc createTreadsOnCurveDoIt( string $curve, string $srcObj, int $ct, int $time, int $aim ) { float $start, $end; float $step; // create the control wheel object string $control[] = `polyPipe -r 3 -h 1 -t 0.5 -sa 20 -sh 1 -sc 1 -ax 1 0 0 -ch 0 -n "controlWheel"`; setKeyframe -t 1 -v 0.0 ($control[0] + ".rx"); setKeyframe -t $time -v 360.0 ($control[0] + ".rx"); setAttr ($control[0] + ".doubleSided") 0; setAttr ($control[0] + ".visibleInRefractions") 0; setAttr ($control[0] + ".visibleInReflections") 0; setAttr ($control[0] + ".smoothShading") 0; setAttr ($control[0] + ".primaryVisibility") 0; setAttr ($control[0] + ".motionBlur") 0; setAttr ($control[0] + ".receiveShadows") 0; setAttr ($control[0] + ".castsShadows") 0; // This sets the initial animation curve on the control object to "cycle with offsets" string $cSrc; if (`connectionInfo -isDestination ($control[0] + ".rx")`){ $cSrc = `connectionInfo -sourceFromDestination ($control[0] + ".rx")`; $cSrc = match("[^\.]*", $cSrc + "\n"); selectKey -add -k $cSrc; setInfinity -pri cycleRelative -poi cycleRelative; } // Create an empty group to drop all of the locators into. // This keeps the Outliner clean and usable string $locatorGroup = `group -empty -n ($control[0] + "Group")`; // Lock the channels of the locator group so that they don't get moved accidentally. setAttr -lock true ($locatorGroup + ".tx"); setAttr -lock true ($locatorGroup + ".ty"); setAttr -lock true ($locatorGroup + ".tz"); setAttr -lock true ($locatorGroup + ".rx"); setAttr -lock true ($locatorGroup + ".ry"); setAttr -lock true ($locatorGroup + ".rz"); setAttr -lock true ($locatorGroup + ".sx"); setAttr -lock true ($locatorGroup + ".sy"); setAttr -lock true ($locatorGroup + ".sz"); // Setup work for driving the tread segment animation through complete rotations of the control wheel. $start = 0; $end = 360.0; $step = 360.0 / $ct; print ("Number of pieces: " + $ct + "\nRotational distance per piece: " + $step + " degrees\n"); int $i; string $locators[]; // The newly-created locator string $locatorList[]; // A list of all of the locators created (used later for aim-constraining) string $objs[]; // The newly-created copy of the source segment geometry for ($i = 0; $i < $ct; $i++) { // Create and select a new locator. $locators = `spaceLocator`; select $locators[0]; $locatorList[size($locatorList)] = $locators[0]; // Add this locator to the list of locators select -add $curve; // Attach the new locator to the curve by using a motion path. Selection order is important. string $path = `pathAnimation -fractionMode true -follow true -followAxis z -upAxis x -worldUpType "objectrotation" -worldUpVector 1 0 0 -worldUpObject $curve -inverseUp true -inverseFront false -bank false -startTimeU $start -endTimeU $end $curve $locators[0]`; // Create a new transform to parent to the locator. string $newGeometryGroup = `group -empty -n ("geomGroup")`; // Since there is a node named "geomGroup" for each locator, we need to add the "|" character to specify // that we want to operate on the only one at the root level of the scene heirarchy. $newGeometryGroup = ("|" + $newGeometryGroup); // Create a new copy of the source geometry. $objs = `duplicate $srcObj`; // Parent the geometry underneath the transform. parent -r $objs[0] $newGeometryGroup; // Parent the transform underneath the current locator. // This allows us to aim constraint the transform while permitting easy replacement of the geometry. parent -r $newGeometryGroup $locators[0]; // This removes the initial animation from the "attach to motion path" command, then sets driven keys // from the control object string $source; if (`connectionInfo -isDestination ($path + ".uValue")`){ $source = `connectionInfo -sourceFromDestination ($path + ".uValue")`; $source = match("[^\.]*", $source + "\n"); // grab just the object name. delete $source; // delete the time-driven animation curve; // Set up keys driven by the "control wheel" to drive the segments along the motion path. setDrivenKeyframe -currentDriver ($control[0] + ".rotateX") -dv $start -v 0 ($path + ".uValue"); setDrivenKeyframe -currentDriver ($control[0] + ".rotateX") -dv $end -v 1 ($path + ".uValue"); }else{ // Execution should never reach this point in the code. print ($path + " is not a destination of a connection\n"); } // This sets up the $source variable to point to the new driven key animation curve if (`connectionInfo -isDestination ($path + ".uValue")`){ $source = `connectionInfo -sourceFromDestination ($path + ".uValue")`; $source = match("[^\.]*", $source + "\n"); // grab just the object name. // This sets the driven key animation curve to cycle properly selectKey -clear; selectKey -add -k $source; setInfinity -pri cycle -poi cycle; } // increment the counters so the next locator is offset properly along the motion path $start += $step; $end += $step; // put the new locator in the locatorGroup parent $locators[0] $locatorGroup; } // If enabled by the $aim flag, iterate through the list of locators created, and create an aim constraint for // the geomGroup underneath each one. This construction allows for replacement of the geometry without having // to re-create the aim constriants for each node. if( 1 == $aim ) { for($i = 0; $i< $ct; $i++) { int $prev = ($i>0 ? $i-1:$ct-1); // set the index for the previous locator in the tread. string $curLoc, $curLocGeomGrp, $prevLoc; $curLoc = $locatorList[$i]; $prevLoc = $locatorList[$prev]; // construct the path through the right locator to the right 'geomGroup' node; $curLocGeomGrp = ($locatorGroup+"|" + $curLoc + "|geomGroup"); // create the aim constraint aimConstraint -offset 0 0 0 -weight 1 -aimVector 0 0 1 -upVector -1 0 0 -worldUpType "objectrotation" -worldUpVector 1 0 0 -worldUpObject $curve $prevLoc $curLocGeomGrp; } } // Clear any animation curves and keyframes from the selection. selectKey -clear; // End the tool with the controlWheel selected. select $control[0]; // Create an arc length measure tool, and place it at the end of the curve. string $name = `arcLengthDimension ($curve+ ".u[1]")` ; // Read the length of the entire curve. float $arcLength = `getAttr ($name + ".arcLength")`; // Divide that length into segment sizes. float $arcLengthPiece = $arcLength / $ct; // Output the results to the Script Editor. print ("Curve length: " + $arcLength + " - Length per piece: " + $arcLengthPiece + "\n"); // Remove the arc length measure tool. delete $name; } // Description: // This script quickly and easily duplicates and rigs geometry to move around a looped // NURBS curve in the manner of a tracked vehicle like a tank. // // $curve - string - name of the input curve // $srcObj - string - name of the input geometry // $ct - int - number of segments you want the treads to have // $time - int - number of frames it takes to have one segment of the treads make a full cycle of the curve. // // Command Format: createTreadsOnCurve curve object count cycleDuration; // Example: createTreadsOnCurve curve1 pPyramid1 100 400; // global proc createTreadsOnCurve( string $curve, string $srcObj, int $ct, int $time) { createTreadsOnCurveDoIt( $curve, $srcObj, $ct, $time, 1); } // Helper function to create treads that only use the curve's normal direction // This will result in the geometry on the curve pointing in the same direction as the locators global proc createTreadsOnCurveNoAim( string $curve, string $srcObj, int $ct, int $time) { createTreadsOnCurveDoIt( $curve, $srcObj, $ct, $time, 0); } //*******************************************************************************************************************