Banner image of the blog
0 0 分钟

Maya传递融合变形插件

2025-08-12 Maya 编辑
Maya传递融合变形插件

  1. 添加了镜像轴向选择:在“镜像”按钮上方,新增了X、Y、Z三个轴向的单选框,您可以选择沿哪个轴进行镜像。
  2. 界面完全中文化:所有的UI标签、按钮文字、提示信息和日志都已翻译成中文,使其更易于使用。

如何使用:

  1. 复制下方的所有 Python 代码。
  2. 在 Autodesk Maya 中,打开脚本编辑器 (Windows > General Editors > Script Editor)。
  3. 新建一个 Python 标签页。
  4. 将代码粘贴进去并按 Ctrl+Enter (或点击执行按钮) 运行脚本。
  5. 一个名为“克隆融合变形”的中文界面窗口将会出现。

Python 脚本

# -*- coding: utf-8 -*-

import maya.cmds as cmds
import maya.mel as mel

def create_clone_blendshape_tool():
    
    win_name = "CloneBlendShapePyUI_CN"
    win_title = u"克隆融合变形"

    if cmds.window(win_name, exists=True):
        cmds.deleteUI(win_name, window=True)

    def find_first_deformer_of_type(geometry, deformer_type):
        history = cmds.listHistory(geometry, pdo=True, il=2)
        if not history: return None
        for node in history:
            if cmds.nodeType(node) == deformer_type:
                return node
        return None

    def clone_blendshape(*args):
        selection = cmds.ls(sl=True)
        if len(selection) != 2:
            cmds.warning(u"请选择两个物体:先选择目标物体,再选择带有融合变形的源物体。")
            return

        target_geo, source_geo = selection[0], selection[1]
        bs_node = find_first_deformer_of_type(source_geo, "blendShape")
        if not bs_node:
            cmds.warning(u"源物体上没有找到 blendShape 节点。")
            return

        target_aliases = cmds.listAttr(f"{bs_node}.w", multi=True)
        if not target_aliases:
            cmds.warning(u"源物体上的 blendShape 节点没有任何目标。")
            return

        use_wrap_method = cmds.checkBox("wrapMethod_cb", query=True, value=True)
        
        if use_wrap_method:
            cmds.select(target_geo, source_geo, r=True)
            mel.eval('CreateWrap;')

        temp_group_name = "WrapConvertTemp"
        if cmds.objExists(temp_group_name): cmds.delete(temp_group_name)
        temp_group = cmds.group(em=True, name=temp_group_name)

        rebuilt_meshes = []
        for alias in target_aliases:
            if cmds.connectionInfo(f"{bs_node}.{alias}", isDestination=True): continue
            original_value = cmds.getAttr(f"{bs_node}.{alias}")
            cmds.setAttr(f"{bs_node}.{alias}", 1)
            duplicated_mesh = cmds.duplicate(target_geo, name=alias)[0]
            cmds.parent(duplicated_mesh, temp_group)
            rebuilt_meshes.append(cmds.ls(duplicated_mesh, long=True)[0])
            cmds.setAttr(f"{bs_node}.{alias}", original_value)

        if use_wrap_method:
            wrap_node = find_first_deformer_of_type(target_geo, "wrap")
            if wrap_node: cmds.delete(wrap_node)
            base_objects = cmds.ls(f"{source_geo}Base*")
            if base_objects: cmds.delete(base_objects)

        if rebuilt_meshes:
            cmds.blendShape(rebuilt_meshes, target_geo, frontOfChain=True)[0]
            print(u"融合变形克隆成功。")
        else:
            cmds.warning(u"没有找到可以克隆的有效融合变形目标。")
        cmds.delete(temp_group)
        
        if mel.eval('exists deltaMush;') and cmds.checkBox("deltaMush_cb", query=True, value=True):
            intensity = cmds.intSliderGrp("deltaMush_slider", query=True, value=True)
            dm_node = cmds.deltaMush(target_geo, smoothingIterations=intensity, smoothingStep=1.0, pinBorderVertices=1, envelope=1)[0]
            current_bs_node = find_first_deformer_of_type(target_geo, "blendShape")
            if not current_bs_node:
                cmds.warning(u"应用 Delta Mush 时找不到融合变形节点。")
                return

            bs_aliases_on_target = cmds.listAttr(f"{current_bs_node}.w", multi=True) or []
            temp_group_dm_name = "WrapConvertTemp_DM"
            if cmds.objExists(temp_group_dm_name): cmds.delete(temp_group_dm_name)
            temp_group_dm = cmds.group(em=True, name=temp_group_dm_name)

            rebuilt_dm_meshes = []
            for alias in bs_aliases_on_target:
                cmds.setAttr(f"{current_bs_node}.{alias}", 1)
                cmds.refresh(force=True)
                duplicated_mesh = cmds.duplicate(target_geo, name=alias)[0]
                cmds.parent(duplicated_mesh, temp_group_dm)
                rebuilt_dm_meshes.append(cmds.ls(duplicated_mesh, long=True)[0])
                cmds.setAttr(f"{current_bs_node}.{alias}", 0)
            
            cmds.delete(dm_node, current_bs_node)
            if rebuilt_dm_meshes: cmds.blendShape(rebuilt_dm_meshes, target_geo, frontOfChain=True)
            cmds.delete(temp_group_dm)
            print(u"Delta Mush 已应用到融合变形目标。")

    def copy_blendshape(*args):
        selections = mel.eval('getShapeEditorTreeviewSelection(14)')
        if not selections or len(selections) != 2:
            cmds.warning(u"请在 Shape Editor 中精确选择两个目标:先选择源,再选择目标。")
            return False
        source_str, dest_str = selections[0], selections[1]
        bs_node_name = source_str.split('.')[0]
        source_index = int(source_str.split('.')[1])
        dest_index = int(dest_str.split('.')[1])
        temp_mesh = cmds.sculptTarget(bs_node_name, e=True, regenerate=True, target=source_index)[0]
        temp_mesh_shape = cmds.listRelatives(temp_mesh, s=True)[0]
        source_attr = f"{temp_mesh_shape}.worldMesh[0]"
        dest_attr = f"{bs_node_name}.inputTarget[0].inputTargetGroup[{dest_index}].inputTargetItem[6000].inputGeomTarget"
        cmds.connectAttr(source_attr, dest_attr, force=True)
        cmds.delete(temp_mesh)
        cmds.refresh()
        print(u"目标 '{}' 已复制到 '{}'。".format(source_str, dest_str))
        return True

    def mirror_blendshape(*args):
        selections = mel.eval('getShapeEditorTreeviewSelection(14)')
        if not selections or len(selections) != 2:
            cmds.warning(u"请在 Shape Editor 中精确选择两个目标:源目标和镜像目标。")
            return
        if not copy_blendshape(): return
        selected_axis = {1: 'x', 2: 'y', 3: 'z'}[cmds.radioButtonGrp("symmetry_axis_rbg", q=True, sl=True)]
        source_str, dest_str = selections[0], selections[1]
        bs_node_name = dest_str.split('.')[0]
        dest_index = int(dest_str.split('.')[1])
        mel_command = f'blendShape -e -ft 0 {dest_index} -ss 1 -sa "{selected_axis}" {bs_node_name};'
        mel.eval(mel_command)
        cmds.refresh()
        print(u"目标 '{}' 已沿 {} 轴镜像到 '{}'。".format(source_str, selected_axis.upper(), dest_str))

    parent_ui = "SuperRiggingEditor"
    if cmds.window(parent_ui, exists=True):
        window = cmds.window(win_name, title=win_title, menuBar=True, parent=parent_ui)
    else:
        window = cmds.window(win_name, title=win_title, menuBar=True)
    
    cmds.columnLayout(adjustableColumn=True, columnAttach=("both", 5), rowSpacing=4)
    cmds.separator(h=10, style="in")
    cmds.checkBox("wrapMethod_cb", label=u"使用包裹 (Wrap) 方式", value=True, align="left")
    
    if mel.eval('exists deltaMush;'):
        cmds.checkBox("deltaMush_cb", label=u"使用 Delta Mush", value=False, align="left")
        cmds.intSliderGrp("deltaMush_slider", 
                          field=True, 
                          label=u"强度", 
                          minValue=0, 
                          maxValue=100, 
                          value=10, 
                          columnWidth3=[45, 50, 100],
                          columnAttach=[(1, 'left', 5)])
    else:
        cmds.text(label=u"Delta Mush (功能不可用)", font="boldLabelFont", align="center")

    cmds.separator(h=12, style="in")
    cmds.button(label=u"克隆", command=clone_blendshape, h=30)
    cmds.button(label=u"复制", command=copy_blendshape, h=30)
    cmds.separator(h=12, style="double")
    cmds.radioButtonGrp("symmetry_axis_rbg", 
                        label=u"镜像轴向", 
                        numberOfRadioButtons=3, 
                        labelArray3=['X', 'Y', 'Z'], 
                        select=1, 
                        columnAttach=[(1, 'left', 5)],
                        columnWidth4=[80, 40, 40, 40])
    cmds.button(label=u"镜像", command=mirror_blendshape, h=30)
    cmds.separator(h=10, style="in")
    
    cmds.showWindow(window)
    cmds.window(win_name, edit=True, widthHeight=(260, 300))

create_clone_blendshape_tool()