翅膀绑定


创建翅骨FK绑定

  1. 创建FK骨骼
  2. 创建FK控制器
  3. 创建简模
  4. 创建羽毛骨骼
  5. 毛囊约束羽毛骨骼

创建羽毛IK绑定

  1. 创建5条曲线,两条前端(跟随曲线,切线曲线),三条末端(提取曲线,IK曲线,动力学曲线)
    1. 末端曲线从简模提取一条,复制两次
    2. 跟随曲线吸附骨骼创建cv点,切线曲线复制跟随曲线并重建减少cv点
  2. 切线曲线.cv点吸附到跟随曲线上
  3. 使用羽毛骨骼的约束跟随曲线
  4. 创建次级蒙皮骨骼,使次级骨骼链与原始骨骼长度一致

方向目标约束

  1. 羽毛骨骼底部移动位置到底部曲线上

  2. 为羽毛骨骼创建约束定位

    1. 自身位置创建两个loc(pos_loc, tan_loc)打组P给羽毛骨骼父物体,用约束用的毛囊点约束loc
  3. 使用切线曲线(cv点少的)切线约束tan_loc, z轴为前方向,y轴为上方向, pos_loc作为向上物体

    1. 主要用来确定tan_loc的方向,方便做目标约束
  4. 末端曲线上创建目标约束定位器

    1. 创建定位器aim_loc,捕捉到末端羽毛骨骼位置,并使用ik曲线约束位置,方向使用tan_loc进行目标约束
  5. aim_loc目标约束羽毛骨骼,tan_loc.y作为上方向旋转轴向

  6. 给末端曲线创建簇控制器,第一层控制动力学曲线,第二层同时控制IK曲线及动力学曲线

  7. 将末端曲线的IK控制器吸附到提取曲线上

次级骨骼绑定

  1. 创建次级控制关节,复制一套作为ik动力学,
  2. fk次级骨骼转控制器(父子关系), 5个父层级connect, rot_all, rot, constrain, fk_ctrl
  3. 使用ik属性连接控制fk控制器顶层(父子约束更卡一些)
  4. 羽毛骨骼末端控制器添加root_rotx, root_roty, root_rotz属性,rot属性连接根骨骼控制器的rota层级,实现控制根骨骼的旋转
  5. 羽毛骨骼末端控制器添加rotx, roty, rotz属性,rot属性连接除根骨骼控制器的rotb层级,实现控制整体骨骼的旋转

创建动力学绑定

  1. 为每条次级骨骼创建曲线,cv点吸附到骨骼一致,为所有曲线创建动力学
  2. 次级骨骼创建样条ik用动力学输出曲线作为曲线
  3. 为动力学初始曲线创建前两个cv点的簇,并添加loc,P给羽毛关节(非次级骨骼)根骨骼
  4. 为动力学初始曲线剩下的cv点创建簇,创建控制器
  5. 使用羽毛根关节目标约束上一步创建的末端控制器的组, 向上物体为tan_loc
  6. 使用末端控制器及第二个cv点的loc父子约束中间控制器(保持偏移),修改两个约束物体的权重
  7. 羽毛末端关节处创建loc,p给羽毛关节(非次级骨骼)根骨骼,创建第二个loc,吸附到动力学曲线上,用第二个loc点约束第一个loc
  8. 在次级末端关节处创建loc,P给羽毛末端关节,用次级末端loc点约束IK控制器

代码

import pymel.core as pm
from zy_utils.ctrl_funcs import create_ctrl
from zy_utils.snap_funcs import snap_obj_on_mesh, snap_obj_on_curve
from zy_utils.general_funcs import get_pynode

# 创建loc,控制毛囊的uv,调整骨骼位置的同时保证骨骼在平面上
name = 'L_yumao'
face = pm.PyNode('L_wing_proxy_mesh')
for jnt in pm.ls('{}_*_jnt'.format(name)):
    world = pm.spaceLocator(n=jnt.name() + '_loc')
    pm.delete(pm.parentConstraint(jnt, world))
    follic = snap_obj_on_mesh(jnt, face, True, rot=False)

    if not pm.pluginInfo('nearestPointOnMesh', q=1, l=1):
        pm.loadPlugin('nearestPointOnMesh')
    near_on_mesh = pm.createNode('nearestPointOnMesh')
    world.t >> near_on_mesh.inPosition
    face.worldMesh >> near_on_mesh.inMesh
    near_on_mesh.parameterU >> follic.parameterU
    near_on_mesh.parameterV >> follic.parameterV

