[Maya binding] Proportional stretching of three-point IK skeleton - combining node editor and command line

foreword

The principle has been mentioned in this document before , it is purely a self-promotion product, probably only you can understand what you wrote. Then implement it according to this idea. The main purpose of this article is to review the node editor. The feel of the maya node editor is really messy.

  • This series is dug from a foreign tutorial, it is not suitable for colored pens like me, the operation is really fast.

Code

I still think the code is the clearest, put the code first

def add_ik_stretch(side, part, ik_chain, base_ctrl, local_ctrl, world_ctrl,primary_axis):
    base_name = side +'_' + part
    # start to end distance
    limb_dist = cmds.createNode('distanceBetween', name=base_name+'_DIST')
    # cinditon node
    limb_cnd = cmds.createNode('condition', name=base_name+'CND')
    # allocate locater, get transform node
    start_LOC = cmds.spaceLocator(name=base_name+'_start_LOC')[0]
    end_LOC = cmds.spaceLocator(name=base_name+'_end_LOC')[0]

    stretch_mdn = cmds.createNode('multiplyDivide', name=base_name+'_stretch_MDN')

    # calculate section length
    length_a = distance_between(ik_chain[0],ik_chain[1])
    length_b = distance_between(ik_chain[1], ik_chain[2])
    total_length = length_b + length_a

    # measure start-end desired direct distance
    # move the loc first
    cmds.pointConstraint(base_ctrl, start_LOC, maintainOffset=False)
    cmds.pointConstraint(local_ctrl, end_LOC, maintainOffset=False)
    connectAttr(start_LOC, 'worldMatrix[0]', limb_dist, 'inMatrix1')
    connectAttr(end_LOC, 'worldMatrix[0]', limb_dist, 'inMatrix2')

    # length ratio
    connectAttr(limb_dist, 'distance', stretch_mdn,'input1X')
    cmds.setAttr(stretch_mdn+'.input2X', total_length)#set devision
    cmds.setAttr(stretch_mdn+'.operation', 2)

    connectAttr(limb_dist,'distance',limb_cnd,'firstTerm')
    connectAttr(stretch_mdn,'outputX', limb_cnd,'colorIfTrueR')
    cmds.setAttr(limb_cnd+'.secondTerm', total_length)
    cmds.setAttr(limb_cnd+'.operation',3)

    # switch stretch
    cmds.addAtter(world_ctrl, attributeType='double', min=0, max=1,
                  defaultValue=1, keyable=True, longName='stretch')
    up_name = 'up' + part.title()
    lo_name = 'lo' + part.title()
    cmds.addAtter(world_ctrl, attributeType='double',
                  defaultValue=1, keyable=True, longName='up_name')
    cmds.addAtter(world_ctrl, attributeType='double',
                  defaultValue=1, keyable=True, longName='lo_name')

    stretch_bta = cmds.createNode('blendTwoAttr', name = base_name+'_BTA')
    cmds.setAttr(stretch_bta+'.input[0]', 1)
    connectAttr(limb_cnd,'outColorR',stretch_bta,'input[1]')
    connectAttr(world_ctrl,'stretch',stretch_bta,'attributesBlender')
    up_pma = cmds.createNode('plusMinusAverage', name=up_name+'_PMA')
    lo_pma = cmds.createNode('plusMinusAverage', name=lo_name + '_PMA')
    connectAttr(world_ctrl, up_name, up_pma, 'input1D[0]')
    connectAttr(world_ctrl, lo_name, lo_pma, 'input1D[0]')
    connectAttr(stretch_bta, 'output', up_pma, 'input1D[1]')
    connectAttr(stretch_bta, 'output', lo_pma, 'input1D[1]')
    cmds.setAttr(up_pma+'.input1D[2]', -1)
    cmds.setAttr(lo_pma + '.input1D[2]', -1)

    connectAttr(up_pma,'output1D',
                ik_chain[0],'scale'+primary_axis[-1])
    connectAttr(lo_pma,'output1D',
                ik_chain[1],'scale'+primary_axis[-1])

    return_dict = {
    
    'measure_locs': [start_LOC, end_LOC],
                   'total_length': total_length,
                   'mdn': stretch_mdn,
                   'cnd': limb_cnd}
    return return_dict

process explanation

Get effector transformation information

We first create two locators and move the locators to the positions of the start and end effectors.
The position of start_effector is recorded by base_ctrl(start_effector) in advance.
insert image description here
Similarly, end_effector is recorded by local_ctrl. The function of this local_ctrl is to control the hand, but there is no hand joint added here.
insert image description here

  • 移动locator到effectors
    cmds.pointConstraint(base_ctrl, start_LOC, maintainOffset=False)
    cmds.pointConstraint(local_ctrl, end_LOC, maintainOffset=False)

