コード例 #1
0
def create_ik_spine():
    log_debug('creating ik spine')
    jointRadius = float(settings.globalPrefs['jointRadius'])

    # get spine jnts
    spineJnts = pm.ls('C_spine*_ctrlJnt')

    # create curve with 3 cvs along spine jnts
    spline_crv = utils.makeCurveFromJoints(spineJnts, name= 'C_spine_splineCrv', rebuild=True, divisions=2)

    ikJnts = utils.createJointsAlongCurve(spline_crv, amount=3, name='C_spine##_ikJnt', jointRadius=jointRadius)
    
    pm.skinCluster(ikJnts, spline_crv, tsb=True, maximumInfluences=3, normalizeWeights=1, obeyMaxInfluences=True)
    
    pm.delete(spline_crv)

    return ikJnts
コード例 #2
0
def build_jnts_leg(jnts={}, mode=['ik', 'fk']):
    '''
    Build leg jnts FK and IK by duplicating ctrlJnts
    Args:
        jnts:
        mode:

    Returns:

    '''
    for RL in ['R', 'L']:
        if '%s_leg01' % RL in jnts:
            if 'fk' in mode:
                if pm.objExists('%s_leg01_fkJnt' % RL):
                    pm.delete('%s_leg01_fkJnt' % RL)  # delete existing

                fkJnt = pm.duplicate(jnts['%s_leg01' % RL]['ctrlJnt'],
                                     n='%s_leg01_fkJnt' % RL)
                log_debug('creating fkLeg %s' % fkJnt)
                pm.select(fkJnt)
                pm.mel.searchReplaceNames("_ctrlJnt", "_fkJnt", "hierarchy")

            if 'ik' in mode:
                if pm.objExists('%s_leg01_ikJnt' % RL):
                    pm.delete('%s_leg01_ikJnt' % RL)  # delete existing
                ikJnt = pm.duplicate(jnts['%s_leg01' % RL]['ctrlJnt'],
                                     n='%s_leg01_ikJnt' % RL)
                log_debug('creating ikLeg %s' % ikJnt)
                pm.select(ikJnt)
                pm.mel.searchReplaceNames("_ctrlJnt", "_ikJnt", "hierarchy")

        if '%s_foot01' % RL in jnts:
            if 'fk' in mode:
                biped.deleteExisting('%s_foot01_fkJnt' % RL)  # delete existing
                fkJnt = pm.duplicate(jnts['%s_foot01' % RL]['ctrlJnt'],
                                     n='%s_foot01_fkJnt' % RL)
                pm.select(fkJnt)
                pm.mel.searchReplaceNames("_ctrlJnt", "_fkJnt", "hierarchy")
            if 'ik' in mode:
                biped.deleteExisting('%s_foot01_ikJnt' % RL)  # delete existing
                ikJnt = pm.duplicate(jnts['%s_foot01' % RL]['ctrlJnt'],
                                     n='%s_foot01_ikJnt' % RL)
                pm.select(ikJnt)
                pm.mel.searchReplaceNames("_ctrlJnt", "_ikJnt", "hierarchy")
    log_info("Done build_jnts_leg()")
コード例 #3
0
def build_ctrlJnts_leg(jnts={}, side='L', hook='C_pelvis01_ctrlJnt'):
    '''
    create ctrl joints for leg
    '''
    log_debug('side is %s' % side)
    pm.delete(pm.ls('%s_leg_jntGrp' % side))
    pm.createNode('transform', name='%s_leg_jntGrp' % side)
    riggUtils.grpIn('Skeleton_grp', '%s_leg_jntGrp' % side)

    # leg
    leg = pm.duplicate('%s_leg01_skinJnt' % side,
                       n='%s_leg01_ctrlJnt' % side)[0]
    jnts['%s_leg01' % side] = {'ctrlJnt': leg}
    pm.select(leg)
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")
    pm.parent(leg, '%s_leg_jntGrp' % side)
    riggUtils.grpCtrl(leg)

    log_debug('constraining to hook %s' % leg.ZERO.get())

    if hook != '':
        pm.parentConstraint(hook, leg.AUTO.get(), mo=1)

    # deal with children
    for childJnt in pm.listRelatives(leg, children=1, type='joint', ad=1):
        log_debug('childJnt leg %s' % childJnt)
        if childJnt == ('%s_foot01_ctrlJnt' % side):
            pm.rename(childJnt, '%s_leg03_ctrlJntEnd' %
                      side)  # create end joint from foot
            # delete fingers
            pm.delete(
                pm.listRelatives('%s_leg03_ctrlJntEnd' % side,
                                 children=1,
                                 ad=1))
            break

    # foot
    foot = pm.duplicate('%s_foot01_skinJnt' % side,
                        n='%s_foot01_ctrlJnt' % side)[0]
    jnts['%s_foot' % side] = {'ctrlJnt': foot}

    pm.select(foot)
    pm.mel.searchReplaceNames("_skinJnt", "_ctrlJnt", "hierarchy")

    riggUtils.grpIn('%s_leg_jntGrp' % side, foot)
    riggUtils.grpCtrl(foot)

    # constrain foot AUTO to leg03End
    pm.pointConstraint('%s_leg03_ctrlJntEnd' % side,
                       pm.getAttr('%s_foot01_ctrlJnt.AUTO' % side))
