import pymel.core as pm import maya.OpenMaya as om import maya.mel as mel # ====================== 工具函数 ====================== def createGrp(grpName, parentGrp=None): if pm.objExists(grpName): om.MGlobal.displayWarning(f"'{grpName}' 已存在,跳过创建") return pm.PyNode(grpName) else: grp = pm.group(em=True, n=grpName) pm.select(cl=True) if parentGrp: pm.parent(grp, parentGrp) return grp def PointSurfaceMaxtriToAim(point_node_name): point_node = pm.PyNode(point_node_name) vector_node = pm.createNode("vectorProduct", n=f"{point_node_name}Vec") point_node.normal >> vector_node.input1 point_node.tangentV >> vector_node.input2 vector_node.operation.set(2) FourMatrix_node = pm.createNode("fourByFourMatrix", n=f"{point_node_name}FourMatrix") point_node.normalX >> FourMatrix_node.in00 point_node.normalY >> FourMatrix_node.in01 point_node.normalZ >> FourMatrix_node.in02 vector_node.outputX >> FourMatrix_node.in10 vector_node.outputY >> FourMatrix_node.in11 vector_node.outputZ >> FourMatrix_node.in12 point_node.normalizedTangentVX >> FourMatrix_node.in20 point_node.normalizedTangentVY >> FourMatrix_node.in21 point_node.normalizedTangentVZ >> FourMatrix_node.in22 point_node.positionX >> FourMatrix_node.in30 point_node.positionY >> FourMatrix_node.in31 point_node.positionZ >> FourMatrix_node.in32 decom_node = pm.createNode("decomposeMatrix", n=f"{point_node_name}Decom") FourMatrix_node.output >> decom_node.inputMatrix return decom_node # ====================== 控制器曲线 ====================== def makeCircleCtrl(name): ctrl = pm.circle(nr=(0,0,1), ch=0, r=0.7, n=name)[0] ctrlShape = ctrl.getShape() ctrlShape.overrideEnabled.set(1) ctrlShape.overrideColor.set(6) return ctrl def makeCubeCurveCtrl(name): pts = [ (0.5, 0.5, 0.5), (0.5, 0.5, -0.5), (-0.5, 0.5, -0.5), (-0.5, 0.5, 0.5), (0.5, 0.5, 0.5), (0.5, -0.5, 0.5), (-0.5, -0.5, 0.5), (-0.5, 0.5, 0.5), (-0.5, 0.5, -0.5), (-0.5, -0.5, -0.5), (-0.5, -0.5, 0.5), (0.5, -0.5, 0.5), (0.5, -0.5, -0.5), (-0.5, -0.5, -0.5), (-0.5, 0.5, -0.5), (0.5, 0.5, -0.5), (0.5, -0.5, -0.5) ] cubeCrv = pm.curve(d=1, p=pts, n=name) cubeCrvShape = cubeCrv.getShape() cubeCrvShape.overrideEnabled.set(1) cubeCrvShape.overrideColor.set(17) pm.makeIdentity(cubeCrv, apply=True, t=1, r=1, s=1) return cubeCrv def makeDiamondCurveCtrl(name): pts = [ (0, 0.5, 0), (0.5, 0, 0), (0, -0.5, 0), (-0.5, 0, 0), (0, 0.5, 0), (0, 0, 0.5), (0, -0.5, 0), (0, 0, -0.5), (0, 0.5, 0), (-0.5, 0, 0), (0, 0, -0.5), (0.5, 0, 0), (0, 0, 0.5), (0, 0.5, 0), (0, 0, 0.5), (-0.5, 0, 0) ] diaCrv = pm.curve(d=1, p=pts, n=name) diaCrvShape = diaCrv.getShape() diaCrvShape.overrideEnabled.set(1) diaCrvShape.overrideColor.set(13) pm.makeIdentity(diaCrv, apply=True, t=1, r=1, s=1) return diaCrv # ====================== 核心绑定逻辑 ====================== def build_ribbon_rig(prefix, ik_seg=7, fk_seg=9): pm.select(hi=True) ribbon_jnt = pm.ls(sl=True, type="joint") if not prefix: pm.warning("前缀不能为空!") return ribbon_rig_grp = createGrp(f"{prefix}_ribbon_rig_grp") prefix_rig_grp = createGrp(f"{prefix}_rig_grp", ribbon_rig_grp) prefix_org_cur_grp = createGrp(f"{prefix}_org_cur_grp", prefix_rig_grp) prefix_con_grp = createGrp(f"{prefix}_con_grp", prefix_rig_grp) prefix_mian_con_grp = createGrp(f"{prefix}_main_con_grp", prefix_con_grp) prefix_fk_con_grp = createGrp(f"{prefix}_fk_con_grp", prefix_con_grp) prefix_ik_con_grp = createGrp(f"{prefix}_ik_con_grp", prefix_con_grp) prefix_base_ik_con_grp = createGrp(f"{prefix}_base_ik_con_grp", prefix_con_grp) prefix_fk_to_ik_con_grp = createGrp(f"{prefix}_fk_to_ik_con_grp", prefix_con_grp) prefix_setup_grp = createGrp(f"{prefix}_setup_grp", prefix_rig_grp) prefix_setup_fk_con_grp = createGrp(f"{prefix}_setup_fk_con_grp", prefix_setup_grp) prefix_setup_ik_loft_jnt_grp = createGrp(f"{prefix}_setup_ik_loft_jnt_grp", prefix_setup_grp) prefix_setup_fk_loft_jnt_grp = createGrp(f"{prefix}_setup_fk_loft_jnt_grp", prefix_setup_grp) prefix_setup_org_jnt_grp = createGrp(f"{prefix}_setup_org_jnt_grp", prefix_setup_grp) prefix_setup_base_ik_jnt_grp = createGrp(f"{prefix}_setup_base_ik_jnt_grp", prefix_setup_grp) prefix_setup_ik_martix_grp = createGrp(f"{prefix}_setup_ik_matrix_grp", prefix_setup_grp) prefix_setup_fk_scale_grp = createGrp(f"{prefix}_setup_fk_scale_grp", prefix_setup_grp) ############ prefix_org_cur_grp.visibility.set(0) prefix_setup_grp.visibility.set(0) mian_con = makeCircleCtrl(f"{prefix}_mian_con") mian_grp1 = pm.group(mian_con, n=f"{mian_con}_extra_grp") mian_grp2 = pm.group(mian_grp1, n=f"{mian_con}_offset_grp") pm.delete(pm.parentConstraint(ribbon_jnt[0], mian_grp2, mo=0)) pm.setAttr(mian_con.scale,3,3,3) pm.select(mian_con) mel.eval("CenterPivot;") mel.eval("FreezeTransformations;") pm.select(cl=True) pm.parent(mian_grp2,f"{prefix}_main_con_grp") if not ribbon_jnt: pm.warning("请先选择需要绑定的骨骼!") return curve_points = [pm.xform(jnt, q=True, ws=True, t=True) for jnt in ribbon_jnt] org_cur = pm.curve(d=3, p=curve_points, n=f"{prefix}_org_curve") pm.delete(pm.parentConstraint(ribbon_jnt[0], f"{prefix}_org_cur_grp", mo=0)) pm.parent(org_cur, prefix_org_cur_grp) pm.select(org_cur) mel.eval("CenterPivot;") mel.eval("FreezeTransformations;") pm.select(cl=True) def createCurveAndLoft(loftName, curveName, conNum): rebuild_seg = max(conNum, 1) pm.rebuildCurve(org_cur, rpo=1, rt=0, end=1, kr=0, kcp=0, kep=1, s=rebuild_seg, d=3, tol=0.001) dup1 = pm.duplicate(org_cur)[0] dup2 = pm.duplicate(org_cur)[0] dup1.translateZ.set(1) dup2.translateZ.set(-1) loft = pm.loft(dup2, dup1, ch=0, u=1, c=0, ar=1, d=3, ss=1, n=f"{prefix}_{loftName}") pm.delete(dup1, dup2) pm.duplicateCurve(f"{prefix}_{loftName}.u[0.5]", rn=0, local=0, n=f"{prefix}_{curveName}") createCurveAndLoft("ik_loft", "ik_loft_curve", ik_seg-1) createCurveAndLoft("fk_loft", "fk_loft_curve", fk_seg-1) createCurveAndLoft("base_ik_loft", "base_ik_loft_curve", 2) pm.parent(f"{prefix}_ik_loft", f"{prefix}_ik_loft_curve", f"{prefix}_fk_loft", f"{prefix}_fk_loft_curve", f"{prefix}_base_ik_loft", f"{prefix}_base_ik_loft_curve", prefix_org_cur_grp) ik_loft = pm.PyNode(f"{prefix}_ik_loft") ik_loft_shape = ik_loft.getShape() ik_skin_jnt = [] for i in range(ik_seg): point_node = pm.createNode("pointOnSurfaceInfo", n=f"{prefix}_point_node{i}") ik_loft_shape.worldSpace[0] >> point_node.inputSurface point_node.parameterV.set(i/(ik_seg-1) if ik_seg>1 else 0) point_node.parameterU.set(0.5) pm.select(cl=True) ik_loft_skin_jnt = pm.joint(n=f"{prefix}_ik_loft_skin_jnt{i}") decom_node = PointSurfaceMaxtriToAim(f"{prefix}_point_node{i}") decom_node.outputTranslate >> ik_loft_skin_jnt.translate decom_node.outputRotate >> ik_loft_skin_jnt.rotate ik_con = makeCubeCurveCtrl(f"{prefix}_ik_set_con_{i}") ik_grp1 = pm.group(ik_con, n=f"{ik_con}_extra_grp") ik_grp2 = pm.group(ik_grp1, n=f"{ik_con}_offset_grp") pm.delete(pm.parentConstraint(ik_loft_skin_jnt, ik_grp2, mo=False)) pm.parentConstraint(ik_con, ik_loft_skin_jnt, mo=True) pm.scaleConstraint(ik_con, ik_loft_skin_jnt) pm.delete(f"{prefix}_point_node{i}Vec", f"{prefix}_point_node{i}FourMatrix", f"{prefix}_point_node{i}Decom", point_node) pm.parent(ik_loft_skin_jnt, prefix_setup_ik_loft_jnt_grp) pm.parent(ik_grp2, prefix_ik_con_grp) ik_skin_jnt.append(ik_loft_skin_jnt) pm.skinCluster(ik_skin_jnt, f"{prefix}_ik_loft", mi=1) fk_skin_jnt = [] for i in range(fk_seg): point_node = pm.createNode("pointOnSurfaceInfo", n=f"{prefix}_fk_point_node{i}") ik_loft_shape.worldSpace[0] >> point_node.inputSurface point_node.parameterV.set(i/(fk_seg-1) if fk_seg>1 else 0) point_node.parameterU.set(0.5) pm.select(cl=True) fk_loft_skin_jnt = pm.joint(n=f"{prefix}_fk_loft_skin_jnt{i}") decom_node = PointSurfaceMaxtriToAim(f"{prefix}_fk_point_node{i}") decom_node.outputTranslate >> fk_loft_skin_jnt.translate decom_node.outputRotate >> fk_loft_skin_jnt.rotate ik_con = makeDiamondCurveCtrl(f"{prefix}_fk_to_ik_con_{i}") ik_grp1 = pm.group(ik_con, n=f"{ik_con}_extra_grp") ik_grp2 = pm.group(ik_grp1, n=f"{ik_con}_offset_grp") fk_con = makeCircleCtrl(f"{prefix}_fk_con_{i}") fk_grp1 = pm.group(fk_con, n=f"{fk_con}_extra_grp") fk_grp2 = pm.group(fk_grp1, n=f"{fk_con}_offset_grp") pm.delete(pm.parentConstraint(fk_loft_skin_jnt, ik_grp2, mo=False)) pm.delete(pm.parentConstraint(fk_loft_skin_jnt, fk_grp2, mo=False)) pm.parentConstraint(ik_con, fk_loft_skin_jnt, mo=True) pm.scaleConstraint(ik_con, fk_loft_skin_jnt) pm.parentConstraint(fk_con, ik_grp2, mo=True) pm.scaleConstraint(fk_con, ik_grp2) pm.delete(f"{prefix}_fk_point_node{i}Vec", f"{prefix}_fk_point_node{i}FourMatrix", f"{prefix}_fk_point_node{i}Decom", point_node) pm.parent(fk_loft_skin_jnt, prefix_setup_fk_loft_jnt_grp) pm.parent(ik_grp2, prefix_fk_to_ik_con_grp) pm.parent(fk_grp2, prefix_fk_con_grp) if i > 0: pm.parent(fk_grp2, f"{prefix}_fk_con_{i-1}") fk_skin_jnt.append(fk_loft_skin_jnt) pm.skinCluster(fk_skin_jnt, f"{prefix}_fk_loft", mi=1, tsb=True) ikTOfk_num = fk_seg for i in range(fk_seg): ikTOfk_grp = pm.group(em=True, n=f"{prefix}_ik_matrix_to_fk_grp{i}") point_node = pm.createNode("pointOnSurfaceInfo", n=f"{prefix}_point_node{i}_ikTOfk") ik_loft_shape.worldSpace[0] >> point_node.inputSurface point_node.parameterV.set(i/(ikTOfk_num-1) if ikTOfk_num>1 else 0) point_node.parameterU.set(0.5) decom_node = PointSurfaceMaxtriToAim(f"{prefix}_point_node{i}_ikTOfk") decom_node.outputTranslate >> ikTOfk_grp.translate decom_node.outputRotate >> ikTOfk_grp.rotate pm.scaleConstraint(mian_con,ikTOfk_grp) pm.parent(ikTOfk_grp, prefix_setup_ik_martix_grp) fk2_con = makeCircleCtrl(f"{prefix}_fk_no_move_con{i}") fk2_grp1 = pm.group(fk2_con, n=f"{fk2_con}_extra_grp") fk2_grp2 = pm.group(fk2_grp1, n=f"{fk2_con}_offset_grp") pm.delete(pm.parentConstraint(ikTOfk_grp, fk2_grp2, mo=0)) pm.parentConstraint(ikTOfk_grp, fk2_con, mo=0) pm.scaleConstraint(ikTOfk_grp, fk2_con) if i > 0: pm.parent(fk2_grp2, f"{prefix}_fk_no_move_con{i-1}") else: pm.parent(fk2_grp2, prefix_setup_fk_con_grp) target_grp = pm.PyNode(f"{prefix}_fk_con_{i}_extra_grp") fk2_con.translate >> target_grp.translate fk2_con.rotate >> target_grp.rotate fk2_con.scale >> target_grp.scale jnt_num = len(ribbon_jnt) fk_loft = pm.PyNode(f"{prefix}_fk_loft") fk_loft_shape = fk_loft.getShape() for idx, jnt in enumerate(ribbon_jnt): pm.select(cl=True) fk_loft_jnt = pm.joint(n=f"{prefix}_org_skin_jnt{idx}") pm.delete(pm.parentConstraint(jnt, fk_loft_jnt, mo=0)) pm.parent(fk_loft_jnt, prefix_setup_org_jnt_grp) point_node = pm.createNode("pointOnSurfaceInfo", n=f"{prefix}_{fk_loft_jnt}_pointOnSur") fk_loft_shape.worldSpace[0] >> point_node.inputSurface point_node.parameterV.set(idx/(jnt_num-1) if jnt_num>1 else 0) point_node.parameterU.set(0.5) decom_node = PointSurfaceMaxtriToAim(point_node) decom_node.outputTranslate >> fk_loft_jnt.translate decom_node.outputRotate >> fk_loft_jnt.rotate pm.parentConstraint(fk_loft_jnt, jnt, mo=1) pm.scaleConstraint(fk_loft_jnt, jnt) ############## A_point_node = pm.createNode("pointOnSurfaceInfo", n=f"{prefix}_sc_A_pointOnSur") B_point_node = pm.createNode("pointOnSurfaceInfo", n=f"{prefix}_sc_B_pointOnSur") fk_loft_shape.worldSpace[0] >> A_point_node.inputSurface fk_loft_shape.worldSpace[0] >> B_point_node.inputSurface A_point_node.parameterV.set(idx/(jnt_num-1) if jnt_num>1 else 0) A_point_node.parameterU.set(0.0) B_point_node.parameterV.set(idx/(jnt_num-1) if jnt_num>1 else 0) B_point_node.parameterU.set(1.0) A_sc_null = pm.group(em = True, n = "f{prefix}_A_sc_null") B_sc_null = pm.group(em = True, n = "f{prefix}_B_sc_null") A_point_node.position >>A_sc_null.translate B_point_node.position >>B_sc_null.translate dis_node = pm.createNode("distanceBetween", n=f"{prefix}_dis_node") A_sc_null.translate >> dis_node.point1 B_sc_null.translate >> dis_node.point2 A_to_B_dis = dis_node.distance.get() sc_multi = pm.createNode("multiplyDivide", n=f"{prefix}_sc_multi") sc_multi.operation.set(2) dis_node.distance >> sc_multi.input1X sc_multi.input2X.set(A_to_B_dis) sc_multi.outputX >> fk_loft_jnt.scaleX sc_multi.outputX >> fk_loft_jnt.scaleY sc_multi.outputX >> fk_loft_jnt.scaleZ pm.parent(A_sc_null,B_sc_null,prefix_setup_fk_scale_grp) # ====================== 8. Base IK 根部控制器(修复版) ====================== base_ik_jnts = [] Base_IK_loft = pm.PyNode(f"{prefix}_base_ik_loft") Base_IK_shape = Base_IK_loft.getShape() # 根部 2 点固定基座控制器 for i in range(3): pm.select(cl=True) # 带前缀命名 base_jnt = pm.joint(n=f"{prefix}base_IK_jnt{i}") base_con = makeCubeCurveCtrl(f"{prefix}base_IK_con{i}") base_mid_grp = pm.group(base_con, n=f"{base_con}_extra_grp") base_grp = pm.group(base_mid_grp, n=f"{base_con}_offset_grp") pm.setAttr(base_con.scale,2,2,2) pm.select(base_con) mel.eval("CenterPivot;") mel.eval("FreezeTransformations;") pm.select(cl=True) # 曲面定位 point_node = pm.createNode("pointOnSurfaceInfo", n=f"{prefix}BASE_point_node{i}") Base_IK_shape.worldSpace[0] >> point_node.inputSurface point_node.parameterV.set(i/2) point_node.parameterU.set(0.5) decom_node = PointSurfaceMaxtriToAim(point_node.name()) decom_node.outputTranslate >> base_grp.translate decom_node.outputRotate >> base_grp.rotate # 约束 pm.parentConstraint(base_con, base_jnt, mo=False) pm.scaleConstraint(base_con, base_jnt) pm.parent(base_grp, prefix_base_ik_con_grp) pm.parent(base_jnt, prefix_setup_base_ik_jnt_grp) base_ik_jnts.append(base_jnt) pm.delete(f"{prefix}BASE_point_node{i}Vec", f"{prefix}BASE_point_node{i}FourMatrix", f"{prefix}BASE_point_node{i}Decom", point_node) for i in range(ik_seg): point_node = pm.createNode("pointOnSurfaceInfo", n=f"{prefix}BASE_point_node{i}") Base_IK_shape.worldSpace[0] >> point_node.inputSurface point_node.parameterV.set(i/(ik_seg-1) if ik_seg>1 else 0) point_node.parameterU.set(0.5) IK_grp2 =pm.PyNode(f"{prefix}_ik_set_con_{i}_offset_grp") decom_node = PointSurfaceMaxtriToAim(point_node.name()) decom_node.outputTranslate >> IK_grp2.translate decom_node.outputRotate >> IK_grp2.rotate pm.scaleConstraint(mian_con,IK_grp2) pm.skinCluster(base_ik_jnts, Base_IK_loft, mi=1, tsb=True) pm.confirmDialog(title='成功', message=f'{prefix} 丝带绑定生成完毕!', button=['OK']) pm.parentConstraint(mian_con,f"{prefix}_base_ik_con_grp",mo = 1) pm.scaleConstraint(mian_con,f"{prefix}_base_ik_con_grp") # ====================== UI 窗口 ====================== def show_ribbon_rig_ui(): if pm.window("ribbonRigUI", exists=True): pm.deleteUI("ribbonRigUI") with pm.window("ribbonRigUI", title="丝带绑定工具 v2.0", width=320, height=200) as win: pm.columnLayout(adj=True, rowSpacing=10) pm.text(label="=== 丝带IK/FK绑定工具 ===", font="boldLabelFont", h=25) pm.separator() pm.rowLayout(numberOfColumns=2, columnWidth2=(100,180)) pm.text(label="绑定前缀:") prefix_field = pm.textField(text="shengzi") pm.setParent('..') pm.rowLayout(numberOfColumns=2, columnWidth2=(100,180)) pm.text(label="IK 分段:") ik_seg_field = pm.intField(value=7, min=2, max=20) pm.setParent('..') pm.rowLayout(numberOfColumns=2, columnWidth2=(100,180)) pm.text(label="FK 分段:") fk_seg_field = pm.intField(value=9, min=2, max=30) pm.setParent('..') pm.separator() pm.button(label="👉 选择骨骼后点击生成", command=lambda x: build_ribbon_rig( prefix=pm.textField(prefix_field, q=True, text=True), ik_seg=pm.intField(ik_seg_field, q=True, value=True), fk_seg=pm.intField(fk_seg_field, q=True, value=True) ), h=40) pm.text(label="使用前请先选择骨骼链", backgroundColor=[0.15,0.25,0.15]) pm.showWindow(win) # 启动窗口 show_ribbon_rig_ui()曲线工具,备用版
张小明
前端开发工程师
【三维目标跟踪,EKF与RTS】三维非线性目标跟踪的MATLAB仿真程序,通过扩展卡尔曼滤波(EKF),在滤波结果的基础上利用 RTS平滑算法进行后向平滑
三维非线性目标跟踪仿真程序,通过扩展卡尔曼滤波(EKF) 对目标状态进行在线估计,并在滤波结果的基础上利用 RTS(Rauch–Tung–Striebel)平滑算法进行后向平滑,以进一步提升轨迹估计精度。运行后输…
别再只懂用Dashboard了!RocketMQ mqadmin命令行工具在CI/CD与自动化运维中的妙用
RocketMQ mqadmin命令行工具:解锁自动化运维的终极武器 在当今快节奏的DevOps环境中,图形化界面虽然直观,但往往成为自动化流程中的瓶颈。RocketMQ的mqadmin命令行工具正是为打破这一瓶颈而生,它让消息队列管理真正融入CI/CD流水线…
[驱动]BL0937:基于HC32L130的功率计量模块驱动设计与实现
1. BL0937电能计量芯片与HC32L130单片机简介 BL0937是一款专门用于电能计量的芯片,它能够精确测量电压、电流和功率等参数。这款芯片在智能插座、能耗监测设备等领域应用广泛。而HC32L130是华大半导体推出的一款低功耗单片机,具有丰富的外设接口和出色的…
nli-distilroberta-base部署指南:在CentOS7系统下的Docker化部署实践
nli-distilroberta-base部署指南:在CentOS7系统下的Docker化部署实践 1. 引言 最近在帮几个企业客户部署自然语言推理模型时,发现很多团队在CentOS生产环境上部署nli-distilroberta-base模型时遇到各种环境问题。这个轻量级的RoBERTa变体模型虽然体积小…
企业微信如何查看员工聊天记录?
需要开通会话存档后,才可以查看员工的聊天记录。流程如下: 1:进入一维助手官方网站。或者在企微应用市场搜索:一维助手。2:进入企业微信官方后台--应用管理--一维助手SCRM内。 点击授权信息--数据与智能专区权限。 如果…
7-Zip开源压缩工具终极指南:解决你文件管理的五大痛点
7-Zip开源压缩工具终极指南:解决你文件管理的五大痛点 【免费下载链接】7z 7-Zip Official Chinese Simplified Repository (Homepage and 7z Extra package) 项目地址: https://gitcode.com/gh_mirrors/7z1/7z 还在为电脑硬盘空间不足而烦恼?需要…