[小脚本] maya 命令行常用操作

其实这些代码大部分是从 chatgpt 中生成的。

骨骼命名

import maya.cmds as cmds

def rename_bones():
    selected_bones = cmds.ls(type="joint")  # 获取选中的骨骼

    for bone in selected_bones:
        if "_" in bone:
            new_name = bone.split("_")[0]  # 获取下划线前面的部分作为新的骨骼名称
            cmds.rename(bone, new_name)  # 重命名骨骼

rename_bones()

层级 rename

import maya.cmds as cmds

def rename_bones_with_suffix(bone, suffix):
    children = cmds.listRelatives(bone, children=True, type="joint") or []  # 获取骨骼的子骨骼

    for child in children:
        rename_bones_with_suffix(child, suffix)  # 递归调用,处理子骨骼
        
    new_name = bone + suffix  # 添加结尾字符串
    cmds.rename(bone, new_name)  # 重命名骨骼
    print("将骨骼 {} 重命名为 {}".format(bone, new_name))

selected_bones = cmds.ls(selection=True, type="joint")  # 获取选中的骨骼

for bone in selected_bones:
    rename_bones_with_suffix(bone, "_R")

属性赋值

左手旋转复制到右手上

import maya.cmds as cmds

def flip_z_rotation(source_bone, target_bone):
    # 获取源骨骼的旋转值
    source_rotation = cmds.getAttr(source_bone + ".rotate")[0]
    
    # 翻转旋转值的Z轴
    flipped_rotation = [source_rotation[0], source_rotation[1], -source_rotation[2]]
    
    # 将翻转后的旋转值赋值给目标骨骼
    cmds.setAttr(target_bone + ".rotate", flipped_rotation[0], flipped_rotation[1], flipped_rotation[2], type="double3")
    
    print("将骨骼 {} 的旋转值翻转并赋值到骨骼 {}".format(source_bone, target_bone))

all_bones = cmds.ls(type="joint")  # 获取所有骨骼

for bone in all_bones:
    if "_L" in bone:
        # 替换"_L"为"_R"的骨骼
        target_bone = bone.replace("_L", "_R")
        
        # 检查替换后的骨骼是否存在
        if cmds.objExists(target_bone):
            flip_z_rotation(bone, target_bone)

控制台调用批量处理

自己的批量处理框架

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

import os
import json
import math
import errno
import numpy as np    

import maya.cmds as cmds
import pymel.core as pm
import pymel.core.nodetypes as nt
import pymel.core.datatypes as dt

from maya import mel 

def SetFPS(fps):
    unit = 'ntscf'
    if fps == 15:
        unit = 'game'
    elif fps == 24:
        unit = 'film'
    elif fps == 25:
        unit = 'pal'
    elif fps == 30:
        unit = 'ntsc'
    elif fps == 48:
        unit = 'show'
    elif fps == 50:
        unit = 'palf'
    elif fps == 60:
        unit = 'ntscf'
    else:
        unit = str(fps)+'fps'

    cmds.currentUnit( time=unit )
    fps = mel.eval('currentTimeUnitToFPS')
    # 等价的
    #mel.eval("currentUnit -time ntsc;")
    return fps

