2 things: in mGear, look into gimmick joints, and make a post run to rebuild them after every iteration. These are good for twisting.
Where you’ll really get some interesting stuff is using ribbons. General idea is to turn a nurbs plane into an nHair deformer, and parent the joints under the follicles. Then use those joints to drive the deformer joints. With this set up, you can use any of the nonlinear deformers on a seperate nurbs plane ribbon, make that ribbon a blend shape for the main ribbbon, and move the follicle joints, hence the deform joints, with it. No blend shapes on the export mesh. All joint driven.
You can always connect nodes outside the hierarchy to drive the deform joints. For example, make control0 at the elbow or something and have its rotation drive a deform joint in the bicep using math nodes, remap values, clamps, etc.
Unfortunately all of this is done post run (as far as I know at the moment) so you’ll have to learn a little python and dig into the source code.
Here are some old scripts I made that drive joints from other objects. They’re hard coded, so easy to use.
Remap values (dont use too many, theyre heavy)
def remap_driver(driver, driven, driven_attr, driver_attr, inMin, inMax, outMin, outMax):
remapper = cmds.createNode("remapValue", n=f"{driver}_{driver_attr}_remap")
cmds.setAttr(f"{remapper}.inputMin", inMin)
cmds.setAttr(f"{remapper}.inputMax", inMax)
cmds.setAttr(f"{remapper}.outputMin", outMin)
cmds.setAttr(f"{remapper}.outputMax", outMax)
cmds.connectAttr(f"{driver}.{driver_attr}", f"{remapper}.inputValue")
cmds.connectAttr(f"{remapper}.outValue", f"{driven}.{driven_attr}")
return remapper
Get the average rotation between 2 objects, and then apply that average to another node (essentially gimmick joints, but with clamps)
def average_rot_between(input1, input2, output, host, axis):
avg = cmds.createNode("plusMinusAverage", n = f"{output}_avgRot")
cmds.setAttr(f"{avg}.operation", 3)
base = "_".join(output.split("_")[:1])
clamp_min = f"{base}_clamp_min"
if not cmds.attributeQuery(clamp_min, n = host, exists = True):
cmds.addAttr(host, ln=clamp_min, at = "float", min = -180, max = 180, dv = 0, k = True)
clamp_max = f"{base}_clamp_max"
if not cmds.attributeQuery(clamp_max, n = host, exists = True):
cmds.addAttr(host, ln = clamp_max, at = "float", min = -180, max = 180, dv = 0, k = True)
for i, inp in enumerate([input1, input2]):
clamp = cmds.createNode("clamp", n = f"{inp}_avgRot_clamp")
cmds.setAttr(f"{clamp}.minR", -360)
cmds.setAttr(f"{clamp}.maxR", 360)
cmds.connectAttr(f"{inp}.rotate{axis}", f"{clamp}.inputR")
cmds.connectAttr(f"{host}.{clamp_min}", f"{clamp}.minR")
cmds.connectAttr(f"{host}.{clamp_max}", f"{clamp}.maxR")
cmds.connectAttr(f"{clamp}.outputR", f"{avg}.input1D[{i}]")
mult = cmds.createNode("multiplyDivide", n = f"{output}_avgRot_switch")
cmds.connectAttr(f"{avg}.output1D", f"{mult}.input1X")
switch_attr = f"{base}_avgRot{axis}_switch"
if not cmds.attributeQuery(switch_attr, n = host, exists = True):
cmds.addAttr(host, ln = switch_attr, at = "float", min = 0, max = 2, dv = 1, k = True)
cmds.connectAttr(f"{host}.{switch_attr}", f"{mult}.input2X")
cmds.connectAttr(f"{mult}.outputX", f"{output}.rotate{axis}")
I’m not a very good coder-- I’m self taught so im sure this sucks. But it works for me.
hope this helps