Skip to content

Instantly share code, notes, and snippets.

@CouriersRyan
Created August 29, 2024 13:32
Show Gist options
  • Select an option

  • Save CouriersRyan/d2bfc3c4f4eef4a23ccaa827b22227ed to your computer and use it in GitHub Desktop.

Select an option

Save CouriersRyan/d2bfc3c4f4eef4a23ccaa827b22227ed to your computer and use it in GitHub Desktop.
from pymel.core import *;
import maya.api.OpenMaya as om;
# From two selected locators, create a vertical spine with ikHandle and ikJoints at the start and end
# Seleect two locators, first one being Start and the second one being End
startEnd = ls(sl=1);
jointCount = 5;
# get the length between the two locators and split it evenly across the joints
posA = om.MVector(xform(startEnd[0], q=1, ws=1, t=1));
posB = om.MVector(xform(startEnd[1], q=1, ws=1, t=1));
startEndDistance = om.MVector.length(posB-posA);
increment = startEndDistance / jointCount;
jointList = [];
startEndJoints = [];
select(cl=1); # clear selection
# Create the IK joint hierarchy
rootJoint = joint(n='IKRoot');
rootJoint.setMatrix(startEnd[0].getMatrix(ws=1));
rootJoint.jointOrient.set(0, 0, 90);
jointList.append(rootJoint);
for i in range(0, jointCount):
myJoint = joint(name = 'IKSpine'+str(i+1));
myJoint.translateX.set(increment);
jointList.append(myJoint);
# create START and END joints that will be squash and stretch the spineCurve
for i in range(0, len(startEnd)):
select(cl = 1);
myJoint = joint();
myJoint.radius.set(3);
startEndJoints.append(myJoint);
if(i == 0):
myJoint.translate.set(posA);
myJoint.rename("START_stretch");
else:
myJoint.translate.set(posB);
myJoint.rename("END_stretch");
# create SplinIK handle
newIK = ikHandle(sol="ikSplineSolver", pcv=0, createCurve=1, numSpans=4, startJoint=jointList[0], endEffector=jointList[-1], name="spineIKHandle");
newIK[0].visibility.set(0);
# get the ikHandle and make a skin cluster for it on the startEnd joints
IKCurve = ikHandle(query=1, curve=1);
skinCluster(startEndJoints, IKCurve, toSelectedBones=1, normalizeWeights=1, maximumInfluences=2, obeyMaxInfluences=1, dropoffRate=10);
# Stretchiness
curveInfo = arclen(IKCurve, ch=1);
ratioNode = createNode("multiplyDivide", n = "currentCurveRatio");
ratioNode.op.set(2)
curveInfo.arcLength.connect(ratioNode.input1X);
ratioNode.input2X.set(startEndDistance);
mulRatioNode = createNode("multiplyDivide", n = "multiplyCurveRatio");
mulRatioNode.op.set(1)
ratioNode.outputX.connect(mulRatioNode.input1X);
mulRatioNode.input2X.set(increment);
for i in range(1, len(jointList)):
mulRatioNode.outputX.connect(jointList[i].translateX);
# FK Starts Here
fkJointCount = 3; # Excluding root joint.
fkIncrement = startEndDistance/fkJointCount;
fkJointList = [];
select(cl=1); # clear selection
# Create the FK joint hierarchy
fkRootJoint = joint(n='FKRoot');
fkRootJoint.setMatrix(startEnd[0].getMatrix(ws=1));
fkRootJoint.jointOrient.set(0, 0, 90);
fkJointList.append(fkRootJoint);
for i in range(0, fkJointCount):
myJoint = joint(name = 'FKSpine'+str(i));
myJoint.translateX.set(fkIncrement);
fkJointList.append(myJoint);
# Create FK Controls on joints
prevCurve = 0;
hipsCtrl = 0;
shoulderCtrl = 0;
hullCtrlsList = [];
for i in range(0, len(fkJointList)):
if(i == 0):
curveCtrl = circle(nr=(1, 0, 0), r=5, name='CTRL_HULL');
groupCtrl = group(curveCtrl, name=curveCtrl[0]+'_grp');
delete(parentConstraint(fkJointList[i], groupCtrl));
hullCtrlsList.append(curveCtrl);
hipsCtrl = circle(nr=(1, 0, 0), r=3.5, name='CTRL_HIPS');
hipsGrp = group(hipsCtrl, name=hipsCtrl[0]+'_grp');
delete(parentConstraint(fkJointList[i], hipsGrp));
parent(hipsGrp, fkJointList[0]);
elif (i == len(fkJointList)-1):
shoulderCtrl = circle(nr=(1, 0, 0), r=2.5, name='CTRL_SHOULDER');
shoulderGrp = group(shoulderCtrl, name=shoulderCtrl[0]+'_grp');
delete(parentConstraint(fkJointList[i], shoulderGrp));
parent(shoulderGrp, fkJointList[-1]);
else:
curveCtrl = circle(nr=(1, 0, 0), r=2, name='CTRL_'+fkJointList[i]);
groupCtrl = group(curveCtrl, name=curveCtrl[0]+'_grp');
delete(parentConstraint(fkJointList[i], groupCtrl));
hullCtrlsList.append(curveCtrl);
if(prevCurve != 0 and shoulderCtrl == 0):
parent(groupCtrl, prevCurve);
prevCurve = curveCtrl;
# Constrain root and end joints of FK to Hips and Shoulder CTRLs respectively.
parentConstraint(hipsCtrl, startEndJoints[0], mo=1);
parentConstraint(shoulderCtrl, startEndJoints[-1], mo=1);
# Constraint FK joints to the Hull and Spine CTRL hierarchy.
for i in range(0, len(hullCtrlsList)):
parentConstraint(hullCtrlsList[i], fkJointList[i], mo=1);
# Create Locators for Twist Controls.
select(cl=1);
locA = spaceLocator(a=1, n='startAim', p=(posA.x + 2, posA.y, posA.z));
locB = spaceLocator(a=1, n='endAim', p=(posB.x + 2, posB.y, posB.z));
locA.visibility.set(0);
locB.visibility.set(0);
parent(locA, hipsCtrl);
parent(locB, shoulderCtrl);
# Enable/Set Up Twist Controls.
newIK[0].dTwistControlEnable.set(1);
newIK[0].dWorldUpType.set(2);
connectAttr(locA.attr('worldMatrix'), newIK[0].attr('dWorldUpMatrix'));
connectAttr(locB.attr('worldMatrix'), newIK[0].attr('dWorldUpMatrixEnd'));
from pymel.core import *;
#get a list of all selected objects (order: control, top of FK chain, top of IK chain, top of main chain)
selection = ls(sl=1);
print(selection);
#set Control object
ctrl = selection[0];
#set FK Chain
fkChain = select(selection[1]);
fkChain = select(hi=1);
fkChain = ls(sl=1, type='joint');
print(fkChain);
#set IK Chain
ikChain = select(selection[2]);
ikChain = select(hi=1);
ikChain = ls(sl=1, type='joint');
print(ikChain);
#set MAIN Chain
mainChain = select(selection[3]);
mainChain = select(hi=1);
mainChain = ls(sl=1, type='joint');
print(mainChain);
#setup Names
drivingChannelName = "FK_IK";
jointNamePrefix=["Shoulder", "Elbow", "Wrist"];
#create driving Attribute
ctrl.addAttr(drivingChannelName, at='double', min=0, max=1, dv=0, k=1);
for i in range(0, len(mainChain)):
myConstraint = orientConstraint(fkChain[i], ikChain[i], mainChain[i], n="ArmChain_"+jointNamePrefix[i]+"_Orient");
ADL_node = createNode("addDoubleLinear", n = jointNamePrefix[i] + "_ADL");
ADL_node.input1.set(1);
PMA_node = createNode("plusMinusAverage", n = jointNamePrefix[i] + "_PMA");
PMA_node.operation.set(2);
ADL_node.output.connect(PMA_node.input1D[0]);
ctrl.attr(drivingChannelName).connect(PMA_node.input1D[1]);
constraintChannel_A = myConstraint.attr("target[0].targetWeight").listConnections(p=1);
constraintChannel_B = myConstraint.attr("target[1].targetWeight").listConnections(p=1);
connectAttr(ctrl.attr(drivingChannelName), constraintChannel_B[0]);
connectAttr(PMA_node.output1D, constraintChannel_A[0]);
from pymel.core import *;
select(hi=1);
selection = ls(sl=1, type='joint');
prevCurve = 0;
for i in range(0, len(selection)):
curveCtrl = circle(nr=(1, 0, 0), name='CTRL_'+selection[i]);
groupCtrl = group(curveCtrl, name=curveCtrl[0]+'_grp');
delete(parentConstraint(selection[i], groupCtrl));
orientConstraint(curveCtrl, selection[i]);
if(prevCurve != 0):
parent(groupCtrl, prevCurve);
prevCurve = curveCtrl;
from pymel.core import *;
selection = ls(sl=1);
selectedHandle = selection[0];
endEffector = ikHandle(selectedHandle, query=True, ee=True);
startJoint = ikHandle(selectedHandle, query=True, sj=True);
endJoint = listRelatives(startJoint, allDescendents=True, type='joint')[0];
curveCtrl = circle(nr=(1, 0, 0), name='CTRL_IK', );
groupCtrl = group(curveCtrl, name='CTRL_IK_grp');
delete(parentConstraint(endEffector, groupCtrl));
pointConstraint(curveCtrl, selectedHandle);
orientConstraint(groupCtrl, endJoint);
from pymel.core import *;
import maya.cmds as cmds
# Select top of finger joint hierarchy first, then the object that will hold the rotate attr.
items = ls(sl=1);
rotateAttributeName = 'Mitten' # name the rotate attribute you will make
select(items[0], hi=1);
selection = ls(sl=1, type='joint');
grpCtrlList = []
prevCurve = 0;
for i in range(0, len(selection)):
curveCtrl = circle(nr=(0, 1, 0), name='CTRL_'+selection[i]);
groupCtrl = group(curveCtrl, name=curveCtrl[0]+'_rotate');
overallCtrl = group(groupCtrl, name=curveCtrl[0]+'_orient');
delete(parentConstraint(selection[i], overallCtrl));
curveCtrl[0].translateBy((0, 4, 0), space='object')
cmds.move(0, -4, 0, curveCtrl[0] + '.scalePivot', curveCtrl[0] + '.rotatePivot', relative = True, objectSpace = True)
orientConstraint(curveCtrl, selection[i]);
if(prevCurve != 0):
parent(overallCtrl, prevCurve);
prevCurve = curveCtrl;
grpCtrlList.append(groupCtrl);
items[1].addAttr(rotateAttributeName, at='float', k=1);
for i in range(0, len(grpCtrlList)):
items[1].attr(rotateAttributeName).connect(grpCtrlList[i].rotateZ);
####### Important #######
# When you are done, move all the ctrls onto the wrist or equivalent, create a group and unparent it from the wrist and then parent constraint it to the wrist.
from pymel.core import *
import maya.OpenMaya as om
# Variables to hold nodes.
curveClusterList = []
jointList = []
scaleCircleList = []
curvePointPercentList = []
# Set Joint Number.
jointNumber = 9
# Create Groups
allGroup = group(n = 'ALL')
select(cl=1)
untouchablesGroup = group(n = 'UNTOUCHABLES')
untouchablesGroup.visibility.set(0)
select(cl=1)
folliclesGroup = group(n = 'follicles_grp')
select(cl=1)
clustersGroup = group(n = 'clusters_grp')
select(cl=1)
scaleCirclesGroup = group(n = 'scaleCircles_grp')
select(cl=1)
# Determine size of first ribbon
ribbonWidth = 10
ribbonLengthRatio = 0.05
ribbonDivisions = ribbonWidth
# Create initial NURBS plane for ribbon
ribbonGeo = nurbsPlane(p = (0, 0, 0), ax = (0, 1, 0), w=ribbonWidth, lr=ribbonLengthRatio, d=3, u=ribbonDivisions, v=1, ch=1, n='ribbon_MAIN')
increment = 1.0 / (jointNumber - 1)
select(cl=1)
# Create the follicles, joints, etc.
for i in range(0, jointNumber):
follicle = createNode('follicle')
follicle.outRotate.connect(follicle.listRelatives(p=1)[0].rotate)
follicle.outTranslate.connect(follicle.listRelatives(p=1)[0].translate)
ribbonGeo[0].listRelatives()[0].local.connect(follicle.inputSurface)
ribbonGeo[0].listRelatives()[0].worldMatrix[0].connect(follicle.inputWorldMatrix)
follicle.parameterV.set(0.5)
follicle.listRelatives(p=1)[0].rename('twist_follicle_{}'.format(i+1))
# add joint to each follicle
follicleJoint = joint(n='twistJoint_bind_{}'.format(i+1))
jointList.append(follicleJoint)
# add scaling circle to each joint
scaleCircle = circle(n = ('scale_display_{}'.format(i+1)), c=(0, 0, 0), nr=(1, 0, 0), r=(2), s=24)
delete(scaleCircle[0], ch=1)
scaleCircleList.append(scaleCircle[0])
#disable soft select
softSelect(e=1, softSelectEnabled=0)
for j in range(0, 24, 2):
select((scaleCircle[0] + ".cv[{}]".format(j)), tgl=1)
scale((1.2761, 1.2761, 1.2761), p=(0, 0, 0), r=1)
scaleCircle[0].scale.set(1, 1, 1) # have to counter scale it back to 1.
parentConstraint(follicleJoint, scaleCircle[0])
parent (scaleCircle[0], scaleCirclesGroup)
if(i > 0):
follicle.parameterU.set(i * increment)
parent(follicle.listRelatives(p=1), folliclesGroup)
# Coloring
colorList = [(255, 0 , 0), (255, 127, 0), (255, 255, 0), (0, 255, 0), (0, 0, 255)]
for circleP in range(0, len(scaleCircleList)):
newColor = (om.MVector(colorList[-1][0], colorList[-1][1], colorList[-1][2]))/255
scaleCircleList[circleP].getShape().overrideEnabled.set(1)
scaleCircleList[circleP].getShape().overrideRGBColors.set(1)
scaleCircleList[circleP].getShape().overrideColorRGB.set(newColor)
# create set for BIND joints
sets(jointList, n='BIND_List')
# Create TWIST and BEND Geo. Create Twist Deformer and Bend Curve. Set up Blendshape, Wire Deformer, and Clusters.
#Ribbon Twist and Bend
ribbon_twist = ribbonGeo[0].duplicate(n='ribbon_TWIST')
ribbon_bend = ribbonGeo[0].duplicate(n='ribbon_BEND')
# Ribbon Blendshapes
newBlendShape = blendShape(ribbon_twist[0], ribbon_bend[0], ribbonGeo[0], n='ribbon_blendshape')
newBlendShape[0].numWeights()
for i in range(0, newBlendShape[0].numWeights()):
newBlendShape[0].setWeight(i, 1)
# Ribbon twist deformations
ribbon_twist[0].visibility.set(0)
twistDeformer = nonLinear(ribbon_twist, type='twist')
twistDeformer[1].rotateZ.set(90)
# Ribbon bend deformers
curveLength = ribbonWidth/2
bendCurve = curve(n='bend_curve', d=2, p=[(curveLength*-1, 0, 0), (0, 0, 0), (curveLength, 0, 0)])
knots = bendCurve.listRelatives(s=1)[0].knots
wireDeformer = wire(ribbon_bend, w=bendCurve, dds=(0,10), en=1.0, ce=0.0, li=1.0)
pointNum = (len(knots)-1)
select(cl=1)
for i in range(0, pointNum):
select(bendCurve + '.cv[{}]'.format(i))
newCluster = cluster(n='twist_cluster_' + str(i+1))
curveClusterList.append(newCluster[1])
parent(newCluster[1], clustersGroup)
# Create Start and End handles. Connect to TWIST and BEND
select(ribbonGeo[0])
bBoxSize = exactWorldBoundingBox()
bBoxWidth = (bBoxSize[5]) - (bBoxSize[2])
select(cl=1)
bBoxStart = (bBoxSize[3])
bBoxEnd = (bBoxSize[0])
# Create Start and End Handles
startHandle = curve(n='start_handle', d=1,
p=[
(1, 0, bBoxSize[2]),
(0, 0, bBoxSize[2]),
(0, 0, bBoxSize[5]),
(1, 0, bBoxSize[5]),
(1, 0, bBoxSize[2])
])
startHandle.translateX.set(bBoxStart)
makeIdentity(apply=1, t=1, r=1, s=1, n=0, pn=1)
endHandle = curve(n='end_handle', d=1,
p=[
(-1, 0, bBoxSize[2]),
(0, 0, bBoxSize[2]),
(0, 0, bBoxSize[5]),
(-1, 0, bBoxSize[5]),
(-1, 0, bBoxSize[2])
])
endHandle.translateX.set(bBoxEnd)
makeIdentity(apply=1, t=1, r=1, s=1, n=0, pn=1)
# Set up different colors for handles
startHandle.getShape().overrideEnabled.set(1)
startHandle.getShape().overrideColor.set(17)
endHandle.getShape().overrideEnabled.set(1)
endHandle.getShape().overrideColor.set(17)
# Lock Handle Attributes
toLock = ['.ry', '.rz', '.sx', '.sy', '.sz', '.v']
# connect handle to twist deformer
startHandle.rotateX.connect(twistDeformer[0].startAngle)
endHandle.rotateX.connect(twistDeformer[0].endAngle)
# connect handle to clusters
startHandle.translateX.connect(curveClusterList[2].translateX)
startHandle.translateY.connect(curveClusterList[2].translateY)
startHandle.translateZ.connect(curveClusterList[2].translateZ)
endHandle.translateX.connect(curveClusterList[0].translateX)
endHandle.translateY.connect(curveClusterList[0].translateY)
endHandle.translateZ.connect(curveClusterList[0].translateZ)
#create mid-handle and connect to middle cluster
midHandle = circle(n='mid_handle', c=(0, 0, 0), nr=(0, 1, 0), r=(2), s=8)
midHandleCtrlGrp = group(midHandle, n='mid_handle_grp')
midHandle[0].translateX.connect(curveClusterList[1].translateX)
midHandle[0].translateY.connect(curveClusterList[1].translateY)
midHandle[0].translateZ.connect(curveClusterList[1].translateZ)
for i in range(0, len(toLock)):
setAttr(startHandle + toLock[i], lock=1, keyable=0, channelBox=0)
setAttr(endHandle + toLock[i], lock=1, keyable=0, channelBox=0)
setAttr(midHandle[0] + toLock[i], lock=1, keyable=0, channelBox=0)
pointConstraint(startHandle, endHandle, midHandleCtrlGrp)
# Create world handle and add attributes
worldHandle = circle(n='twist_WORLD', c=(0, 0, 0), nr=(0, 1, 0), r=(4), s=8)
worldHandle[0].addAttr('scaleVis', at='bool', k=1, dv=1)
worldHandle[0].addAttr('volumePres', at='bool', k=1, dv=1)
worldHandle[0].addAttr('minSquash', at='double', k=1, dv=.3)
worldHandle[0].addAttr('squashPercent', at='double', k=1, dv=.1)
worldHandle[0].addAttr('COMPENSATE', at='double', k=1, dv=0)
worldHandle[0].addAttr('compensateAmt', at='double', k=0)
setAttr(worldHandle[0].compensateAmt, e=1, channelBox=1)
# Create Volume Profile Curve
profileCurveIncrement = ribbonWidth/(jointNumber - 1)
profileCurve = curve(n='profile_curve', d=3, p=[((curveLength*-1), 0, 0)])
for c in range(1, jointNumber):
curve(profileCurve, append=1, p=[ (((curveLength*-1) + (profileCurveIncrement * c)), 0, 0)])
profileCurve.translateY.set(3)
profileCurve.overrideEnabled.set(1)
profileCurve.overrideColor.set(17)
for CP in range(0, (profileCurve.numCVs() + 1)):
curvePointPercent_MD = createNode('multiplyDivide', n = ('CURVE_PNT_PERCENT_MD_{}'.format(CP)))
curvePointPercentList.append(curvePointPercent_MD)
for MD in range(0, (profileCurve.numCVs() + 1)):
controlPoint = (profileCurve + '.controlPoints[{}].yValue'.format(MD))
connectAttr(controlPoint, curvePointPercentList[MD].input1X)
worldHandle[0].squashPercent.connect(curvePointPercentList[MD].input2X)
# Create Node Network For Volume Adjustments
negateEnd_MD = createNode('multiplyDivide', n = 'NEGATE_END_CTRL_MD')
negateEnd_MD.input2X.set(-1)
endHandle.translateX.connect(negateEnd_MD.input2X)
addCtrlX_ADL = createNode('addDoubleLinear', n = 'addCtrlX_ADDL')
negateEnd_MD.outputX.connect(addCtrlX_ADL.input1)
startHandle.translateX.connect(addCtrlX_ADL.input2)
addCtrlX_ADL.output.connect(worldHandle[0].compensateAmt)
scaleCompensate_ADL = createNode('addDoubleLinear', n = 'scaleCompensate_ADDL')
worldHandle[0].COMPENSATE.connect(scaleCompensate_ADL.input1)
addCtrlX_ADL.output.connect(scaleCompensate_ADL.input2)
conditionNode = createNode('condition', n = 'VOLUME_ON_OFF_COND')
conditionNode.secondTerm.set(1)
worldHandle[0].volumePres.connect(conditionNode.firstTerm)
scaleCompensate_ADL.output.connect(conditionNode.colorIfTrueR)
for k in range(0, profileCurve.numCVs()):
print(str(k))
controlCurvePercent_MD = createNode('multiplyDivide', n = 'CURVE_POINT_Y_MD_{}'.format(k))
conditionNode.outColorR.connect(controlCurvePercent_MD.input1X)
curvePointPercentList[k].outputX.connect(controlCurvePercent_MD.input2X)
normalize_ADL = createNode('addDoubleLinear', n='normalize_ADL_{}'.format(k))
controlCurvePercent_MD.outputX.connect(normalize_ADL.input1)
normalize_ADL.input2.set(1)
minscale_CLAMP = createNode('clamp', n = 'MIN_SCALE_CLAMP_{}'.format(k))
normalize_ADL.output.connect(minscale_CLAMP.inputR)
minscale_CLAMP.maxR.set(10)
worldHandle[0].minSquash.connect(minscale_CLAMP.minR)
minscale_CLAMP.outputR.connect(scaleCircleList[k].scaleY)
minscale_CLAMP.outputR.connect(scaleCircleList[k].scaleZ)
worldHandle[0].scaleVis.connect(scaleCirclesGroup.v)
worldHandle[0].scaleVis.connect(profileCurve.v);
# Clean up and grouping
select(cl=1)
baseWire = wireDeformer[0].listConnections()[3]
parent(ribbon_bend[0], baseWire, folliclesGroup, clustersGroup, ribbon_twist[0], twistDeformer[1], bendCurve, untouchablesGroup)
parent(startHandle, endHandle, midHandleCtrlGrp, ribbonGeo[0], profileCurve, worldHandle[0])
parent(scaleCirclesGroup, untouchablesGroup, worldHandle[0], allGroup)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment