创建翅骨FK绑定
- 创建FK骨骼
- 创建FK控制器
- 创建简模
- 创建羽毛骨骼
- 毛囊约束羽毛骨骼
创建羽毛IK绑定
- 创建5条曲线,两条前端(跟随曲线,切线曲线),三条末端(提取曲线,IK曲线,动力学曲线)
- 末端曲线从简模提取一条,复制两次
- 跟随曲线吸附骨骼创建cv点,切线曲线复制跟随曲线并重建减少cv点
- 切线曲线.cv点吸附到跟随曲线上
- 使用羽毛骨骼的约束跟随曲线
- 创建次级蒙皮骨骼,使次级骨骼链与原始骨骼长度一致
方向目标约束
羽毛骨骼底部移动位置到底部曲线上
为羽毛骨骼创建约束定位
- 自身位置创建两个loc(pos_loc, tan_loc)打组P给羽毛骨骼父物体,用约束用的毛囊点约束loc
使用切线曲线(cv点少的)切线约束tan_loc, z轴为前方向,y轴为上方向, pos_loc作为向上物体
- 主要用来确定tan_loc的方向,方便做目标约束
末端曲线上创建目标约束定位器
- 创建定位器aim_loc,捕捉到末端羽毛骨骼位置,并使用ik曲线约束位置,方向使用tan_loc进行目标约束
aim_loc目标约束羽毛骨骼,tan_loc.y作为上方向旋转轴向
给末端曲线创建簇控制器,第一层控制动力学曲线,第二层同时控制IK曲线及动力学曲线
将末端曲线的IK控制器吸附到提取曲线上
次级骨骼绑定
- 创建次级控制关节,复制一套作为ik动力学,
- fk次级骨骼转控制器(父子关系), 5个父层级connect, rot_all, rot, constrain, fk_ctrl
- 使用ik属性连接控制fk控制器顶层(父子约束更卡一些)
- 羽毛骨骼末端控制器添加root_rotx, root_roty, root_rotz属性,rot属性连接根骨骼控制器的rota层级,实现控制根骨骼的旋转
- 羽毛骨骼末端控制器添加rotx, roty, rotz属性,rot属性连接除根骨骼控制器的rotb层级,实现控制整体骨骼的旋转
创建动力学绑定
- 为每条次级骨骼创建曲线,cv点吸附到骨骼一致,为所有曲线创建动力学
- 次级骨骼创建样条ik用动力学输出曲线作为曲线
- 为动力学初始曲线创建前两个cv点的簇,并添加loc,P给羽毛关节(非次级骨骼)根骨骼
- 为动力学初始曲线剩下的cv点创建簇,创建控制器
- 使用羽毛根关节目标约束上一步创建的末端控制器的组, 向上物体为tan_loc
- 使用末端控制器及第二个cv点的loc父子约束中间控制器(保持偏移),修改两个约束物体的权重
- 羽毛末端关节处创建loc,p给羽毛关节(非次级骨骼)根骨骼,创建第二个loc,吸附到动力学曲线上,用第二个loc点约束第一个loc
- 在次级末端关节处创建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