Calculate some distance values

  • Get the distance between the effectors, the distance of the effector is the target we expect to lengthen the original IKhandeler, this number is more criticallimb_dist.distance
    insert image description here
     # start to end distance
    limb_dist = cmds.createNode('distanceBetween', name=base_name+'_DIST')
    ···
    connectAttr(start_LOC, 'worldMatrix[0]', limb_dist, 'inMatrix1')
    connectAttr(end_LOC, 'worldMatrix[0]', limb_dist, 'inMatrix2')
  • Model the distances of the upper and lower arms and their sumtotal_length

    length_a = distance_between(ik_chain[0],ik_chain[1])
    length_b = distance_between(ik_chain[1], ik_chain[2])
    total_length = length_b + length_a
    

Get the stretch ratio

Tips: The following function connectAttr()is the cmds method rewritten by myself, and the functions are the same

    stretch_mdn = cmds.createNode('multiplyDivide', name=base_name+'_stretch_MDN')
    ···
    # length ratio
    connectAttr(limb_dist, 'distance', stretch_mdn,'input1X')
    cmds.setAttr(stretch_mdn+'.input2X', total_length)#set devision
    cmds.setAttr(stretch_mdn+'.operation', 2)

insert image description here
Yes stretch ratio = effectors的距离/模型手臂长, this part is completed with the node 'multiplyDivide'. It is very troublesome, if you want to achieve real-time calculation, you must use nodes, unless it is a constant value, such as the constant of the model arm length, we can calculate it offline

Conditional branch switching of stretch ratio

     # cinditon node
    limb_cnd = cmds.createNode('condition', name=base_name+'CND')
    ···
    connectAttr(limb_dist,'distance',limb_cnd,'firstTerm')
    connectAttr(stretch_mdn,'outputX', limb_cnd,'colorIfTrueR')
    cmds.setAttr(limb_cnd+'.secondTerm', total_length)
    cmds.setAttr(limb_cnd+'.operation',3)

The reason is: effectors距离>=模型手臂总长when it is enabled, it is calculated before strech ratio, otherwise it is 1
insert image description here

Add a few attributes to the end effctor

The new attribute we add here is on the parent controller world_ctrl of local_ctrl, and the pivot world positions of these two things are the same

    # switch stretch
    cmds.addAtter(world_ctrl, attributeType='double', min=0, max=1,
                  defaultValue=1, keyable=True, longName='stretch')
    up_name = 'up' + part.title()
    lo_name = 'lo' + part.title()
    cmds.addAtter(world_ctrl, attributeType='double',
                  defaultValue=1, keyable=True, longName='up_name')
    cmds.addAtter(world_ctrl, attributeType='double',
                  defaultValue=1, keyable=True, longName='lo_name')

insert image description here

  • Stretch: the mixing ratio of IK overall stretching or not stretching
  • up Arm: The stretch factor of the upper arm
  • lo Arm: the same for the lower arm

Achieving Joint Stretch

    stretch_bta = cmds.createNode('blendTwoAttr', name = base_name+'_BTA')
    cmds.setAttr(stretch_bta+'.input[0]', 1)
    connectAttr(limb_cnd,'outColorR',stretch_bta,'input[1]')
    connectAttr(world_ctrl,'stretch',stretch_bta,'attributesBlender')

insert image description here

  • One pitfall: Under the pure node editor, the input of the BTA node must be activated, and the constant cannot be entered if it is not activated. input[0]=1On behalf of the world_ctrlde stretch coefficient is 0, take the same measures
    -insert image description here

Obtain the stretch of the part and realize the scaling of the upper and lower skeletons

 up_pma = cmds.createNode('plusMinusAverage', name=up_name+'_PMA')
    lo_pma = cmds.createNode('plusMinusAverage', name=lo_name + '_PMA')
    connectAttr(world_ctrl, up_name, up_pma, 'input1D[0]')
    connectAttr(world_ctrl, lo_name, lo_pma, 'input1D[0]')
    connectAttr(stretch_bta, 'output', up_pma, 'input1D[1]')
    connectAttr(stretch_bta, 'output', lo_pma, 'input1D[1]')
    cmds.setAttr(up_pma+'.input1D[2]', -1)
    cmds.setAttr(lo_pma + '.input1D[2]', -1)

insert image description here
insert image description here
We use the stretch ratio that has been attenuated by the scaling factor of the upper and lower arms according to the above figure for the scale of the IK joint to achieve stretching

    connectAttr(up_pma,'output1D',
                ik_chain[0],'scale'+primary_axis[-1])
    connectAttr(lo_pma,'output1D',
                ik_chain[1],'scale'+primary_axis[-1])

end

The above is the general sorting process and there are still many details left unsaid. I am afraid that I will forget it. I also put the mb file with the same function in the node editor version , which can only be opened after 2020.
insert image description here

Guess you like

Origin blog.csdn.net/qq_43544518/article/details/130007498