Update
This commit is contained in:
@@ -71,7 +71,8 @@ api.importSkinWeights(selected=False, stripJointNamespaces=False, addNewToHierar
|
||||
## 🔧 版本兼容性
|
||||
|
||||
### 支持的 Maya 版本
|
||||
- **所有 Maya 版本** - 从 Maya 2016 到最新版本
|
||||
- **所有 Maya 版本** - 从 Maya 2016 到 Maya 2025+
|
||||
- **Maya 2025** - 完全兼容,修复了 PyMEL 相关问题
|
||||
|
||||
### API 兼容性
|
||||
模块采用双重 API 支持策略:
|
||||
@@ -84,6 +85,8 @@ api.importSkinWeights(selected=False, stripJointNamespaces=False, addNewToHierar
|
||||
2. **优雅降级** - PyMEL 不可用时自动使用 cmds
|
||||
3. **相对导入** - 支持作为包导入或独立模块使用
|
||||
4. **异常处理** - 完善的错误处理和用户提示
|
||||
5. **空值安全** - 处理节点无父节点等边界情况
|
||||
6. **Maya 2025 优化** - 修复 PyMEL 在新版本中的兼容性问题
|
||||
|
||||
## 📝 文件格式
|
||||
|
||||
@@ -121,6 +124,11 @@ api.importSkinWeights(selected=False, stripJointNamespaces=False, addNewToHierar
|
||||
- 大量物体操作时会显示进度条
|
||||
- 权重文件使用 pickle 格式,不同 Python 版本间可能存在兼容性问题
|
||||
|
||||
### Maya 2025 特别说明
|
||||
- 已修复 PyMEL 在处理无父节点骨骼时的 `'NoneType' object has no attribute 'name'` 错误
|
||||
- 增强了所有 PyMEL 对象的空值检查
|
||||
- 建议使用 `saveJointInfo=True` 导出完整的骨骼信息
|
||||
|
||||
## 🐛 故障排除
|
||||
|
||||
### 导入失败
|
||||
|
||||
@@ -111,26 +111,42 @@ def getSkinClusterInfo(objectName, saveJointInfo=False):
|
||||
return False
|
||||
|
||||
def getSkinJointInformation(influences):
|
||||
"""
|
||||
获取骨骼信息(父节点、矩阵、旋转、关节方向)
|
||||
兼容 PyMEL 和 cmds,处理无父节点的情况
|
||||
"""
|
||||
jointInformation = {}
|
||||
|
||||
for inf in influences:
|
||||
jointInfo = {}
|
||||
if pm:
|
||||
infNode = pm.PyNode(inf)
|
||||
jointInfo["parent"] = str(infNode.getParent().name())
|
||||
jointInfo["matrix"] = infNode.getMatrix(worldSpace=True)
|
||||
jointInfo["rotation"] = infNode.getRotation()
|
||||
jointInfo["jointOrient"] = infNode.getAttr("jointOrient")
|
||||
jointInformation[str(infNode)] = copy.deepcopy(jointInfo)
|
||||
else:
|
||||
# cmds 版本
|
||||
infName = str(inf)
|
||||
parents = cmds.listRelatives(infName, parent=True)
|
||||
jointInfo["parent"] = parents[0] if parents else ""
|
||||
jointInfo["matrix"] = cmds.xform(infName, q=True, matrix=True, worldSpace=True)
|
||||
jointInfo["rotation"] = cmds.xform(infName, q=True, rotation=True)
|
||||
jointInfo["jointOrient"] = cmds.getAttr(infName + ".jointOrient")[0]
|
||||
jointInformation[infName] = copy.deepcopy(jointInfo)
|
||||
try:
|
||||
if pm:
|
||||
infNode = pm.PyNode(inf)
|
||||
# 安全获取父节点,避免 None.name() 错误
|
||||
parent = infNode.getParent()
|
||||
jointInfo["parent"] = str(parent.name()) if parent else ""
|
||||
jointInfo["matrix"] = infNode.getMatrix(worldSpace=True)
|
||||
jointInfo["rotation"] = infNode.getRotation()
|
||||
jointInfo["jointOrient"] = infNode.getAttr("jointOrient")
|
||||
jointInformation[str(infNode)] = copy.deepcopy(jointInfo)
|
||||
else:
|
||||
# cmds 版本
|
||||
infName = str(inf)
|
||||
parents = cmds.listRelatives(infName, parent=True)
|
||||
jointInfo["parent"] = parents[0] if parents else ""
|
||||
jointInfo["matrix"] = cmds.xform(infName, q=True, matrix=True, worldSpace=True)
|
||||
jointInfo["rotation"] = cmds.xform(infName, q=True, rotation=True)
|
||||
jointInfo["jointOrient"] = cmds.getAttr(infName + ".jointOrient")[0]
|
||||
jointInformation[infName] = copy.deepcopy(jointInfo)
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to get joint information for {inf}: {e}")
|
||||
# 使用默认值
|
||||
jointInfo["parent"] = ""
|
||||
jointInfo["matrix"] = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]
|
||||
jointInfo["rotation"] = [0, 0, 0]
|
||||
jointInfo["jointOrient"] = [0, 0, 0]
|
||||
jointInformation[str(inf)] = copy.deepcopy(jointInfo)
|
||||
|
||||
return jointInformation
|
||||
|
||||
def getMPlugObjects(MFnSkinCluster):
|
||||
@@ -350,10 +366,16 @@ def buildSkinWeightsDict(objectList, showLoadingBar=True, saveJointInfo=False):
|
||||
|
||||
sourceWeightDict = {}
|
||||
for object in objectList:
|
||||
if pm:
|
||||
objectAsString = pm.PyNode(object).name()
|
||||
else:
|
||||
# cmds 版本 - object 已经是字符串
|
||||
try:
|
||||
if pm:
|
||||
# 安全转换为字符串,处理可能的 None 或无效对象
|
||||
obj_node = pm.PyNode(object) if not isinstance(object, pm.PyNode) else object
|
||||
objectAsString = str(obj_node.name()) if obj_node else str(object)
|
||||
else:
|
||||
# cmds 版本 - object 已经是字符串
|
||||
objectAsString = str(object)
|
||||
except Exception as e:
|
||||
print(f"Warning: Failed to process object {object}: {e}")
|
||||
objectAsString = str(object)
|
||||
|
||||
if showLoadingBar:
|
||||
@@ -394,7 +416,11 @@ def transferSkinWeights(transferNodes=None, showLoadingBar=True):
|
||||
if len(transferNodes):
|
||||
|
||||
sourceObj = transferNodes[0]
|
||||
sourceName = sourceObj.name()
|
||||
# 安全获取名称
|
||||
try:
|
||||
sourceName = str(sourceObj.name()) if hasattr(sourceObj, 'name') else str(sourceObj)
|
||||
except:
|
||||
sourceName = str(sourceObj)
|
||||
targetNameList = transferNodes[1:]
|
||||
|
||||
loadBarMaxVal = len(targetNameList)
|
||||
@@ -410,7 +436,11 @@ def transferSkinWeights(transferNodes=None, showLoadingBar=True):
|
||||
# deep copy because: Mutable datatypes
|
||||
sourceWeightDictCopy = copy.deepcopy(sourceWeightDict)
|
||||
|
||||
targetName = str(tgtObject.name())
|
||||
# 安全获取名称
|
||||
try:
|
||||
targetName = str(tgtObject.name()) if hasattr(tgtObject, 'name') else str(tgtObject)
|
||||
except:
|
||||
targetName = str(tgtObject)
|
||||
|
||||
barycentrWeightDict = apiUtils.getBarycentricWeights(sourceName, targetName)
|
||||
|
||||
|
||||
@@ -399,8 +399,13 @@ def matchDictionaryToSceneMeshes(weightDictionary, selected=False):
|
||||
if vtxCountMatch(sceneNode, weightDictionary[dictNodeName]["vtxCount"]):
|
||||
# found match on both name and vtxcount, copy info to the local sceneWeightDict
|
||||
if pm:
|
||||
sceneWeightDict[sceneNode.name()] = weightDictionary[dictNodeName]
|
||||
validNodeList.append(sceneNode.name())
|
||||
# 安全获取节点名称
|
||||
try:
|
||||
node_name = str(sceneNode.name()) if hasattr(sceneNode, 'name') else str(sceneNode)
|
||||
except:
|
||||
node_name = str(sceneNode)
|
||||
sceneWeightDict[node_name] = weightDictionary[dictNodeName]
|
||||
validNodeList.append(node_name)
|
||||
else:
|
||||
sceneWeightDict[sceneNode] = weightDictionary[dictNodeName]
|
||||
validNodeList.append(sceneNode)
|
||||
|
||||
Reference in New Issue
Block a user