コード例 #4
0
def create_ctrl_spine(jnts, spine_count=4, scale=1):
    '''
    Create COG, pelvis, spine and chest ctrls

    Args:
        jnts:
        spine_count:
        size: ctrl scale

    Returns:

    '''
    ### TODO cest offset control, to rotate from chest if needed (human_luma:C_chest_offsetCtrl)
    log_info('Creating Spine Ctrls')
    side = 'C'
    scale = scale * 2 # scale multiplyier

    # - COG - #

    # create ctrl
    cog = curveLib.wireController(type='circle', name='C_COG_ctrl', size=scale*1.25, color='yellow', facingAxis='y+')
    riggUtils.cleanUpAttr(sel=[cog], listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
    # grp - snap - parent - gimbal
    riggUtils.grpCtrl(cog)
    riggUtils.snap(jnts['pelvis01']['ctrlJnt'], cog.ZERO.get(), 'point')
    pm.parent(cog.ZERO.get(), 'DIRECTION')
    riggUtils.addGimbal(cog)
    # add to dictionary
    jnts['cog'] = {'ctrl': pm.PyNode('C_COG_ctrl')}

    spine_ctrl_grp = pm.createNode('transform', n='C_spineCtrl_grp', p=cog.gimbal.get())
    riggUtils.snap(cog.gimbal.get(), spine_ctrl_grp)
    
    
    # - Pelvis - #

    log_debug('creating pelvis ctrls')
    jnts['pelvis01']['ctrl'] = curveLib.wireController(type='circle', name='C_pelvis_ctrl', size=scale,
                                                       color='red', facingAxis='y+', aOffset=[0, -0.5, 0])
    riggUtils.cleanUpAttr(sel=jnts['pelvis01']['ctrl'], listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
    # rotation order
    jnts['pelvis01']['ctrlJnt'].rotateOrder.set(2)
    jnts['pelvis01']['ctrl'].rotateOrder.set(2)
    # grp - snap - parent - gimbal
    riggUtils.grpCtrl(jnts['pelvis01']['ctrl'])
    riggUtils.snap(jnts['pelvis01']['ctrlJnt'], jnts['pelvis01']['ctrl'].ZERO.get(), 'point')
    pm.parent(jnts['pelvis01']['ctrl'].ZERO.get(), spine_ctrl_grp)
    riggUtils.addGimbal(jnts['pelvis01']['ctrl'])

    # - Spine - #

    # ik spine
    
    log_debug('creating ik spine ctrls %s' % spine_count)
    spine_ik_jnts = pm.ls('C_spine*_ikJnt*')

    if len(spine_ik_jnts) < 3:
        log_info("No spine*_fkJnts found. Skipping")   
    else:
        parent_to=spine_ctrl_grp 
    
        # for ik ctrl, we skip 01 and 03, we will parent those to pelvis and chest, we only want a 'C_spine_midCtrl'
        ctrl = curveLib.wireController(type='cube', name='C_spineMid_ikCtrl', size=scale *0.25,
                                                        color='blue', facingAxis='y+',  aOffset=[0, 0, -3])
        riggUtils.cleanUpAttr(ctrl, listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
        riggUtils.grpCtrl(ctrl)
        riggUtils.snap(spine_ik_jnts[1], ctrl.ZERO.get(), 'point')
        pm.parent(ctrl.ZERO.get(), parent_to)
        
        # add to dic    
        jnts['spine02']['ikCtrl'] = ctrl

        #parent jnt
        pm.parent(spine_ik_jnts[1], ctrl)
    
    
    # fk spine

    log_debug('creating fk spine ctrls %s' % spine_count)
    spine_fk_jnts = pm.ls('C_spine*_ctrlJnt')

    if len(spine_fk_jnts) == 0:
        log_info("No spine*_fkJnts found. Skipping")
    
    parent_to=spine_ctrl_grp

    for i in range(0, len(spine_fk_jnts)):
        jnt_nr = '%02d' % ( i + 1)
        ctrl = curveLib.wireController(type='circle', name='C_spine%s_fkCtrl'%jnt_nr, size=scale,
                                                      color='blue', facingAxis='y+')
        riggUtils.cleanUpAttr(ctrl, listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
        riggUtils.grpCtrl(ctrl)
        riggUtils.snap(spine_fk_jnts[i], ctrl.ZERO.get(), 'point')
        pm.parent(ctrl.ZERO.get(), parent_to)
        
        # parent next jnt to this ctrl
        parent_to = ctrl

        # add to dic
        jnts['spine%s'%jnt_nr]['fkCtrl'] = ctrl

    # - Chest - #

    log_debug('creating chest ctrls')
    jnts['chest01']['ctrl'] = curveLib.wireController(type='circle', name='C_spineChest_ctrl', size=scale,
                                                      color='red', facingAxis='y+')
    riggUtils.cleanUpAttr(sel=jnts['chest01']['ctrl'], listAttr=['sx', 'sy', 'sz'], l=0, k=0, cb=0)
    # rotation order
    jnts['chest01']['ctrlJnt'].rotateOrder.set(2)
    jnts['chest01']['ctrl'].rotateOrder.set(2)

    # grp - snap - parent - gimbal
    riggUtils.grpCtrl(jnts['chest01']['ctrl'])
    riggUtils.snap(jnts['chest01']['ctrlJnt'], jnts['chest01']['ctrl'].ZERO.get(), 'point')
    pm.parent(jnts['chest01']['ctrl'].ZERO.get(), spine_ctrl_grp)
    riggUtils.addGimbal(jnts['chest01']['ctrl'])

    log_debug('spine_ik is %s'%spine_ik_jnts)
    
    # lets parent 01_ikJnt to pelvis ctrl and 03_ikJnt to chest (ik 2 has its own ctrl)
    if len(spine_ik_jnts) > 2:
        pm.parent(spine_ik_jnts[0],  jnts['pelvis01']['ctrl'])
        pm.parent(spine_ik_jnts[2],  jnts['chest01']['ctrl'])

        # align spineMid halfway between pelvis and chest
        pm.parentConstraint([jnts['pelvis01']['ctrl'], jnts['chest01']['ctrl']], jnts['spine02']['ikCtrl'].AUTO.get(), mo=1)
    
    log_debug('done create_ctrl_spine')
コード例 #5
0
def setup_spine(jnts, fkCtrls=3):
    spineCrv = 'C_spine_splineCrv'
    pm.delete(pm.ls(spineCrv))
    spineFkCtrls = pm.ls('C_spine*_fkCtrl', type='transform')
    spineIkJnts = pm.ls('C_spine*_ikJnt*', type='joint')
    spineCtrlJnts =pm.ls('C_spine*_ctrlJnt*', type='joint')
    
    #clean up from old runs
    utils.deleteChildrenConstraints(jnts['pelvis01']['ctrlJnt'].attr('AUTO').get())
    utils.deleteChildrenConstraints(jnts['chest01']['ctrlJnt'].attr('AUTO').get())
    

    # constrain pelvis
    pm.parentConstraint(jnts['pelvis01']['ctrl'], jnts['pelvis01']['ctrlJnt'].attr('AUTO').get(), mo=1)
    # constrain chest
    pm.parentConstraint(jnts['chest01']['ctrl'], jnts['chest01']['ctrlJnt'].attr('AUTO').get(), mo=1)

    spine_list = [j for j in jnts.keys() if j[0:5] == 'spine']
    spine_list.sort()
    spineJnts = pm.ls('C_spine*_skinJnt')

    # ik stretchy spline

    spline_crv = utils.makeCurveFromJoints(spineJnts, name=spineCrv, rebuild=True, divisions=2)
    
    spline_ik = pm.ikHandle(n='C_spine_splineIK', sj=jnts[spine_list[0]]['ctrlJnt'], ee=jnts[spine_list[-1]]['ctrlJnt'],
                            sol='ikSplineSolver', curve=spline_crv, ccv=False)
    print 'spine_ik is %s' % spline_ik
    spline_hndl = spline_ik[0]
    spline_eff = spline_ik[1].rename('C_spine_splineEfctr')
    # spline_crv = spline_ik[2].rename('C_spine_splineCrv')

    riggUtils.grpIn('C_spineNonScale_grp',[spline_hndl, spline_crv])
    riggUtils.grpIn('RIG_NONSCALE','C_spineNonScale_grp')

    # skin curve to ik joints
    pm.select(clear=1)
    pm.select(spineIkJnts)
    pm.skinCluster(spineIkJnts, spline_crv, maximumInfluences=4, normalizeWeights=1, obeyMaxInfluences=True)


    log_debug( 'creating stretchy spline' )
    # add attributes
    pm.addAttr(jnts['chest01']['ctrl'], ln='stretchy', at='float', dv=1, k=1)
    pm.addAttr(jnts['chest01']['ctrl'], ln='follow', at='float', dv=1, k=1)
    
    # make stretchy
    createStretchSpline(spline_crv, volume=1, worldScale=1, worldScaleObj='RIG_SCALE', worldScaleAttr='scaleX', disable=1, disableObj=jnts['chest01']['ctrl'], disableAttr='stretchy' )

    # fk - expect fkJnts having fkCtrls in right spot
    
    for spineFkCtrl in spineFkCtrls:
        log_debug('fk constraint %s'%spineFkCtrl)
        spineFkJnt = spineFkCtrl.replace('fkCtrl', 'fkJnt')
        pm.parentConstraint(spineFkCtrl, spineFkJnt, mo=1)

    # connect ik to fk
    pm.parentConstraint(spineFkCtrls[-1], 'C_spineChest_AUTO', mo=1)

    # connect spine to chest
    ### TODO Pin to attribute blend set on chest of Lumahuman
    pm.pointConstraint(spineCtrlJnts[-1], jnts['chest01']['ctrlJnt'])

    # connect rigg scale to curve
    pm.addAttr(spineCrv, ln='globalScale', at='float', dv=1, k=1)
    pm.connectAttr('RIG_SCALE.scaleX', '%s.globalScale'%spineCrv)
コード例 #6
0
def setup_ik_stretch_leg(jnts, RL='L'):
    ### TODO: consider global scale, stretch attribute on/off on ik Ctlr
    ### TODO: split up upper and lower leg stretch amound (as seen in AM steward)

    # distance measure. 2 locators, parent foot and up_leg
    dd = pm.distanceDimension(
        sp=jnts['L_leg01']['ikJnt'].t.get(),
        ep=jnts['L_foot01']['ctrlJnt'].t.get(),
    )
    pm.rename(dd.getParent(), '%s_leg_ik_dist' % RL)

    start_loc = pm.spaceLocator(n='%s_leg_ik_startDist' % RL)
    end_loc = pm.spaceLocator(n='%s_leg_ik_endDist' % RL)
    pm.pointConstraint(jnts['L_leg01']['ikJnt'], start_loc)
    pm.pointConstraint('%s_leg_ikHandle' % RL, end_loc)
    # connect loc to distanceNode
    start_loc.t >> dd.startPoint
    end_loc.t >> dd.endPoint

    riggUtils.grpIn('L_legikJnt_distGrp', [start_loc, end_loc])
    log_debug('dist create done')

    # sum_leg_tx to compare. ADD UP UP LOW DISTANCfE

    leg02_tx_orig = pm.createNode('plusMinusAverage',
                                  n='%s_leg02StrOrig_plsMns')
    leg02_tx_orig.input1D[0].set(jnts['L_leg02']['ikJnt'].tx.get())
    leg03_tx_orig = pm.createNode('plusMinusAverage',
                                  n='%s_leg03StrOrig_plsMns')
    leg03_tx_orig.input1D[1].set(jnts['L_leg03']['ikJnt'].tx.get())

    leg_tx_orig = pm.createNode('plusMinusAverage', n='%s_legStrOrig_plsMns')
    leg02_tx_orig.output1D >> leg_tx_orig.input1D[0]
    leg03_tx_orig.output1D >> leg_tx_orig.input1D[1]
    log_debug('summing up tx to compare done')

    # divide dist node by summed

    stretch_factor_mult = pm.createNode('multiplyDivide',
                                        n='%s_legStrFactor_mult')
    leg_tx_orig.output1D >> stretch_factor_mult.input2X
    dd.distance >> stretch_factor_mult.input1X
    stretch_factor_mult.operation.set(2)
    log_debug('figure out distance multiplyer done')

    # clamp output 1 to max stretch (don't want to go below 1, squash..)
    clamp_stretch = pm.createNode('clamp', n='%s_legStr_clamp' % RL)
    clamp_stretch.minR.set(1)
    clamp_stretch.maxR.set(3)
    # TODO: max stretch sett a attribute for it animator can adjust
    stretch_factor_mult.outputX >> clamp_stretch.inputR
    log_debug('clamp done')

    # multiply tx fo leg02 and 03 with stretch
    stretch_mult = pm.createNode('multiplyDivide', n='%s_legStr_mult')
    clamp_stretch.outputR >> stretch_mult.input2X
    clamp_stretch.outputR >> stretch_mult.input2Y
    leg02_tx_orig.output1D >> stretch_mult.input1X
    leg03_tx_orig.output1D >> stretch_mult.input1Y
    log_debug('mult tx to leg 02/03 stretch done')

    # now connect that to actual ikJnt TX
    stretch_mult.outputX >> jnts['L_leg02']['ikJnt'].tx
    stretch_mult.outputY >> jnts['L_leg03']['ikJnt'].tx
    log_debug('connecting done')
コード例 #7
0
def setup_ikfkSwitch_leg(jnts, RL='L'):
    # - # create blend ctrl if not exists
    if pm.objExists('%s_leg_fkIkSwitch' % RL) == False:
        log_debug('No ctrl for blend found. Building')
        color = biped.define_color(RL)
        log_debug('create switch')
        switch = create_ctrl_ikfkSwitch(name='%s_leg_fkIkSwitch' % RL,
                                        color=color,
                                        parent='%s_leg_ctrlGrp' % RL)
        log_debug('constrain')
        pm.parentConstraint(jnts['%s_foot01' % RL]['ctrlJnt'],
                            switch.attr('ZERO').get(),
                            mo=0)
    else:
        log_debug('switch ctrl already exists. skippin')
        switch = pm.PyNode('%s_leg_fkIkSwitch' % RL)
    #jnts['%s_foot01' % RL]['switchCtrl'] = switch

    # - # create blendnodes for rot/trans/scale for leg01 and leg02
    for i in [1, 2, 3]:  # leg01 and leg02
        # blend node
        log_debug(jnts['%s_leg%02d' % (RL, i)])
        jnt_name = jnts['%s_leg%02d' % (RL, i)]['ctrlJnt'].name()

        # delete existing
        for attr in ['transBlend', 'rotBlend', 'scaleBlend']:
            pm.delete(pm.ls('%s_%s' % (jnt_name, attr)))

        blend_trans = pm.createNode('blendColors',
                                    n='%s_transBlend' % jnt_name)
        blend_rot = pm.createNode('blendColors', n='%s_rotBlend' % jnt_name)
        blend_scale = pm.createNode('blendColors',
                                    n='%s_scaleBlend' % jnt_name)

        jnts['%s_leg%02d' % (RL, i)]['fkJnt'].t >> blend_trans.color2
        jnts['%s_leg%02d' % (RL, i)]['ikJnt'].t >> blend_trans.color1
        blend_trans.output >> jnts['%s_leg%02d' % (RL, i)]['ctrlJnt'].t

        jnts['%s_leg%02d' % (RL, i)]['fkJnt'].r >> blend_rot.color2
        jnts['%s_leg%02d' % (RL, i)]['ikJnt'].r >> blend_rot.color1
        blend_rot.output >> jnts['%s_leg%02d' % (RL, i)]['ctrlJnt'].r

        jnts['%s_leg%02d' % (RL, i)]['fkJnt'].s >> blend_scale.color2
        jnts['%s_leg%02d' % (RL, i)]['ikJnt'].s >> blend_scale.color1
        blend_scale.output >> jnts['%s_leg%02d' % (RL, i)]['ctrlJnt'].s

        switch = pm.PyNode('%s_leg_fkIkSwitch' % RL)

        log_debug('connecting')
        switch.fkIk >> blend_trans.blender
        switch.fkIk >> blend_rot.blender
        switch.fkIk >> blend_scale.blender

    # -- foot--#
    # foot01_ctrlJnt - weight blend existing orient constrain
    const = pm.PyNode('%s_foot01_ctrlJnt_orientCon' % RL)
    weightList = const.getWeightAliasList()

    rev = pm.createNode('reverse', n='%s_legIkFk_rev' % RL)
    switch.fkIk >> weightList[1]
    switch.fkIk >> rev.inputX
    rev.outputX >> weightList[0]

    # hook up visibility of ctrls
    rev.outputX >> pm.PyNode(
        jnts['%s_leg01' % RL]['fkCtrl'].attr('ZERO').get()).visibility
    rev.outputX >> jnts['%s_leg01' % RL]['fkJnt'].visibility
    rev.outputX >> pm.PyNode(
        jnts['%s_foot01' % RL]['fkCtrl'].attr('ZERO').get()).visibility

    switch.fkIk >> pm.PyNode(
        jnts['%s_foot01' % RL]['ikCtrl'].attr('ZERO').get()).visibility
    switch.fkIk >> jnts['%s_leg01' % RL]['ikJnt'].visibility

    log_info('Done: setup_ik_leg')
    return jnts
コード例 #8
0
def create_ctrl_ik_leg(jnts, side='L', parent='DIRECTION', scale=1):
    '''
    Args:
        jnts: joint dictionary
        side: 'R' or 'L'
    Returns:   joint dictionary
    '''

    color = biped.define_color(side)
    id = '%s_foot01' % side
    # check if exists
    if pm.objExists('%s_ikCtrl' % id):
        pm.select('%s_ikCtrl' % id)
        return '%s_ikCtrl Exists' % id

    # -- ik ctrl -- #
    jnts[id]['ikCtrl'] = curveLib.createShapeCtrl(type='cube',
                                                  name='%s_ikCtrl' % id,
                                                  scale=scale,
                                                  color=color)
    # group and align
    zero = riggUtils.grpCtrl(jnts[id]['ikCtrl'])
    riggUtils.snap(jnts[id]['ctrlJnt'], zero, typeCnx='point')
    riggUtils.grpIn('%s_leg_ctrlGrp' % side, zero)  # parent leg_ctrlGrp

    # limit
    riggUtils.cleanUpAttr(sel=[jnts[id]['ikCtrl']],
                          listAttr=['sx', 'sy', 'sz'],
                          l=0,
                          k=0,
                          cb=0)
    log_debug('limit')

    # gimbal
    gimbalCtrl = pm.duplicate(jnts[id]['ikCtrl'], n='%s_ikGimbalCtrl' % id)[0]

    pm.parent(gimbalCtrl, jnts[id]['ikCtrl'])
    pm.xform('%s.cv[0:]' % gimbalCtrl.getShape(), s=(.8, .8, .8), r=1)
    pm.addAttr(jnts[id]['ikCtrl'], ln='gimbal_vis', at='bool', k=1)
    jnts[id]['ikCtrl'].gimbal_vis >> gimbalCtrl.visibility
    # store attr of gimbal on main ctrl
    pm.addAttr(jnts[id]['ikCtrl'], ln='gimbal', dt='string')
    jnts[id]['ikCtrl'].attr('gimbal').set('%s' % gimbalCtrl,
                                          k=0,
                                          l=0,
                                          type='string')
    log_debug('gimal done')

    # -- pole vector -- #
    jnts[id]['pvecCtrl'] = curveLib.createShapeCtrl(type='sphere',
                                                    name='%s_pvecCtrl' % id,
                                                    scale=scale,
                                                    color=color)
    # group and align
    zero = riggUtils.grpCtrl(jnts[id]['pvecCtrl'])
    log_debug('finding pole position')
    polePos = riggUtils.poleVectorPosition(
        startJnt=jnts['%s_leg01' % side]['ikJnt'],
        midJnt=jnts['%s_leg02' % side]['ikJnt'],
        endJnt=jnts['%s_foot01' % side]['ctrlJnt'],
        length=12,
        createLoc=0,
        createViz=0)
    log_debug('polepos is %s' % polePos)
    pm.xform(zero, ws=1, t=(polePos.x, polePos.y, polePos.z))
    riggUtils.grpIn('%s_leg_ctrlGrp' % side, zero)  # parent leg_ctrlGrp
    riggUtils.grpIn(parent, '%s_leg_ctrlGrp' % side)  # parent to DIRECTION

    # limit
    riggUtils.cleanUpAttr(sel=[jnts[id]['pvecCtrl']],
                          listAttr=['sx', 'sy', 'sz', 'rx', 'ry', 'rz'],
                          l=0,
                          k=0,
                          cb=0)

    log_info("Done create_ctrl_ik_leg()")

    return jnts