# 创建ik骨骼链
for sel_jnt in pm.selected():
    num = 5
    name = ''

    end_jnt = sel_jnt.getChildren()[0]
    pm.select(cl=1)
    jnt = pm.joint(n=sel_jnt.replace('_jnt', '_ik_{:0>2}_jnt'.format(i)))
    pm.delete(pm.parentConstraint(sel_jnt, jnt))
    length = end_jnt.t.get().length() / float(num - 1)
    ik_jnt = [jnt]
    ik_pos = [jnt.getTranslation(space='world')]
    for i in range(2, num + 1):
        pm.select(jnt)
        jnt = pm.joint(n=sel_jnt.replace('_jnt', '_ik_{:0>2}_jnt'.format(i)))
        jnt.tx.set(length)
        ik_jnt.append(jnt)
        ik_pos.append(jnt.getTranslation(space='world'))

    ik_curve = pm.curve(n=name+'_ik_curve')
    ik_handle = pm.ikHandle(sol='ikSplineSolver', ccv=0,  scv=0, pcv=0, sj=ik_jnt[0], ee=ik_jnt[-1],
                            c=ik_curve)

# 创建follow,tangent曲线复制后转5cv点3度曲线,
# follow约束tangent曲线
name = 'L_yumao'
sel = pm.selected()
cv_grp = get_pynode('{}_curve_grp'.format(name))
control_grp = get_pynode('{}_control_loc_grp'.format(name))
all_pos = []
for jnt in sel:
    pos = pm.xform(jnt, q=1, ws=1, t=1)
    all_pos.append(pos)
follow_cv = pm.curve(d=3, p=all_pos, n='{}_follow_cv'.format(name))
follow_cv.setParent(cv_grp)
tangent_cv = pm.duplicate(follow_cv, n='{}_tangent_cv'.format(name))[0]
tangent_cv.setParent(cv_grp)
pm.rebuildCurve(tangent_cv, rpo=1, kr=0, s=4, d=3)
pm.delete(tangent_cv.cv[1])
pm.delete(tangent_cv.cv[4])
for i, jnt in enumerate(sel):
    jnt.t >> follow_cv.controlPoints[i]

all_loc = []
cv_info = pm.createNode('curveInfo')
tangent_cv.worldSpace >> cv_info.inputCurve
for cv in tangent_cv.cv[:]:
    index = cv.index()
    loc = pm.spaceLocator(n=tangent_cv.name() + '_control_loc')
    cv_info.controlPoints[index] >> loc.t
    loc.t.disconnect()
    loc.t >> tangent_cv.controlPoints[index]
    snap_loc = snap_obj_on_curve(loc, follow_cv, con=False)
    snap_loc.t.listConnections(s=1, d=0, p=1)[0] >> loc.t
    snap_loc.r.listConnections(s=1, d=0, p=1)[0] >> loc.r
    pm.delete(snap_loc)
    pm.parent(loc, control_grp)

# 复制deform_curve命名为ik_curve,并将ik_curve吸附到deform_curve上
name = 'L_yumao'
sel = pm.selected()
cv_grp = get_pynode('{}_curve_grp'.format(name))
control_grp = get_pynode('{}_control_loc_grp'.format(name))
deform_curve = pm.PyNode('')
ik_curve = pm.duplicate(deform_curve, n='{}_tangent_cv'.format(name))[0]
tangent_cv.setParent(cv_grp)
## 为ik_curve创建控制器
all_loc = []
cv_info = pm.createNode('curveInfo')
tangent_cv.worldSpace >> cv_info.inputCurve
for cv in tangent_cv.cv[:]:
    index = cv.index()
    loc = pm.spaceLocator(n=tangent_cv.name() + '_control_loc')
    cv_info.controlPoints[index] >> loc.t
    loc.t.disconnect()
    loc.t >> tangent_cv.controlPoints[index]

    ctrl_fix = '_ctrl'
    grp_list = ['_ctrl_SN', '_ctrl_PH', '_connect', '_xform']
    grp, ctrl = create_ctrl('{}_{:0>3}_ik_cluster{:0>2}'.format(name, i, index + 1),
                            ctrl_fix=ctrl_fix, shape='A002_cube',
                            grp=grp_list, scale=5, vis_enable=True, scale_enable=True)



    snap_loc = snap_obj_on_curve(loc, follow_cv, con=False)
    snap_loc.t.listConnections(s=1, d=0, p=1)[0] >> loc.t
    snap_loc.r.listConnections(s=1, d=0, p=1)[0] >> loc.r
    pm.delete(snap_loc)
    pm.parent(loc, control_grp)

