MetaFusion/scripts/utils/define_utils.py

496 lines
14 KiB
Python
Raw Normal View History

2025-02-06 04:00:17 +08:00
import os
import json
import maya.cmds as cmds
from scripts.config import data
class DNADefinition:
"""DNA定义类"""
def __init__(self):
self.file_path = ""
self.content = {}
def load(self, file_path):
"""加载DNA文件"""
if not os.path.exists(file_path):
raise FileNotFoundError(f"DNA文件不存在: {file_path}")
try:
with open(file_path, "r") as f:
self.content = json.load(f)
self.file_path = file_path
return True
except Exception as e:
cmds.warning(f"加载DNA文件失败: {str(e)}")
return False
def save(self, file_path=None):
"""保存DNA文件"""
save_path = file_path or self.file_path
if not save_path:
cmds.warning("未指定保存路径")
return False
try:
with open(save_path, "w") as f:
json.dump(self.content, f, indent=4)
return True
except Exception as e:
cmds.warning(f"保存DNA文件失败: {str(e)}")
return False
def validate(self):
"""验证DNA内容"""
required_keys = ["joints", "blendshapes", "description"]
for key in required_keys:
if key not in self.content:
return False
return True
# 全局DNA定义实例
dna_definition = DNADefinition()
def browse_dna_file():
"""浏览DNA文件"""
file_path, _ = cmds.fileDialog2(
fileFilter="DNA Files (*.dna)",
dialogStyle=2,
fileMode=1
)
if not file_path:
return
# 加载DNA文件
if dna_definition.load(file_path[0]):
# 更新UI预览
update_dna_preview()
# 更新骨骼列表
update_joint_list()
# 更新BlendShape列表
update_blendshape_list()
def update_dna_preview():
"""更新DNA预览"""
# 获取UI实例
from scripts.MetaFusion import app
if not app:
return
define_tab = app.window.findChild(QtWidgets.QWidget, "DefineTab")
if not define_tab:
return
# 更新预览内容
preview_text = json.dumps(dna_definition.content, indent=4)
define_tab.dna_preview.setText(preview_text)
def update_joint_list():
"""更新骨骼列表"""
from scripts.MetaFusion import app
if not app:
return
define_tab = app.window.findChild(QtWidgets.QWidget, "DefineTab")
if not define_tab:
return
# 清空列表
define_tab.joint_list.clear()
# 添加骨骼项
joints = dna_definition.content.get("joints", [])
for joint in joints:
item = QtWidgets.QTreeWidgetItem([
joint["name"],
str(joint["position"]),
str(joint["rotation"]),
str(joint["scale"])
])
define_tab.joint_list.addTopLevelItem(item)
def update_blendshape_list():
"""更新BlendShape列表"""
from scripts.MetaFusion import app
if not app:
return
define_tab = app.window.findChild(QtWidgets.QWidget, "DefineTab")
if not define_tab:
return
# 清空列表
define_tab.bs_list.clear()
# 添加BlendShape项
blendshapes = dna_definition.content.get("blendshapes", [])
for bs in blendshapes:
item = QtWidgets.QTreeWidgetItem([
bs["name"],
bs["target"],
str(bs["weight"])
])
define_tab.bs_list.addTopLevelItem(item)
# 骨骼功能
def add_joint():
"""添加骨骼"""
# 获取选中的骨骼
sel = cmds.ls(sl=True, type="joint")
if not sel:
cmds.warning("请先选择要添加的骨骼")
return
# 获取骨骼信息
joint = sel[0]
pos = cmds.xform(joint, q=True, ws=True, t=True)
rot = cmds.xform(joint, q=True, ws=True, ro=True)
scl = cmds.xform(joint, q=True, ws=True, s=True)
# 添加到DNA定义
joint_data = {
"name": joint,
"position": pos,
"rotation": rot,
"scale": scl
}
if "joints" not in dna_definition.content:
dna_definition.content["joints"] = []
dna_definition.content["joints"].append(joint_data)
# 更新UI
update_joint_list()
def delete_joint():
"""删除骨骼"""
from scripts.MetaFusion import app
if not app:
return
define_tab = app.window.findChild(QtWidgets.QWidget, "DefineTab")
if not define_tab:
return
# 获取选中项
items = define_tab.joint_list.selectedItems()
if not items:
cmds.warning("请先选择要删除的骨骼")
return
# 从DNA定义中删除
for item in items:
joint_name = item.text(0)
joints = dna_definition.content.get("joints", [])
dna_definition.content["joints"] = [
j for j in joints if j["name"] != joint_name
]
# 更新UI
update_joint_list()
def modify_joint():
"""修改骨骼"""
from scripts.MetaFusion import app
if not app:
return
define_tab = app.window.findChild(QtWidgets.QWidget, "DefineTab")
if not define_tab:
return
# 获取选中项
items = define_tab.joint_list.selectedItems()
if not items:
cmds.warning("请先选择要修改的骨骼")
return
# 获取场景中选中的骨骼
sel = cmds.ls(sl=True, type="joint")
if not sel:
cmds.warning("请先选择要用于更新的骨骼")
return
# 更新骨骼信息
joint = sel[0]
pos = cmds.xform(joint, q=True, ws=True, t=True)
rot = cmds.xform(joint, q=True, ws=True, ro=True)
scl = cmds.xform(joint, q=True, ws=True, s=True)
# 更新DNA定义
joint_name = items[0].text(0)
joints = dna_definition.content.get("joints", [])
for j in joints:
if j["name"] == joint_name:
j["position"] = pos
j["rotation"] = rot
j["scale"] = scl
break
# 更新UI
update_joint_list()
# BlendShape功能
def add_blendshape():
"""添加BlendShape"""
# 获取选中的BlendShape
sel = cmds.ls(sl=True, type="blendShape")
if not sel:
cmds.warning("请先选择要添加的BlendShape")
return
# 获取BlendShape信息
bs = sel[0]
target = cmds.blendShape(bs, q=True, g=True)[0]
weight = cmds.getAttr(f"{bs}.weight[0]")
# 添加到DNA定义
bs_data = {
"name": bs,
"target": target,
"weight": weight
}
if "blendshapes" not in dna_definition.content:
dna_definition.content["blendshapes"] = []
dna_definition.content["blendshapes"].append(bs_data)
# 更新UI
update_blendshape_list()
def delete_blendshape():
"""删除BlendShape"""
from scripts.MetaFusion import app
if not app:
return
define_tab = app.window.findChild(QtWidgets.QWidget, "DefineTab")
if not define_tab:
return
# 获取选中项
items = define_tab.bs_list.selectedItems()
if not items:
cmds.warning("请先选择要删除的BlendShape")
return
# 从DNA定义中删除
for item in items:
bs_name = item.text(0)
blendshapes = dna_definition.content.get("blendshapes", [])
dna_definition.content["blendshapes"] = [
bs for bs in blendshapes if bs["name"] != bs_name
]
# 更新UI
update_blendshape_list()
def modify_blendshape():
"""修改BlendShape"""
from scripts.MetaFusion import app
if not app:
return
define_tab = app.window.findChild(QtWidgets.QWidget, "DefineTab")
if not define_tab:
return
# 获取选中项
items = define_tab.bs_list.selectedItems()
if not items:
cmds.warning("请先选择要修改的BlendShape")
return
# 获取场景中选中的BlendShape
sel = cmds.ls(sl=True, type="blendShape")
if not sel:
cmds.warning("请先选择要用于更新的BlendShape")
return
# 更新BlendShape信息
bs = sel[0]
target = cmds.blendShape(bs, q=True, g=True)[0]
weight = cmds.getAttr(f"{bs}.weight[0]")
# 更新DNA定义
bs_name = items[0].text(0)
blendshapes = dna_definition.content.get("blendshapes", [])
for b in blendshapes:
if b["name"] == bs_name:
b["target"] = target
b["weight"] = weight
break
# 更新UI
update_blendshape_list()
# DNA相关功能
def load_dna_preview(dna_file):
"""加载DNA预览
Args:
dna_file: DNA文件路径
Returns:
bool: 是否加载成功
"""
try:
# 加载DNA文件
if not dna_definition.load(dna_file):
return False
# 更新UI预览
update_dna_preview()
return True
except Exception as e:
cmds.warning(f"加载DNA预览失败: {str(e)}")
return False
def validate_dna(dna_file):
"""验证DNA文件
Args:
dna_file: DNA文件路径
Returns:
bool: 是否验证通过
"""
try:
# 加载DNA文件
if not dna_definition.load(dna_file):
return False
# 验证必要字段
if not dna_definition.validate():
cmds.warning("DNA文件缺少必要字段")
return False
# 验证骨骼数据
joints = dna_definition.content.get("joints", [])
for joint in joints:
required = ["name", "position", "rotation", "scale"]
if not all(key in joint for key in required):
cmds.warning(f"骨骼数据不完整: {joint.get('name', 'unknown')}")
return False
# 验证BlendShape数据
blendshapes = dna_definition.content.get("blendshapes", [])
for bs in blendshapes:
required = ["name", "target", "weight"]
if not all(key in bs for key in required):
cmds.warning(f"BlendShape数据不完整: {bs.get('name', 'unknown')}")
return False
return True
except Exception as e:
cmds.warning(f"验证DNA文件失败: {str(e)}")
return False
def export_dna_definition(dna_file):
"""导出DNA定义
Args:
dna_file: 导出文件路径
Returns:
bool: 是否导出成功
"""
try:
# 收集场景中的骨骼数据
joints = []
for joint in cmds.ls(type="joint"):
pos = cmds.xform(joint, q=True, ws=True, t=True)
rot = cmds.xform(joint, q=True, ws=True, ro=True)
scl = cmds.xform(joint, q=True, ws=True, s=True)
joints.append({
"name": joint,
"position": pos,
"rotation": rot,
"scale": scl
})
# 收集场景中的BlendShape数据
blendshapes = []
for bs in cmds.ls(type="blendShape"):
target = cmds.blendShape(bs, q=True, g=True)[0]
weight = cmds.getAttr(f"{bs}.weight[0]")
blendshapes.append({
"name": bs,
"target": target,
"weight": weight
})
# 更新DNA内容
dna_definition.content.update({
"joints": joints,
"blendshapes": blendshapes,
"description": {
"name": cmds.file(q=True, sn=True, shn=True),
"version": data.TOOL_VERSION,
"author": data.TOOL_AUTHOR,
"date": cmds.date(format="YYYY-MM-DD HH:mm:ss")
}
})
# 保存文件
return dna_definition.save(dna_file)
except Exception as e:
cmds.warning(f"导出DNA定义失败: {str(e)}")
return False
def import_dna_definition(dna_file):
"""导入DNA定义
Args:
dna_file: DNA文件路径
Returns:
bool: 是否导入成功
"""
try:
# 验证DNA文件
if not validate_dna(dna_file):
return False
# 导入骨骼
joints = dna_definition.content.get("joints", [])
for joint_data in joints:
# 检查骨骼是否存在
joint_name = joint_data["name"]
if not cmds.objExists(joint_name):
# 创建骨骼
joint = cmds.joint(name=joint_name)
else:
joint = joint_name
# 设置变换
cmds.xform(joint,
ws=True,
t=joint_data["position"],
ro=joint_data["rotation"],
s=joint_data["scale"]
)
# 导入BlendShape
blendshapes = dna_definition.content.get("blendshapes", [])
for bs_data in blendshapes:
# 检查目标是否存在
if not cmds.objExists(bs_data["target"]):
cmds.warning(f"BlendShape目标不存在: {bs_data['target']}")
continue
# 创建或获取BlendShape
bs_name = bs_data["name"]
if not cmds.objExists(bs_name):
bs = cmds.blendShape(
bs_data["target"],
name=bs_name,
frontOfChain=True
)[0]
else:
bs = bs_name
# 设置权重
cmds.setAttr(f"{bs}.weight[0]", bs_data["weight"])
return True
except Exception as e:
cmds.warning(f"导入DNA定义失败: {str(e)}")
return False