I was unhappy with the controls I was giving the tentacles of our squid/octopus-looking monster. I decided to recreate them. This time, I’ll avoid as many constraints as I possibly could. And I’ll notate as I go through everything.
Right from the start, we wanted to give each tentacle an ability to pose in all sorts of ways. The control types that came to mind were either a spline IK or a ribbon controller. There are pros and cons for both.
The ik spline is awesome. It is widely used for lots of setups. I’ve seen cat tails and pony tails set up in this manner.
Essentially, ik splines (like most ik systems) handle the rotations of your joints. It handles them so that they would always fall exactly where your curve is.
My problem with using this for our tentacles is its lack of ability to twist in a detailed manner. If you wonder why CG systems haven’t developed enough to meet this requirement, you are asking the wrong question. The lack of proper twisting for spline IK’s makes sense. Splines, after all, cannot twist–just as a point contains no information of orientation and a line has no idea of where its flat surface may be.
This lack is solved by a ribbon setup. A ribbon, after all, is what you get when you extrude a spline in a direction giving it an ability to twist. A ribbon setup is also awesome. There’s a gazillion things you could do with a ribbon setup and it all depends on how much you control you want to have. In essence, you’ll have a bunch of joints or transforms follow specific points on a surface in position and orientation. If you twist that surface, those joints would all twist for you. And the surface can have as little as two controllers to make it bend, twist or whatever or just as many joints as you have, which is stupid. This of course depends on the amount of control you want to have.
The problem with the ribbon setup is, since joints would always follow specific points on the surface, when those surfaces extend too much, you’ll get stretching. Something you might want to have at times, but not all the time. This stretching can also occur for controllers you place in the middle of the surface. If it was a ribbon setup for the spine of a human character and you a middle controller somewhere in the abdomen, moving this controller up the chest would make the lower part of the abdomen stretch while crunching the part that goes into the chest. This might be important for toony animations but it does destroy realism for realistic characters. On the other hand, an IK spline, controlling nothing but rotations, won’t have this problem. The space between joints would remain the same all throughout the animation regardless of the surface’s stretching.
I wanted to combine both. I was thinking of doing it like how they combine IK and FK setups in an IK-FK blend, but it was obvious it wasn’t enough. If the ribbon setup is curling up a branch or a cylinder of some sort, and we switch to the IK spline, we’ll have the joints cut through the cylinder. It’ll be like an ugly morph where each point would just look for its corresponding target point and make a bee line towards it—a straight unrealistic, pass-through-anything-that-gets-in-the-way path. In the same way, if our tentacle has to stretch (or relax) it needs to be intelligent enough to do all this while in the space of the surface.
So the idea I had was to have a ribbon setup with each joint’s position depend on the UV coordinates provided by a blend between a constant and the UV coordinates that’s closest to a corresponding joint in an IK spline setup. Whew that was a mouthful. LOL!
To say that piece by piece, it goes: We have a ribbon setup. In that ribbon setup are joints that follow specific points on a surface. They know which specific points on that surface because of a UV input. If that input is constant, they’ll just follow that point on the surface regardless. But we’ll make that input a blend between two values. The first value would be the original constant, a value that would tell the joint its rightful point on the surface. While the second value the blend blends to is the closes UV point to a respective joint on an IK spline setup that also follows the surface.
That last bit is still a mouthful. It almost seems like a parallel story line that needs further breaking up. While we were working on the ribbon setup, there’s another set of joints. These joints are an exact replica of the original, except for their names obviously. These joints are connected to special nodes that try to find information for points on surfaces that are closest to positions. The information we are looking for are the UV coordinates these IK joints are closest to. By the way, we make sure this IK spline is following the surface exactly by duplicating an isoparm on the surface, with construction history kept on.
So that’s the idea. How’s it done? Well, I wrote a script for it.
The two procedures are:
proc jointsOnSurfaceToSplineRibbonBlend (string $jointList[], string $nurbsSurface, string $blendNode) {
//init
if (!`objExists "follicleGRP"`) createNode transform -n "follicleGRP";
if (!`objExists "iksGRP"`) createNode transform -n "iksGRP";
if (!`attributeExists "blend" $blendNode`) {
addAttr -ln "blend" -at double -min 0 -max 1 -dv 0 $blendNode;
setAttr -e-keyable true ($blendNode+".blend");
}
// make ik equivalent of joints
for ($i=0; $i < `size $jointList`;$i++) {
$dup = `duplicate -n ($jointList[$i]+"ik") -po $jointList[$i] `;
if ($i>0) parent $dup ($jointList[$i-1]+"ik");
else parent $dup "iksGRP";
}
// duplicate curve
$dupCurve =`duplicateCurve -ch 1 -rn 0 -local 0 ($nurbsSurface+".v[0.5]")`;
//create ik spline
string $ikHandle[];
select -cl;
$ikHandle = `ikHandle -curve $dupCurve[0] -sol ikSplineSolver -ccv false -pcv false -ns 3 -startJoint ($jointList[0]+"ik") -endEffector ($jointList[size($jointList)-1]+"ik")`;
//create follicles for each joint
for ($each in $jointList) {
$folAr = `createFollicle ($each+"ik") $nurbsSurface`;
// make those follicles become controlled by the blendNode
// note: we made sure that the createFollicle proc has the blendNode as the fourth (or index 3) element in the array return variable
connectAttr ($blendNode+".blend") ($folAr[3]+".attributesBlender");
parent $each $folAr[0];
parent $folAr[0] "follicleGRP";
}
// clean ups
}
proc string[] createFollicle (string $posTransform, string $nurbsSurface) {
// make vector product nodes to get correct positions of the transform node
$vecprod = `createNode vectorProduct`;
setAttr ($vecprod+".operation") 4;
connectAttr ($posTransform+".worldMatrix") ($vecprod+".matrix");
connectAttr ($posTransform+".rotatePivot") ($vecprod+".input1");
// connect the correct position to a closest point on surface node created
$cpos = `createNode closestPointOnSurface -n ($posTransform+"_CPOS")`;
connectAttr ($nurbsSurface+".ws") ($cpos+".is");
connectAttr ($vecprod+".output") ($cpos+".inPosition");
// create a follicle node and connect it
$folTransform = `createNode transform -n ($posTransform+"follicle")`;
$fol = `createNode follicle -n ($posTransform+"follicleShape") -p $folTransform`;
connectAttr -f ($fol+".outTranslate") ($folTransform+".translate");
connectAttr -f ($fol+".outRotate") ($folTransform+".rotate");
connectAttr -f ($nurbsSurface+".local") ($fol+".is");
connectAttr -f ($nurbsSurface+".worldMatrix[0]") ($fol+".inputWorldMatrix");
setAttr ($fol+".parameterU") (`getAttr ($cpos+".parameterU")`);
setAttr ($fol+".parameterV") (`getAttr ($cpos+".parameterV")`);
//create a blend between two attributes, a constant and the parameterU output of the cpos to control the follicle's parameter U
$blendU = `createNode blendTwoAttr`;
setAttr ($blendU+".input[0]") (`getAttr ($cpos+".parameterU")`);
connectAttr ($cpos+".parameterU") ($blendU+".input[1]") ;
connectAttr ($blendU+".output") ($fol+".parameterU");
//return strings
select -r $folTransform $fol $cpos $blendU ;
$list = `ls -sl`;
select -cl;
return $list ;
}
To use the code, you’ll need to have an unlimited version of Maya. Run those commands so they are registered in Maya’s brains. Then you will need a surface.

Make sure that this surface is longer in the parameter U of its UV coordinates rather than its V parameter. The script I wrote is designed to allow it to go both ways, but just for simplicities sake, I did it this way. Create joints on the middle surface and a control curve somewhere. I used snap to grid to make sure my joints are right at the center of the surface’s V parameter.

Then, run this command.
$jointList = {"joint1","joint2","joint3"}; // this is a list of the joints in order. You could choose to use 'ls –sl' for this if you want.
$nurbsSurface = "nurbsPlane1"; // name of the transform node of the surface
$blendNode = "nurbsCircle1"; // name of the control curve to which the blend will be added.
jointsOnSurfaceToSplineRibbonBlend $jointList $nurbsSurface $blendNode ; // this is the actual command with the variables added in proper order
You can set up clusters to control your nurbs surface or you could drag its control points just to test them out. Make the stretch and control as much as you want. Then change the values of the “blend” attribute you have in the nurbsCurve you created. Watch the joints (now no longer in a hierarchy) stretch and relax in the space of the surface.