# 手动缩短羽毛骨骼到曲线上
# 创建羽毛骨骼的毛囊并吸附到mesh上,创建羽毛骨骼的目标约束loc,创建animloc并吸附到ik曲线
mesh = pm.PyNode('L_wing_proxy_mesh')
ik_cv = pm.PyNode('wing_ik_cv')
name = 'L_yumao_2'
tangent_cv = pm.PyNode('{}_tangent_cv'.format(name))

sel = pm.selected()
pm.select(cl=1)
for jnt in sel:
    end_jnt = jnt.getChildren()[0]
    anim_loc_grp = get_pynode('{}_anim_loc_grp'.format(name))
    anim_loc = pm.spaceLocator(n=jnt.replace('_jnt', '_anim_loc'))
    anim_loc.setParent(anim_loc_grp)
    pm.delete(pm.parentConstraint(end_jnt, anim_loc))
    follic = snap_obj_on_mesh(jnt, mesh, rot=False)
    parent = jnt.getParent()
    grp = pm.createNode('transform', n=jnt.replace('_jnt', '_tan_grp'))
    grp.setParent(parent)

    tan_loc = pm.spaceLocator(n=jnt.replace('_jnt', '_tan_loc'))
    tan_loc.setParent(grp)
    pos_loc = pm.spaceLocator(n=jnt.replace('_jnt', '_pos_loc'))
    pos_loc.setParent(grp)
    pm.delete(pm.parentConstraint(jnt, grp))
    pm.delete(pm.parentConstraint(jnt, tan_loc))
    pm.delete(pm.parentConstraint(jnt, pos_loc))

    pm.pointConstraint(follic, tan_loc)
    pm.pointConstraint(follic, pos_loc)
    grp.v.set(0)
    # 上方向需要作出对应修改
    pm.tangentConstraint(tangent_cv, tan_loc, aimVector=[1, 0, 0], upVector=[0, 1, 0],
                         worldUpType='objectrotation', worldUpVector=[0, 1, 0],
                         worldUpObject=pos_loc)

    pm.aimConstraint(anim_loc, jnt, aimVector=[1, 0, 0], upVector=[0, 1, 0],
                     worldUpType='objectrotation', worldUpVector=[1, 0, 0],
                     worldUpObject=tan_loc)

# ik_connect曲线创建簇并进行约束
for i in range(1, 34):
    curve = pm.PyNode('{}_{:0>3}_ik_connectCV'.format(name, i))
    grp = pm.createNode('transform', n='{}_{:0>3}_connect_cluster_grp'.format(name, i))
    for index in range(5):
        cluster = pm.cluster('curve.cv[index]')[1]
        cluster.rename('{}_{:0>3}_connect_cluster{:0>2}'.format(name, i, index))
        cluster.setParent(grp)
    tan_loc = pm.PyNode('{}_{:0>3}_jnt_tan_loc'.format(name, i))
    pm.aimConstraint(tan_loc, cluster, aimVector=[1, 0, 0], upVector=[0, 1, 0],
                     worldUpType='objectrotation', worldUpVector=[0, 0, 1],
                     worldUpObject='tan_loc')
    pm.pointConstraint('{}_{:0>3}_ik_end_loc'.format(name, i), '{}_{:0>3}_connect_cluster05'.format(name, i), mo=1)
    pm.parentConstraint('{}_{:0>3}_jnt'.format(name, i), '{}_{:0>3}_connect_cluster01'.format(name, i), mo=1)
    pm.parentConstraint('{}_{:0>3}_connect_cluster'.format(name, i), '{}_{:0>3}_connect_cluster02'.format(name, i),
                        mo=1)
    con = pm.parentConstraint('{}_{:0>3}_jnt'.format(name, i),
                              '{}_{:0>3}_connect_cluster05'.format(name, i),
                              '{}_{:0>3}_connect_cluster03'.format(name, i), mo=1)
    con.attr('{}_{:0>3}_jntW0'.format(name, i)).set(0.4)
    con.attr('{}_{:0>3}_connect_cluster05W1'.format(name, i)).set(0.6)
    con = pm.parentConstraint('{}_{:0>3}_jnt'.format(name, i),
                              '{}_{:0>3}_connect_cluster05'.format(name, i),
                              '{}_{:0>3}_connect_cluster04'.format(name, i), mo=1)
    con.attr('{}_{:0>3}_jntW0'.format(name, i)).set(0.6)
    con.attr('{}_{:0>3}_connect_cluster05W1'.format(name, i)).set(0.4)

