Metapipe/extractDeltas.py
2025-01-15 23:17:52 +08:00

444 lines
14 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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'
# --------------------------------------------------------------------------------
# argument flags
# --------------------------------------------------------------------------------
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 <n/a> this message'
helpText += '\n -s -skin <string> the name of the skinned mesh'
helpText += '\n -c -corrective <string> the name of the sculpted shape'
helpText += '\n -vl -vertexList <string> optional list of vertices, comma separated string'
helpText += '\n Usage: Execute the command with the following arguments:'
helpText += '\n Execute: extractDeltas -s <mesh with skin cluster> -c <corrective mesh name>'
# --------------------------------------------------------------------------------
# main command
# --------------------------------------------------------------------------------
class extractDeltas(OpenMayaMPx.MPxCommand):
def __init__(self):
OpenMayaMPx.MPxCommand.__init__(self)
def doIt(self, args):
self.dagModifier = OpenMaya.MDagModifier()
skinName = ''
correctiveName = ''
resultName = ''
listString = ''
# --------------------------------------------------------------------------------
# parse the arguments
# --------------------------------------------------------------------------------
argData = OpenMaya.MArgDatabase(self.syntax(), args)
# help flag
if argData.isFlagSet(helpFlag):
self.setResult(helpText)
return
# skin flag
if argData.isFlagSet(skinFlag):
skinName = argData.flagArgumentString(skinFlag, 0)
# corrective flag
if argData.isFlagSet(correctiveFlag):
correctiveName = argData.flagArgumentString(correctiveFlag, 0)
# vertex list flag
if argData.isFlagSet(vertexListFlag):
listString = argData.flagArgumentString(vertexListFlag, 0)
# --------------------------------------------------------------------------------
# check the selection
# --------------------------------------------------------------------------------
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] + ' has no shape node.')
return
if cmds.nodeType(shapes[0]) != 'mesh':
OpenMaya.MGlobal.displayError(shapes[0] + ' is not a mesh object.')
return
elif i == 0 and len(shapes) > 1:
skin = cmds.listConnections(shapes[0], type = 'skinCluster')
if skin == None:
OpenMaya.MGlobal.displayError(shapes[0] + ' is not bound to a skin cluster.')
return
if cmds.getAttr(shapes[1] + '.intermediateObject'):
shapeList.append(shapes[1])
else:
OpenMaya.MGlobal.displayError(shapes[1] + ' is not an intermediate/original shape node.')
return
shapeList.append(shapes[0])
if len(shapeList) != 3:
OpenMaya.MGlobal.displayError('Select a skinned mesh with a valid original shape node and a target mesh object.')
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)
# --------------------------------------------------------------------------------
# define the mesh functions and get the points
# --------------------------------------------------------------------------------
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)
# --------------------------------------------------------------------------------
# get the delta points through a temporary blendShape node
# --------------------------------------------------------------------------------
pointList = []
for i in range(0, skinPoints.length()):
if skinPoints[i] != targetPoints[i]:
pointList.append(i)
if len(pointList) == 0:
OpenMaya.MGlobal.displayError('No shape extracted. Both meshes are identical.')
return
# create an intersection list between the delta points and the given vertex list
vList = []
if listString != '':
array = listString.split(',')
array = map(int, array)
intersectList = list(set(pointList) & set(array))
pointList = intersectList
# --------------------------------------------------------------------------------
# duplicate the original
# --------------------------------------------------------------------------------
resultFn = OpenMaya.MFnMesh()
resultObj = OpenMaya.MObject()
# copies the mesh using API functions but its not easily undoable
#resultObj = resultFn.copy(intermediateObj, OpenMaya.cvar.MObject_kNullObj)
# duplicating the mesh through maya commands is a bit more complex
# but the undo comes for free
resultMesh = cmds.duplicate(shapeList[0], rc = True)
shapes = cmds.listRelatives(resultMesh, s = True)
# delete the main shape node and deactivate the intermediate object
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)
# --------------------------------------------------------------------------------
# build a relative coordinate space by first preturbing
# the origional mesh and then building a coordinate space
# on the skinned mesh
# --------------------------------------------------------------------------------
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)
# set the original points back
intermediateFn.setPoints(intermediatePoints)
# --------------------------------------------------------------------------------
# perform the extraction from the skinned mesh
# --------------------------------------------------------------------------------
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)
# --------------------------------------------------------------------------------
# cleanup
# --------------------------------------------------------------------------------
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
# --------------------------------------------------------------------------------
# define the syntax, needed to make it work with mel and python
# --------------------------------------------------------------------------------
# creator
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
# initialization
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 procedures
# --------------------------------------------------------------------------------
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)