class Cls_get_pure_bone:
    def __init__(self, bones, dstRoot=None, fps=30):
        
        self.use_joints = bones
        self.dstRoot = dstRoot
        self.fps = fps    

    def setup(self):
        cmds.file(new=True, force=True)
        if self.fps == 30:
            mel.eval("currentUnit -time ntsc;")
            self._import_fps = False
        else:
            SetFPS(self.fps)
            self._import_fps = False
            #self._import_fps = True
        

    def run(self, animFbx, outFn, targetOnly=True):
        
        # import source animation
        pm.importFile(
            animFbx, 
            type='FBX',
            importFrameRate=self._import_fps,
            importTimeRange='override'
        )

        startFrame = pm.playbackOptions(animationStartTime=True, q=True)
        endFrame   = pm.playbackOptions(animationEndTime=True, q=True)
    
        def string_array_contains(array, string):
            for item in array:
                if item == string:
                    return True  # 包含指定字符串
            return False  # 不包含指定字符串

        all_joints=cmds.ls(type="joint")
        # 遍历所有骨骼
        for joint in all_joints:
            # 检查当前骨骼是否在给定的骨骼列表中
            if not string_array_contains(self.use_joints, joint):
            # 检查当前骨骼是否在给, joint):
                # 删除不在列表中的骨骼
                if cmds.objExists(joint):
                    print("删除骨骼 " + joint)
                    cmds.delete(joint)
                else:
                    print("骨骼 " + joint + " 不存在")
       
        pm.playbackOptions(animationStartTime=startFrame, e=True)
        pm.playbackOptions(minTime=startFrame, e=True)
        pm.playbackOptions(animationEndTime=endFrame, e=True)
        pm.playbackOptions(maxTime=endFrame, e=True)
        
        # export retargeted anim
        if targetOnly:
            pm.select(self.dstRoot)
            pm.exportSelected(outFn, type='FBX export', force=True)
        else:
            pm.exportAll(outFn, type='FBX export', force=True) 
        return outFn  

 
if __name__ == '__main__':
    import argparse
    
    parser = argparse.ArgumentParser()
    parser.add_argument('--inputAnimFile', type=str, default=None, help='Input Anim Fbx')
    
    parser.add_argument('--inputFolder', type=str, default=None, help='Input Fbx Directory, batch mode')
    parser.add_argument('--outputFolder', type=str, default=None, help='Output Fbx Directory')
    parser.add_argument('--resume', action='store_true', help='skip exist or redo')
    parser.add_argument('--targetOnly', action='store_true', help='only keep target char')
 
    parser.add_argument('--dstRoot', type=str, default='')
    parser.add_argument('--fps', type=int, default=30)

    args = parser.parse_args()
    
    file_path = "scripts/data/coco_bone.txt"

    # 打开文件
    with open(file_path, "r") as file:
        # 按行读取文件内容
        bones = [ e.strip() for e in file.readlines() ]
    
    # list input files
    if args.inputFolder is None:
        inputFiles = [args.inputAnimFile]
        inputFolder = os.path.dirname(args.inputAnimFile)
    else:
        from pathlib import Path
        if not os.path.isdir(args.inputFolder):
            # regex pattern in arg
            pattern = os.path.basename(args.inputFolder)
            inputFolder = os.path.dirname(args.inputFolder)
        else:
            # directory
            inputFolder = args.inputFolder
            # pattern = r'**/*_anim.fbx'
            pattern = r'**/*.fbx'
            
        inputFiles = sorted([str(f) for f in Path(r'{0}'.format(inputFolder)).glob(pattern)])
        
    
    if args.outputFolder is None:
        outputFolder = inputFolder
    else:
        outputFolder = args.outputFolder
    
    cls = Cls_get_pure_bone(
        bones,
        dstRoot=args.dstRoot,
        fps=args.fps
    )
    
    cls.setup()

    for inputFile in inputFiles:
        relpath  = os.path.relpath(os.path.dirname(inputFile), inputFolder)        
        
        fout  = os.path.join(outputFolder, relpath) + f"/{os.path.basename(inputFile)}"
        
        ddir=fout.rsplit("/",1)[0]
        if not os.path.exists(ddir):
            os.makedirs(ddir)
        
        print('-- work on file: {0}'.format(inputFile))
        if args.resume and os.path.exists(fout):
            print('-- skip {0}'.format(inputFile))
            continue
        
        cls.run(inputFile, fout)
        

调用方式

@echo off
setlocal

rem 

"C:\Program Files\Autodesk\Maya2022\bin\mayapy.exe" /path/get_pure_bone.py ^
    --inputFolder  \path\fbx_org ^
    --dstRoot DeformationSystem ^
    --outputFolder \path\fbx_org_clear ^
    --targetOnly --fps 30
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值