# ik骨骼链接fk骨骼
for jnt in pm.ls('L_yumao_*_ik_*_jnt'.format(i)):
    pm.connectAttr(jnt.t, jnt.replace('_ik', '').replace('_jnt', name) + '.t', f=1)
    pm.connectAttr(jnt.r, jnt.replace('_ik', '').replace('_jnt', name) + '.r', f=1)
    pm.connectAttr(jnt.s, jnt.replace('_ik', '').replace('_jnt', name) + '.s', f=1)

# 创建ik,并添加ik高级扭曲
master = pm.PyNode('Master_ctrl')
for i in range(1, 34):
    # 创建ik_spline_cv曲线,复制connect曲线
    all_pos = []
    name = 'L_yumao_4_{:0>3}'.format(i)
    jnt_name = ''
    all_jnt = pm.ls(jnt_name)
    for jnt in all_jnt:
        pos = pm.xform(jnt, q=1, ws=1, t=1)
        all_pos.append(pos)
    ik_spline_cv = pm.curve(d=3, p=all_pos, n='{}_ik_spline_cv'.format(name))
    connect_cv = pm.duplicate(follow_cv, n='{}_connect'
                                           '_cv'.format(name))[0]

    # 控制器用anim_loc约束进行父子约束(跳过旋转),用tan_loc进行目标约束,向上旋转物体也用tan_loc

    # 创建ik

    # 添加ik高级扭曲
    ikhand = pm.PyNode('{:0>3}_ik_handle'.format(i))
    ikhand.dTwistControlEnable.set(1)
    ikhand.dWorldUpType.set(3)
    ikhand.dForwardAxis.set(0)
    ikhand.dWorldUpAxis.set(0)
    ikhand.dWorldUpVector.set(0, 1, 0)
    pm.connectAttr('tan_loc.worldMatrix', ikhand.dWorldUpMatrix)

    # 添加全局缩放
    # 初始长度除以connect的长乘以motionpath的初始uValue
    # ik曲线长度关联缩放
    ik_curve = pm.PyNode('{}_{:0>3}_ik_splieCV'.format(name, i))
    connect_curve = pm.PyNode('{}_{:0>3}_ik_connectCV'.format(name, i))

    near_point = pm.createNode('nearestPointOnCurve', n='temp_near_point')
    connect_curve >> near_point.inputCurve

    cluster_grp = get_pynode('{}_{:0>3}_ik_cluster_grp'.format(name, i))
    cluster_grp.v.set(0)
    for index in range(5):
        cluster = pm.cluster(ik_curve.cv[index])[1]
        cluster.rename('{}_{:0>3}_ik_cluster{:0>2}'.format(name, i, index))
        cluster.setParent(cluster_grp)

        jnt = pm.PyNode('{}_{:0>3}_{:0>2}_connect'.format(name, i, index))
        point = jnt.getTranslation(space='world')
        rot = jnt.getRotation(space='world')
        near_point.inPosition.set(point)
        orig = near_point.parameter.get() / connect_curve.maxValue.get()
        ctrl_fix = '_ctrl'
        grp_list = ['_ctrl_SN', '_ctrl_PH', '_connect', '_xform']
        grp, ctrl = create_ctrl('{}_{:0>3}_ik_cluster{:0>2}'.format(name, i, index + 1),
                                ctrl_fix=ctrl_fix, shape='A002_cube', t_offset=point, rot_offset=rot,
                                grp=grp_list, scale=5, vis_enable=True, scale_enable=True)

        pm.pointConstraint(ctrl, cluster)
        motion_path = pm.createNode('motionPath', n='{}_4_{:0>3}_{:0>2}'.format(name, i, index))
        connect_curve.worldSpace >> motion_path.geometryPath
        motion_path.allCoordinates >> grp.t
        motion_path.r >> grp.r

        cv_info = pm.createNode('curveInfo', n='{}_{:0>3}_ik_connectCV_info'.format(name, i))
        connect_curve.worldSpace >> cv_info.inputCurve
        mult1 = pm.createNode('multiplyDivide')  # length / orig_length
        mult2 = pm.createNode('multiplyDivide')  # orig / mult1
        mult3 = pm.createNode('multiplyDivide')  # value * global_scale
        mult4 = pm.createNode('multiplyDivide')  # value * elbow_scale

        mult1.operation.set(2)
        mult2.operation.set(2)

        cv_info.arcLength >> mult1.input1X
        mult1.input2X.set(cv_info.arcLength.get())
        mult2.input1X.set(orig)
        mult1.outputX >> mult2.ipnut2X
        mult2.outputX >> mult3.input1X
        master.global_scale >> mult3.ipnut2X
        mult3.outputX >> mult4.input1X
        mult4.input2X.set(1)
        mult4.outputX >> motion_path.uValue
        motion_path.fractionMode.set(1)
    pm.delete(near_point)

    # ik缩放关联fk控制器
    cv = pm.PyNode('curve1')
    cvinfo = pm.createNode('curveInfo')
    mult = pm.createNode('multiplyDivide')
    mult.operation.set(2)
    plus = pm.createNode('plusMinusAverage')

    cv.worldSpace[0] >> cvinfo.inputCurve
    cvinfo.arcLength >> mult.input1X
    length = cvinfo.arcLength.get()
    mult.input2X.set(length)
    mult.outputX >> plus.input1D
    jnt_list = []
    for jnt in jnt_list:
        plus.output1D >> jnt.sx
        plus.output1D >> jnt.sy
        plus.output1D >> jnt.sz
    # 添加sin函数表达式
    ex_txt = ''
    pm.expression(s=ex_txt, ae=1, uc='all')

