From 1983e3260fb64ead234593968cca4ad4882bde5f Mon Sep 17 00:00:00 2001 From: Jeffreytsai1004 Date: Wed, 15 Jan 2025 23:58:58 +0800 Subject: [PATCH] Update --- scripts/ExportFBX.py | 54 +++++ scripts/bsIndex.py | 38 ++++ scripts/extractDeltas.py | 441 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 533 insertions(+) create mode 100644 scripts/ExportFBX.py create mode 100644 scripts/bsIndex.py create mode 100644 scripts/extractDeltas.py diff --git a/scripts/ExportFBX.py b/scripts/ExportFBX.py new file mode 100644 index 0000000..54594ed --- /dev/null +++ b/scripts/ExportFBX.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import os +import maya.cmds as cmds + +def run(): + TOOL_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__))).replace("\\", "/") + OUTPUT_PATH = os.path.join(TOOL_PATH, "data", "output").replace("\\", "/") + BODY_FILE_NAME = "body.fbx" + BODY_FILE_PATH = os.path.join(OUTPUT_PATH, BODY_FILE_NAME).replace("\\", "/") + HEAD_FILE_NAME = "head.fbx" + HEAD_FILE_PATH = os.path.join(OUTPUT_PATH, HEAD_FILE_NAME).replace("\\", "/") + + # 导出Body + cmds.select(clear=True) + cmds.select("body_rig", add=True) + cmds.select("DHIbody:root", add=True) + cmds.file(BODY_FILE_PATH, force=True, options="groups=0;ptgroups=0;materials=0;smoothing=1;normals=1", type='FBX export', exportSelected=True) + + # 导出Head + cmds.select("DHIbody:spine_04", hi=True) # 选择"DHIbody:spine_04"及其子对象 + cmds.delete() # 删除选定的对象 + cmds.select("DHIbody:thigh_r", hi=True) # 选择"DHIbody:spine_04"及其子对象 + cmds.delete() # 删除选定的对象 + cmds.select("DHIbody:thigh_l", hi=True) # 选择"DHIbody:spine_04"及其子对象 + cmds.delete() # 删除选定的对象 + # 将"DHIhead:spine_04"作为"DHIbody:spine_03"的子对象 + cmds.parent("DHIhead:spine_04", "DHIbody:spine_03") + # 打印"DHIhead:spine_04"的新父对象 + print(cmds.listRelatives("DHIhead:spine_04", parent=True)) + cmds.select(clear=True) + cmds.select("head_grp", add=True) + cmds.select("DHIbody:root", add=True) + cmds.file(HEAD_FILE_PATH, force=True, options="groups=0;ptgroups=0;materials=0;smoothing=1;normals=1", type='FBX export', exportSelected=True) + + + # 撤销导出前对当前场景的删除等操作 + cmds.undo() + cmds.undo() + cmds.undo() + cmds.undo() + cmds.undo() + cmds.undo() + cmds.undo() + cmds.undo() + cmds.undo() + cmds.undo() + +if __name__ == "__main__": + run() + + diff --git a/scripts/bsIndex.py b/scripts/bsIndex.py new file mode 100644 index 0000000..6b57d7b --- /dev/null +++ b/scripts/bsIndex.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import pymel.core as pm +import maya.cmds as cmds +import sys + +def calc(): + selected_meshes = cmds.ls(sl=True) + targetblend = selected_meshes[0] + # 假设'rl4Embedded_Archtype'是节点的名称 + node_name = 'head_lod0_mesh_blendShapes' + + # 获取指定节点的PyNode + node = pm.PyNode(node_name) + + # 假设'bs_Output'是输出属性的名称 + output_attr = node.weight + + # 获取输出属性的连接插件 + connected_plugs = output_attr.connections(plugs=True) + blendIndex = [] + # 遍历连接的插件并打印它们的值 + for index, plug in enumerate(connected_plugs, start=1): + sub_node_value = plug.get() + if sub_node_value == 1: + print("Sub-node {} value: {}".format(index-1, sub_node_value)) + blendIndex = index-1 + print (blendIndex) + blend_shape_node = 'head_lod0_mesh_blendShapes' + rebuild_mesh = cmds.sculptTarget(blend_shape_node, e=True, regenerate=True, target=blendIndex) + cmds.select(rebuild_mesh) + blend_shape_node = "blendShape1" + cmds.blendShape(rebuild_mesh[0], automatic=True) + cmds.blendShape(blend_shape_node, edit=True, target=(rebuild_mesh[0], 2, targetblend+"_corrective", 1.0)) + cmds.setAttr(blend_shape_node+"."+targetblend+"_corrective", 1) + #cmds.delete(rebuild_mesh[0]) + cmds.delete(targetblend+"_corrective") \ No newline at end of file diff --git a/scripts/extractDeltas.py b/scripts/extractDeltas.py new file mode 100644 index 0000000..4949426 --- /dev/null +++ b/scripts/extractDeltas.py @@ -0,0 +1,441 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# ---------------------------------------------------------------------------------------------- +# +# extractDeltas.py +# v1.4 +# +# 从变形的蒙皮网格中提取建模的修正形状 +# +# 原始 c++ extract deltas 插件作者:James Jacobs +# +# Python 转换、改进和维护:Ingo Clemens +# www.braverabbit.com +# +# brave rabbit, Ingo Clemens 2014 +# +# 版本历史: +# +# 1.4 - 包含了 mel 脚本 +# 1.3 - 改进了形状比较,无需使用混合变形 +# 1.2 - 添加了顶点列表标志,仅在给定的组件列表上工作 +# 1.1 - 优化了性能,因为现在只对雕刻的点进行处理 +# (0.06 - c++ 版本, 1.88 - 1.0版本, 0.14 - 1.1版本) +# 1.0 - 初始 Python 转换 +# +# ---------------------------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------------------------- +# +# 本程序是自由软件;您可以根据自由软件基金会发布的 GNU 通用公共许可证 +# 第2版或(您可以选择)任何更新版本的条款重新发布和/或修改它。 +# +# 本程序的发布是希望它能有用,但不提供任何保证;甚至没有 +# 适销性或特定用途适用性的暗示保证。详情请参阅 +# GNU 通用公共许可证。 +# +# ---------------------------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------------------------- +# +# 使用和修改需自行承担风险!! +# +# ---------------------------------------------------------------------------------------------- + + +import maya.OpenMaya as OpenMaya +import maya.OpenMayaMPx as OpenMayaMPx +import maya.cmds as cmds +from maya.mel import eval as meval +import re +import sys + +kPluginCmdName = 'extractDeltas' + + +# -------------------------------------------------------------------------------- +# 参数标志 +# -------------------------------------------------------------------------------- + +helpFlag = '-h' +helpFlagLong = '-help' + +skinFlag = '-s' +skinFlagLong = '-skin' + +correctiveFlag = '-c' +correctiveFlagLong = '-corrective' + +vertexListFlag = '-vl' +vertexListFlagLong = '-vertexList' + +helpText = '' +helpText += '\n Description: Extract a modeled corrective shape from a deformed skinned mesh.' +helpText += '\n' +helpText += '\n Flags: extractDeltas -h -help this message' +helpText += '\n -s -skin the name of the skinned mesh' +helpText += '\n -c -corrective the name of the sculpted shape' +helpText += '\n -vl -vertexList optional list of vertices, comma separated string' +helpText += '\n Usage: Execute the command with the following arguments:' +helpText += '\n Execute: extractDeltas -s -c ' + + +# -------------------------------------------------------------------------------- +# 主命令 +# -------------------------------------------------------------------------------- + +class extractDeltas(OpenMayaMPx.MPxCommand): + + def __init__(self): + OpenMayaMPx.MPxCommand.__init__(self) + + def doIt(self, args): + + self.dagModifier = OpenMaya.MDagModifier() + + skinName = '' + correctiveName = '' + resultName = '' + listString = '' + + # -------------------------------------------------------------------------------- + # 解析参数 + # -------------------------------------------------------------------------------- + + argData = OpenMaya.MArgDatabase(self.syntax(), args) + + # 帮助标志 + if argData.isFlagSet(helpFlag): + self.setResult(helpText) + return + + # 皮肤标志 + if argData.isFlagSet(skinFlag): + skinName = argData.flagArgumentString(skinFlag, 0) + + # 修正标志 + if argData.isFlagSet(correctiveFlag): + correctiveName = argData.flagArgumentString(correctiveFlag, 0) + + # 顶点列表标志 + if argData.isFlagSet(vertexListFlag): + listString = argData.flagArgumentString(vertexListFlag, 0) + + # -------------------------------------------------------------------------------- + # 检查选择 + # -------------------------------------------------------------------------------- + + sel = [] + if skinName != '' and correctiveName != '': + sel.append(skinName) + sel.append(correctiveName) + else: + sel = cmds.ls(sl = True, tr = True) + + shapeList = [] + + for i in range(len(sel)): + shapes = cmds.listRelatives(sel[i], s = True) + if shapes == None: + OpenMaya.MGlobal.displayError(sel[i] + ' 没有形状节点。') + return + if cmds.nodeType(shapes[0]) != 'mesh': + OpenMaya.MGlobal.displayError(shapes[0] + ' 不是一个网格对象。') + return + elif i == 0 and len(shapes) > 1: + skin = cmds.listConnections(shapes[0], type = 'skinCluster') + if skin == None: + OpenMaya.MGlobal.displayError(shapes[0] + ' 没有绑定到皮肤簇。') + return + if cmds.getAttr(shapes[1] + '.intermediateObject'): + shapeList.append(shapes[1]) + else: + OpenMaya.MGlobal.displayError(shapes[1] + ' 不是一个中间/原始形状节点。') + return + shapeList.append(shapes[0]) + + if len(shapeList) != 3: + OpenMaya.MGlobal.displayError('选择一个带有有效原始形状节点和目标网格对象的蒙皮网格。') + return + + selList = OpenMaya.MSelectionList() + for sl in shapeList: + selList.add(sl) + + intermediateObj = OpenMaya.MObject() + skinObj = OpenMaya.MObject() + targetObj = OpenMaya.MObject() + + selList.getDependNode(0, intermediateObj) + selList.getDependNode(1, skinObj) + selList.getDependNode(2, targetObj) + + # -------------------------------------------------------------------------------- + # 定义网格函数并获取点 + # -------------------------------------------------------------------------------- + + skinFn = OpenMaya.MFnMesh() + skinFn.setObject(skinObj) + targetFn = OpenMaya.MFnMesh() + targetFn.setObject(targetObj) + intermediateFn = OpenMaya.MFnMesh() + intermediateFn.setObject(intermediateObj) + + skinPoints = OpenMaya.MPointArray() + skinFn.getPoints(skinPoints) + targetPoints = OpenMaya.MPointArray() + targetFn.getPoints(targetPoints) + intermediatePoints = OpenMaya.MPointArray() + intermediateFn.getPoints(intermediatePoints) + + extractPoints = OpenMaya.MPointArray(intermediatePoints) + + # -------------------------------------------------------------------------------- + # 通过临时 blendShape 节点获取 delta 点 + # -------------------------------------------------------------------------------- + + pointList = [] + for i in range(0, skinPoints.length()): + if skinPoints[i] != targetPoints[i]: + pointList.append(i) + + if len(pointList) == 0: + OpenMaya.MGlobal.displayError('没有形状提取。两个网格相同。') + return + + # 创建 delta 点与给定顶点列表之间的交集列表 + vList = [] + if listString != '': + array = listString.split(',') + array = map(int, array) + intersectList = list(set(pointList) & set(array)) + pointList = intersectList + + # -------------------------------------------------------------------------------- + # 复制原始网格 + # -------------------------------------------------------------------------------- + + resultFn = OpenMaya.MFnMesh() + resultObj = OpenMaya.MObject() + + # 使用 API 函数复制网格,但不容易撤消 + # resultObj = resultFn.copy(intermediateObj, OpenMaya.cvar.MObject_kNullObj) + # 通过 Maya 命令复制网格有点复杂 + # 但撤消是免费的 + + resultMesh = cmds.duplicate(shapeList[0], rc = True) + shapes = cmds.listRelatives(resultMesh, s = True) + # 删除主形状节点并禁用中间对象 + cmds.delete(shapes[0]) + cmds.setAttr(shapes[1] + '.intermediateObject', 0) + cmds.rename(shapes[1], shapes[0]) + attrList = ['tx', 'ty', 'tz', 'rx', 'ry', 'rz', 'sx', 'sy', 'sz'] + for a in attrList: + cmds.setAttr(resultMesh[0] + '.' + a, l = False) + + selList.clear() + selList.add(shapes[0]) + selList.getDependNode(0, resultObj) + resultFn.setObject(resultObj) + + resultPoints = OpenMaya.MPointArray() + resultFn.getPoints(resultPoints) + + # -------------------------------------------------------------------------------- + # 通过首先对原始网格进行预扰动,然后在蒙皮网格上构建坐标空间来构建相对坐标空间 + # -------------------------------------------------------------------------------- + + xArray = OpenMaya.MPointArray(intermediatePoints) + yArray = OpenMaya.MPointArray(intermediatePoints) + zArray = OpenMaya.MPointArray(intermediatePoints) + + xPointArray = OpenMaya.MPointArray() + yPointArray = OpenMaya.MPointArray() + zPointArray = OpenMaya.MPointArray() + + for i in pointList: + xArray.set(i, intermediatePoints[i].x + 1.0, intermediatePoints[i].y, intermediatePoints[i].z) + yArray.set(i, intermediatePoints[i].x, intermediatePoints[i].y + 1.0, intermediatePoints[i].z) + zArray.set(i, intermediatePoints[i].x, intermediatePoints[i].y, intermediatePoints[i].z + 1.0) + + intermediateFn.setPoints(xArray) + skinFn.getPoints(xPointArray) + + for i in pointList: + offX = xPointArray[i].x - skinPoints[i].x + offY = xPointArray[i].y - skinPoints[i].y + offZ = xPointArray[i].z - skinPoints[i].z + xPointArray.set(i, offX, offY, offZ) + + intermediateFn.setPoints(yArray) + skinFn.getPoints(yPointArray) + + for i in pointList: + offX = yPointArray[i].x - skinPoints[i].x + offY = yPointArray[i].y - skinPoints[i].y + offZ = yPointArray[i].z - skinPoints[i].z + yPointArray.set(i, offX, offY, offZ) + + intermediateFn.setPoints(zArray) + skinFn.getPoints(zPointArray) + + for i in pointList: + offX = zPointArray[i].x - skinPoints[i].x + offY = zPointArray[i].y - skinPoints[i].y + offZ = zPointArray[i].z - skinPoints[i].z + zPointArray.set(i, offX, offY, offZ) + + # 将原始点设置回去 + intermediateFn.setPoints(intermediatePoints) + + # -------------------------------------------------------------------------------- + # 从蒙皮网格中提取 + # -------------------------------------------------------------------------------- + + for i in pointList: + extractItems = [zPointArray[i].x, zPointArray[i].y, zPointArray[i].z, 0.0, + xPointArray[i].x, xPointArray[i].y, xPointArray[i].z, 0.0, + yPointArray[i].x, yPointArray[i].y, yPointArray[i].z, 0.0, + skinPoints[i].x, skinPoints[i].y, skinPoints[i].z, 1.0] + + resultItems = [0.0, 0.0, 1.0, 0.0, + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + resultPoints[i].x, resultPoints[i].y, resultPoints[i].z, 1.0] + + extractMatrix = OpenMaya.MMatrix() + OpenMaya.MScriptUtil.createMatrixFromList(extractItems, extractMatrix) + + resultMatrix = OpenMaya.MMatrix() + OpenMaya.MScriptUtil.createMatrixFromList(resultItems, resultMatrix) + + point = OpenMaya.MPoint() + point = targetPoints[i] * extractMatrix.inverse() + point *= resultMatrix + extractPoints.set(point, i) + + resultFn.setPoints(extractPoints) + + # -------------------------------------------------------------------------------- + # 清理 + # -------------------------------------------------------------------------------- + + cmds.sets(resultFn.fullPathName(), e = True, fe = 'initialShadingGroup') + parentNode = cmds.listRelatives(resultFn.fullPathName(), p = True) + resultName = cmds.rename(parentNode, sel[1] + '_corrective') + + self.setResult(resultName) + + return self.redoIt() + + def redoIt(self): + self.dagModifier.doIt() + + def undoIt(self): + self.dagModifier.undoIt() + + def isUndoable(self): + return True + + +# -------------------------------------------------------------------------------- +# 定义语法,需要与 mel 和 python 一起工作 +# -------------------------------------------------------------------------------- + +# 创建者 +def cmdCreator(): + return OpenMayaMPx.asMPxPtr(extractDeltas()) + +def syntaxCreator(): + syn = OpenMaya.MSyntax() + syn.addFlag(helpFlag, helpFlagLong) + syn.addFlag(skinFlag, skinFlagLong, OpenMaya.MSyntax.kString) + syn.addFlag(correctiveFlag, correctiveFlagLong, OpenMaya.MSyntax.kString) + syn.addFlag(vertexListFlag, vertexListFlagLong, OpenMaya.MSyntax.kString) + return syn + +# 初始化 +def initializePlugin(mobject): + mplugin = OpenMayaMPx.MFnPlugin(mobject, 'Original plugin by James Jacobs / Python adaption by Ingo Clemens', '1.4', 'Any') + try: + mplugin.registerCommand(kPluginCmdName, cmdCreator, syntaxCreator) + except: + sys.stderr.write('Failed to register command: %s\n' % kPluginCmdName) + raise + +def uninitializePlugin(mobject): + mplugin = OpenMayaMPx.MFnPlugin(mobject) + try: + mplugin.deregisterCommand(kPluginCmdName) + except: + sys.stderr.write( 'Failed to unregister command: %s\n' % kPluginCmdName ) + raise + +# -------------------------------------------------------------------------------- +# mel 过程 +# -------------------------------------------------------------------------------- + +mel = ''' + +global proc extractDeltasDuplicateMesh() +{ + string $sel[] = `ls -sl`; + string $shapes[] = `listRelatives -s $sel[0]`; + string $skin[] = `listConnections -type "skinCluster" $shapes[0]`; + if (`size($skin)`) + { + string $dup[] = `duplicate -rr -rc $sel`; + $shapes = `listRelatives -s $dup[0]`; + for ($s in $shapes) + { + if (`getAttr ($s + ".intermediateObject")`) + { + delete $s; + } + } + setAttr -l 0 ($dup[0] + ".tx"); + setAttr -l 0 ($dup[0] + ".ty"); + setAttr -l 0 ($dup[0] + ".tz"); + setAttr -l 0 ($dup[0] + ".rx"); + setAttr -l 0 ($dup[0] + ".ry"); + setAttr -l 0 ($dup[0] + ".rz"); + setAttr -l 0 ($dup[0] + ".sx"); + setAttr -l 0 ($dup[0] + ".sy"); + setAttr -l 0 ($dup[0] + ".sz"); + } +} + +global proc performExtractDeltas() +{ + string $sel[] = `ls -sl -tr`; + string $shapes[]; + for ($s in $sel) + { + $shapes = `listRelatives -s $s`; + for ($sh in $shapes) + { + if (`nodeType $sh` != "mesh") + { + error "The selected geometry is no polygon object!"; + } + } + } + if (size($sel) == 2) + { + $shapes = `listRelatives -s $sel[0]`; + string $skin[] = `listConnections -type "skinCluster" $shapes[0]`; + if (!`size($skin)`) + { + error "The first selected object is not bound to a skin cluster!"; + } + } + else + { + error "Please select two polygonal objects!"; + } + extractDeltas -s $sel[0] -c $sel[1]; +} + +''' +meval(mel)