# 创建anim_loc吸附到ik曲线上,并用tan_loc进行方向约束
for i in sel:
    name = i.name()[:13]
    anim_loc = '{}_anim_loc'.format(name)
    tan_loc = '{}_tan_loc'.format(name)
    pm.aimConstraint(tan_loc, anim_loc, aimVector=[-1, 0, 0], upVector=[0, 0, 1],
                     worldUpType='objectrotation', worldUpVector=[1, 0, 0],
                     worldUpObject=tan_loc, mo=1)

# 为ik曲线创建簇+控制器, 前两个簇用羽毛骨骼约束,最后一个簇创建ik控制器
for i in sel:
    name = i.name()[:13]
    jnt = '{}_jnt'.format(name)
    for num in range(1, 3):
        cluster = '{}_ik_cv_cluster{:0>2}_xform'.format(name, num)
        pm.parentConstraint(jnt, cluster, mo=1)

    cluster5 = '{}_ik_cv_cluster05_xform'.format(name)
    anim_loc = '{}_anim_loc'.format(name)
    ik_end_loc = pm.spaceLocator(n='{}_ik_end_loc'.format(name))
    pm.parentConstraint(ik_end_loc, cluster5, mo=1)
    tan_loc = '{}_tan_loc'.format(name)
    pm.aimConstraint(tan_loc, anim_loc, aimVector=[-1, 0, 0], upVector=[0, 1, 0],
                     worldUpType='objectrotation', worldUpVector=[1, 0, 0],
                     worldUpObject=tan_loc, mo=1)

    cluster3 = '{}_ik_cv_cluster03_xform'.format(name)
    cluster4 = '{}_ik_cv_cluster04_xform'.format(name)
    ctrl2 = '{}_ik_cv_cluster02_ctrl'.format(name)
    ctrl5 = '{}_ik_cv_cluster03_ctrl'.format(name)

    con = pm.parentConstraint(ctrl2, ctrl5, cluster3, mo=1)
    con.attr('{}W0'.format(ctrl2)).set(0.6)
    con.attr('{}W1'.format(ctrl5)).set(0.4)

    con = pm.parentConstraint(ctrl2, ctrl5, cluster4, mo=1)
    con.attr('{}W0'.format(ctrl2)).set(0.4)
    con.attr('{}W1'.format(ctrl5)).set(0.6)


# 蒙皮骨骼创建fk控制器
for jnt in pm.ls(sl=1):
    parent = jnt.getParent()
    jnt.drawStyle.set(2)
    grp_list = 'fk_ctrl', 'constrain', 'rot', 'rot_all', 'connect'
    grp, ctrl = create_ctrl('{}_{:0>3}_ik_cluster{:0>2}'.format(name, i, index + 1),
                    ctrl_fix=ctrl_fix, shape='A002_cube',
                    grp=grp_list, scale=5, vis_enable=True, scale_enable=True)
    pm.delete(pm.parentConstraint(jnt, grp))
    grp.setParent(parent)
    jnt.setParent(grp)
    jnt = grp

评论
  目录