MetaFusion/scripts/utils/adjust_utils.py
2025-02-06 04:46:41 +08:00

1194 lines
39 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#===================================== 1. Module Imports =====================================
import maya.cmds as cmds
import maya.mel as mel
import sys
import os
#===================================== 2. BlendShape Manager Class =====================================
class BlendShapeManager:
"""BlendShape管理器"""
def __init__(self):
self.main_bs = None # 主要BlendShape节点
self.related_bs = [] # 相关BlendShape节点
self.current_weights = {} # 当前权重缓存
def set_main_blendshape(self, bs_node):
"""设置主要BlendShape节点"""
if not cmds.objExists(bs_node):
raise ValueError(f"BlendShape节点不存在: {bs_node}")
self.main_bs = bs_node
def add_related_blendshape(self, bs_node):
"""添加相关BlendShape节点"""
if not cmds.objExists(bs_node):
raise ValueError(f"BlendShape节点不存在: {bs_node}")
if bs_node not in self.related_bs:
self.related_bs.append(bs_node)
def clear_related_blendshapes(self):
"""清空相关BlendShape节点"""
self.related_bs = []
def get_all_targets(self, bs_node):
"""获取BlendShape的所有目标"""
if not cmds.objExists(bs_node):
return []
# 获取目标数量
target_count = cmds.blendShape(bs_node, q=True, weightCount=True)
targets = []
# 获取每个目标的名称
for i in range(target_count):
alias = cmds.aliasAttr(f"{bs_node}.weight[{i}]", q=True)
if alias:
targets.append(alias)
return targets
def set_weight(self, bs_node, target, weight):
"""设置BlendShape权重"""
if not cmds.objExists(bs_node):
return False
try:
# 获取权重索引
weight_index = -1
target_count = cmds.blendShape(bs_node, q=True, weightCount=True)
for i in range(target_count):
alias = cmds.aliasAttr(f"{bs_node}.weight[{i}]", q=True)
if alias == target:
weight_index = i
break
if weight_index >= 0:
cmds.setAttr(f"{bs_node}.weight[{weight_index}]", weight)
return True
except Exception as e:
cmds.warning(f"设置权重失败: {str(e)}")
return False
def get_weight(self, bs_node, target):
"""获取BlendShape权重"""
if not cmds.objExists(bs_node):
return 0.0
try:
# 获取权重索引
weight_index = -1
target_count = cmds.blendShape(bs_node, q=True, weightCount=True)
for i in range(target_count):
alias = cmds.aliasAttr(f"{bs_node}.weight[{i}]", q=True)
if alias == target:
weight_index = i
break
if weight_index >= 0:
return cmds.getAttr(f"{bs_node}.weight[{weight_index}]")
except Exception as e:
cmds.warning(f"获取权重失败: {str(e)}")
return 0.0
def store_weights(self):
"""存储当前权重"""
self.current_weights = {}
# 存储主要BlendShape权重
if self.main_bs:
self.current_weights[self.main_bs] = {}
for target in self.get_all_targets(self.main_bs):
self.current_weights[self.main_bs][target] = self.get_weight(self.main_bs, target)
# 存储相关BlendShape权重
for bs in self.related_bs:
self.current_weights[bs] = {}
for target in self.get_all_targets(bs):
self.current_weights[bs][target] = self.get_weight(bs, target)
def restore_weights(self):
"""还原存储的权重"""
for bs, weights in self.current_weights.items():
for target, weight in weights.items():
self.set_weight(bs, target, weight)
# 全局BlendShape管理器实例
bs_manager = BlendShapeManager()
def reset_blendshapes():
"""重置所有BlendShape"""
try:
# 重置主要BlendShape
if bs_manager.main_bs:
for target in bs_manager.get_all_targets(bs_manager.main_bs):
bs_manager.set_weight(bs_manager.main_bs, target, 0)
# 重置相关BlendShape
for bs in bs_manager.related_bs:
for target in bs_manager.get_all_targets(bs):
bs_manager.set_weight(bs, target, 0)
print("BlendShape重置完成")
except Exception as e:
cmds.warning(f"重置BlendShape失败: {str(e)}")
def filter_blendshapes():
"""过滤BlendShape"""
# TODO: 实现BlendShape过滤功能
print("BlendShape过滤功能待实现")
def on_main_bs_selected(targets):
"""主要BlendShape选择变化"""
try:
# 存储当前权重
bs_manager.store_weights()
# 设置新的权重
if bs_manager.main_bs:
for target in targets:
bs_manager.set_weight(bs_manager.main_bs, target, 1.0)
except Exception as e:
cmds.warning(f"设置BlendShape权重失败: {str(e)}")
def on_related_bs_selected(targets):
"""相关BlendShape选择变化"""
try:
# 存储当前权重
bs_manager.store_weights()
# 设置新的权重
for bs in bs_manager.related_bs:
for target in targets:
bs_manager.set_weight(bs, target, 1.0)
except Exception as e:
cmds.warning(f"设置BlendShape权重失败: {str(e)}")
def set_bs_value(value):
"""设置BlendShape权重值"""
try:
# 设置主要BlendShape权重
if bs_manager.main_bs:
for target in bs_manager.get_all_targets(bs_manager.main_bs):
bs_manager.set_weight(bs_manager.main_bs, target, value)
# 设置相关BlendShape权重
for bs in bs_manager.related_bs:
for target in bs_manager.get_all_targets(bs):
bs_manager.set_weight(bs, target, value)
except Exception as e:
cmds.warning(f"设置BlendShape权重值失败: {str(e)}")
def set_expr_value(value):
"""设置表情权重值"""
try:
# 获取当前选中的表情控制器
sel = cmds.ls(sl=True)
if not sel:
return
# 设置权重
for ctrl in sel:
if cmds.objExists(f"{ctrl}.weight"):
cmds.setAttr(f"{ctrl}.weight", value)
except Exception as e:
cmds.warning(f"设置表情权重值失败: {str(e)}")
# 表情控制功能
def reset_expression():
"""还原默认表情"""
try:
# 还原存储的权重
bs_manager.restore_weights()
print("表情已还原")
except Exception as e:
cmds.warning(f"还原表情失败: {str(e)}")
def select_expression():
"""选择表情"""
# TODO: 实现表情选择功能
print("表情选择功能待实现")
def write_expression():
"""写入当前表情"""
# TODO: 实现表情写入功能
print("表情写入功能待实现")
def find_controller():
"""查找控制器"""
# TODO: 实现控制器查找功能
print("控制器查找功能待实现")
def select_joints():
"""选择关联关节"""
# TODO: 实现关联关节选择功能
print("关联关节选择功能待实现")
def write_mirror_expression():
"""写入镜像表情"""
# TODO: 实现镜像表情写入功能
print("镜像表情写入功能待实现")
def reset_expression():
"""恢复表情"""
print("恢复表情功能待实现")
def blend_filter():
"""混合筛选"""
print("混合筛选功能待实现")
def flip():
"""翻转"""
print("翻转功能待实现")
def mirror_target():
"""镜像目标"""
print("镜像目标功能待实现")
def find_flip_target():
"""查找翻转目标"""
print("查找翻转目标功能待实现")
def add_blend_target():
"""添加混合目标"""
print("添加混合目标功能待实现")
def delete_blend_target():
"""删除混合目标"""
print("删除混合目标功能待实现")
def batch_blend_target():
"""批量混合目标"""
print("批量混合目标功能待实现")
def rebuild_selected_target():
"""重建选择目标"""
print("重建选择目标功能待实现")
def blend_selected_target():
"""混合选择目标"""
print("混合选择目标功能待实现")
# 功能开关
def toggle_psd(checked):
"""PSD开关"""
print(f"PSD功能{'开启' if checked else '关闭'}")
def toggle_bse(checked):
"""BSE开关"""
print(f"BSE功能{'开启' if checked else '关闭'}")
def toggle_key(checked):
"""KEY开关"""
print(f"KEY功能{'开启' if checked else '关闭'}")
def toggle_mir(checked):
"""MIR开关"""
print(f"MIR功能{'开启' if checked else '关闭'}")
def toggle_ark(checked):
"""ARK开关"""
print(f"ARK功能{'开启' if checked else '关闭'}")
def toggle_ctr(checked):
"""CTR开关"""
print(f"CTR功能{'开启' if checked else '关闭'}")
# 底部功能按钮
def reset_default_expression():
"""还原默认表情"""
print("还原默认表情功能待实现")
def find_control_panel():
"""查找控制面板"""
print("查找控制面板功能待实现")
def select_related_joints():
"""选择关联关节"""
print("选择关联关节功能待实现")
def write_mirror_expression():
"""写入镜像表情"""
print("写入镜像表情功能待实现")
def mirror_blendshape(source_bs, target_bs, left_prefix="L_", right_prefix="R_"):
"""镜像BlendShape
Args:
source_bs: 源BlendShape节点
target_bs: 目标BlendShape节点
left_prefix: 左侧前缀
right_prefix: 右侧前缀
"""
try:
# 获取源BlendShape的所有目标
source_targets = bs_manager.get_all_targets(source_bs)
for source_target in source_targets:
# 获取镜像目标名称
if source_target.startswith(left_prefix):
mirror_target = source_target.replace(left_prefix, right_prefix)
elif source_target.startswith(right_prefix):
mirror_target = source_target.replace(right_prefix, left_prefix)
else:
continue
# 获取源权重
weight = bs_manager.get_weight(source_bs, source_target)
# 设置镜像权重
bs_manager.set_weight(target_bs, mirror_target, weight)
print(f"BlendShape镜像完成: {source_bs} -> {target_bs}")
except Exception as e:
cmds.warning(f"BlendShape镜像失败: {str(e)}")
def find_mirror_target(target_name, left_prefix="L_", right_prefix="R_"):
"""查找镜像目标
Args:
target_name: 目标名称
left_prefix: 左侧前缀
right_prefix: 右侧前缀
Returns:
str: 镜像目标名称
"""
if target_name.startswith(left_prefix):
return target_name.replace(left_prefix, right_prefix)
elif target_name.startswith(right_prefix):
return target_name.replace(right_prefix, left_prefix)
return ""
def create_blend_target(base_mesh, target_mesh, target_name):
"""创建混合目标
Args:
base_mesh: 基础模型
target_mesh: 目标模型
target_name: 目标名称
Returns:
str: 创建的BlendShape节点名称
"""
try:
# 检查模型是否存在
if not all(cmds.objExists(obj) for obj in [base_mesh, target_mesh]):
raise ValueError("模型不存在")
# 创建BlendShape节点
bs_node = cmds.blendShape(
target_mesh,
base_mesh,
name=f"{base_mesh}_blendShape",
frontOfChain=True
)[0]
# 重命名目标
target_index = 0
cmds.aliasAttr(target_name, f"{bs_node}.weight[{target_index}]")
return bs_node
except Exception as e:
cmds.warning(f"创建混合目标失败: {str(e)}")
return None
def batch_create_blend_targets(base_mesh, target_meshes, name_prefix=""):
"""批量创建混合目标
Args:
base_mesh: 基础模型
target_meshes: 目标模型列表
name_prefix: 名称前缀
"""
try:
# 创建BlendShape节点
bs_node = cmds.blendShape(
base_mesh,
name=f"{base_mesh}_blendShape",
frontOfChain=True
)[0]
# 添加目标
for i, target in enumerate(target_meshes):
target_name = f"{name_prefix}target_{i+1}"
cmds.blendShape(bs_node, edit=True, target=(base_mesh, i, target, 1.0))
cmds.aliasAttr(target_name, f"{bs_node}.weight[{i}]")
print(f"批量创建混合目标完成: {bs_node}")
return bs_node
except Exception as e:
cmds.warning(f"批量创建混合目标失败: {str(e)}")
return None
def rebuild_blend_target(bs_node, target_name):
"""重建混合目标
Args:
bs_node: BlendShape节点
target_name: 目标名称
"""
try:
# 获取基础模型
base_mesh = cmds.blendShape(bs_node, q=True, geometry=True)[0]
# 获取目标索引
target_index = -1
target_count = cmds.blendShape(bs_node, q=True, weightCount=True)
for i in range(target_count):
alias = cmds.aliasAttr(f"{bs_node}.weight[{i}]", q=True)
if alias == target_name:
target_index = i
break
if target_index < 0:
raise ValueError(f"找不到目标: {target_name}")
# 提取目标形状
target_mesh = cmds.duplicate(base_mesh, name=f"{target_name}_rebuild")[0]
cmds.setAttr(f"{bs_node}.weight[{target_index}]", 1)
cmds.delete(target_mesh, constructionHistory=True)
# 重建目标
cmds.blendShape(bs_node, edit=True, remove=True, target=(base_mesh, target_index, target_name, 1.0))
cmds.blendShape(bs_node, edit=True, target=(base_mesh, target_index, target_mesh, 1.0))
# 清理
cmds.delete(target_mesh)
print(f"重建混合目标完成: {target_name}")
except Exception as e:
cmds.warning(f"重建混合目标失败: {str(e)}")
def blend_selected_targets(bs_node, targets, weight=1.0):
"""混合选中的目标
Args:
bs_node: BlendShape节点
targets: 目标名称列表
weight: 混合权重
"""
try:
# 重置所有权重
all_targets = bs_manager.get_all_targets(bs_node)
for target in all_targets:
bs_manager.set_weight(bs_node, target, 0)
# 设置选中目标的权重
for target in targets:
bs_manager.set_weight(bs_node, target, weight)
print(f"混合目标完成: {targets}")
except Exception as e:
cmds.warning(f"混合目标失败: {str(e)}")
def create_corrective_blend(base_mesh, pose_mesh, corrective_mesh, pose_attrs):
"""创建修正混合变形
Args:
base_mesh: 基础模型
pose_mesh: 姿势模型
corrective_mesh: 修正模型
pose_attrs: 姿势属性列表 [(attr, value), ...]
"""
try:
# 创建BlendShape节点
bs_node = cmds.blendShape(
[pose_mesh, corrective_mesh],
base_mesh,
name=f"{base_mesh}_corrective",
frontOfChain=True
)[0]
# 设置驱动关系
for attr, value in pose_attrs:
# 创建条件节点
cond = cmds.createNode("condition", name=f"{bs_node}_cond")
cmds.setAttr(f"{cond}.operation", 2) # Greater Than
cmds.setAttr(f"{cond}.secondTerm", value)
# 连接属性
cmds.connectAttr(attr, f"{cond}.firstTerm")
cmds.connectAttr(f"{cond}.outColorR", f"{bs_node}.weight[1]")
print(f"创建修正混合变形完成: {bs_node}")
return bs_node
except Exception as e:
cmds.warning(f"创建修正混合变形失败: {str(e)}")
return None
def create_expression_set(name, targets, base_mesh=None):
"""创建表情集
Args:
name: 表情集名称
targets: 目标列表 [(target_name, weight), ...]
base_mesh: 基础模型(可选)
Returns:
str: 创建的表情集节点名称
"""
try:
# 创建表情集节点
expr_set = cmds.createNode("objectSet", name=f"{name}_exprSet")
# 添加自定义属性
cmds.addAttr(expr_set, ln="weight", at="double", min=0, max=1, dv=0)
cmds.setAttr(f"{expr_set}.weight", e=True, keyable=True)
# 添加目标信息
cmds.addAttr(expr_set, ln="targets", dt="string")
target_data = ";".join([f"{t}:{w}" for t, w in targets])
cmds.setAttr(f"{expr_set}.targets", target_data, type="string")
# 添加基础模型
if base_mesh:
cmds.addAttr(expr_set, ln="baseMesh", dt="string")
cmds.setAttr(f"{expr_set}.baseMesh", base_mesh, type="string")
return expr_set
except Exception as e:
cmds.warning(f"创建表情集失败: {str(e)}")
return None
def apply_expression_set(expr_set, weight=None):
"""应用表情集
Args:
expr_set: 表情集节点
weight: 权重值(可选)
"""
try:
# 获取目标信息
target_data = cmds.getAttr(f"{expr_set}.targets")
targets = []
for item in target_data.split(";"):
name, value = item.split(":")
targets.append((name, float(value)))
# 获取权重
if weight is None:
weight = cmds.getAttr(f"{expr_set}.weight")
# 应用权重
for target_name, target_weight in targets:
bs_node = target_name.split(".")[0]
bs_manager.set_weight(bs_node, target_name, target_weight * weight)
except Exception as e:
cmds.warning(f"应用表情集失败: {str(e)}")
def create_mirror_expression(expr_set, left_prefix="L_", right_prefix="R_"):
"""创建镜像表情
Args:
expr_set: 源表情集节点
left_prefix: 左侧前缀
right_prefix: 右侧前缀
Returns:
str: 创建的镜像表情集节点名称
"""
try:
# 获取源表情信息
target_data = cmds.getAttr(f"{expr_set}.targets")
source_targets = []
for item in target_data.split(";"):
name, value = item.split(":")
source_targets.append((name, float(value)))
# 创建镜像目标
mirror_targets = []
for target_name, weight in source_targets:
mirror_name = find_mirror_target(target_name, left_prefix, right_prefix)
if mirror_name:
mirror_targets.append((mirror_name, weight))
# 创建镜像表情集
name = cmds.getAttr(f"{expr_set}.name")
mirror_name = f"{name}_mirror"
if cmds.objExists(f"{expr_set}.baseMesh"):
base_mesh = cmds.getAttr(f"{expr_set}.baseMesh")
else:
base_mesh = None
return create_expression_set(mirror_name, mirror_targets, base_mesh)
except Exception as e:
cmds.warning(f"创建镜像表情失败: {str(e)}")
return None
def optimize_weights(bs_node, threshold=0.001):
"""优化权重
Args:
bs_node: BlendShape节点
threshold: 权重阈值
"""
try:
# 获取所有目标
targets = bs_manager.get_all_targets(bs_node)
# 检查每个目标的权重
for target in targets:
weight = bs_manager.get_weight(bs_node, target)
# 移除小权重
if abs(weight) < threshold:
bs_manager.set_weight(bs_node, target, 0)
# 规范化权重
elif abs(weight - 1.0) < threshold:
bs_manager.set_weight(bs_node, target, 1.0)
print(f"权重优化完成: {bs_node}")
except Exception as e:
cmds.warning(f"权重优化失败: {str(e)}")
def create_weight_driver(bs_node, target, driver_attr):
"""创建权重驱动器
Args:
bs_node: BlendShape节点
target: 目标名称
driver_attr: 驱动属性
"""
try:
# 创建条件节点
cond = cmds.createNode("condition", name=f"{target}_cond")
cmds.setAttr(f"{cond}.operation", 2) # Greater Than
cmds.setAttr(f"{cond}.secondTerm", 0)
# 连接驱动属性
cmds.connectAttr(driver_attr, f"{cond}.firstTerm")
# 获取目标索引
target_index = -1
target_count = cmds.blendShape(bs_node, q=True, weightCount=True)
for i in range(target_count):
alias = cmds.aliasAttr(f"{bs_node}.weight[{i}]", q=True)
if alias == target:
target_index = i
break
if target_index >= 0:
# 连接权重
cmds.connectAttr(f"{cond}.outColorR", f"{bs_node}.weight[{target_index}]")
print(f"创建权重驱动器完成: {target}")
return cond
except Exception as e:
cmds.warning(f"创建权重驱动器失败: {str(e)}")
return None
def create_expression_group(name, expressions):
"""创建表情组
Args:
name: 组名称
expressions: 表情集列表 [(expr_name, weight), ...]
Returns:
str: 创建的表情组节点名称
"""
try:
# 创建组节点
group = cmds.createNode("objectSet", name=f"{name}_exprGroup")
# 添加表情集信息
cmds.addAttr(group, ln="expressions", dt="string")
expr_data = ";".join([f"{e}:{w}" for e, w in expressions])
cmds.setAttr(f"{group}.expressions", expr_data, type="string")
return group
except Exception as e:
cmds.warning(f"创建表情组失败: {str(e)}")
return None
def create_expression_controller(name, attributes):
"""创建表情控制器
Args:
name: 控制器名称
attributes: 属性列表 [(attr_name, min_val, max_val, default), ...]
Returns:
str: 创建的控制器名称
"""
try:
# 创建控制器
ctrl = cmds.circle(name=f"{name}_ctrl", normal=(0, 1, 0))[0]
# 添加属性
for attr_name, min_val, max_val, default in attributes:
cmds.addAttr(ctrl, ln=attr_name, at="double",
min=min_val, max=max_val, dv=default)
cmds.setAttr(f"{ctrl}.{attr_name}", e=True, keyable=True)
return ctrl
except Exception as e:
cmds.warning(f"创建表情控制器失败: {str(e)}")
return None
def create_expression_driver(source_attr, target_attrs, remap=False):
"""创建表情驱动器
Args:
source_attr: 源属性
target_attrs: 目标属性列表 [(attr, value), ...]
remap: 是否重映射值
"""
try:
for target_attr, target_value in target_attrs:
if remap:
# 创建重映射节点
remap_node = cmds.createNode("remapValue")
cmds.connectAttr(source_attr, f"{remap_node}.inputValue")
cmds.connectAttr(f"{remap_node}.outValue", target_attr)
# 设置映射值
cmds.setAttr(f"{remap_node}.value[0].value_Position", 0)
cmds.setAttr(f"{remap_node}.value[0].value_FloatValue", 0)
cmds.setAttr(f"{remap_node}.value[1].value_Position", 1)
cmds.setAttr(f"{remap_node}.value[1].value_FloatValue", target_value)
else:
# 直接连接
cmds.connectAttr(source_attr, target_attr)
print(f"创建表情驱动器完成: {source_attr}")
except Exception as e:
cmds.warning(f"创建表情驱动器失败: {str(e)}")
def create_expression_pose(name, targets, mirror=False):
"""创建表情姿势
Args:
name: 姿势名称
targets: 目标列表 [(node, attr, value), ...]
mirror: 是否创建镜像姿势
Returns:
str: 创建的姿势节点名称
"""
try:
# 创建姿势节点
pose = cmds.createNode("objectSet", name=f"{name}_pose")
# 添加目标信息
cmds.addAttr(pose, ln="targets", dt="string")
target_data = ";".join([f"{n}.{a}:{v}" for n, a, v in targets])
cmds.setAttr(f"{pose}.targets", target_data, type="string")
if mirror:
# 创建镜像姿势
mirror_targets = []
for node, attr, value in targets:
if "_L_" in node:
mirror_node = node.replace("_L_", "_R_")
elif "_R_" in node:
mirror_node = node.replace("_R_", "_L_")
else:
continue
mirror_targets.append((mirror_node, attr, value))
if mirror_targets:
create_expression_pose(f"{name}_mirror", mirror_targets)
return pose
except Exception as e:
cmds.warning(f"创建表情姿势失败: {str(e)}")
return None
def apply_expression_pose(pose, weight=1.0):
"""应用表情姿势
Args:
pose: 姿势节点
weight: 应用权重
"""
try:
# 获取目标信息
target_data = cmds.getAttr(f"{pose}.targets")
for item in target_data.split(";"):
attr_path, value = item.split(":")
node, attr = attr_path.split(".")
if cmds.objExists(attr_path):
current = cmds.getAttr(attr_path)
target = float(value)
blend = current + (target - current) * weight
cmds.setAttr(attr_path, blend)
except Exception as e:
cmds.warning(f"应用表情姿势失败: {str(e)}")
def create_expression_sequence(name, poses, times):
"""创建表情序列
Args:
name: 序列名称
poses: 姿势列表
times: 时间列表
Returns:
str: 创建的序列节点名称
"""
try:
# 创建序列节点
sequence = cmds.createNode("objectSet", name=f"{name}_sequence")
# 添加序列信息
cmds.addAttr(sequence, ln="poses", dt="string")
pose_data = ";".join([f"{p}:{t}" for p, t in zip(poses, times)])
cmds.setAttr(f"{sequence}.poses", pose_data, type="string")
return sequence
except Exception as e:
cmds.warning(f"创建表情序列失败: {str(e)}")
return None
def play_expression_sequence(sequence):
"""播放表情序列
Args:
sequence: 序列节点
"""
try:
# 获取序列信息
pose_data = cmds.getAttr(f"{sequence}.poses")
for item in pose_data.split(";"):
pose, time = item.split(":")
# 设置时间
cmds.currentTime(float(time))
# 应用姿势
apply_expression_pose(pose)
except Exception as e:
cmds.warning(f"播放表情序列失败: {str(e)}")
def create_weight_editor(bs_node):
"""创建权重编辑器
Args:
bs_node: BlendShape节点
Returns:
str: 创建的编辑器节点名称
"""
try:
# 创建编辑器节点
editor = cmds.createNode("objectSet", name=f"{bs_node}_weightEditor")
# 添加编辑器信息
cmds.addAttr(editor, ln="blendShape", dt="string")
cmds.setAttr(f"{editor}.blendShape", bs_node, type="string")
# 获取所有目标
targets = bs_manager.get_all_targets(bs_node)
# 添加目标权重属性
for target in targets:
cmds.addAttr(editor, ln=target, at="double", min=0, max=1, dv=0)
cmds.setAttr(f"{editor}.{target}", e=True, keyable=True)
# 连接权重
weight = bs_manager.get_weight(bs_node, target)
cmds.setAttr(f"{editor}.{target}", weight)
return editor
except Exception as e:
cmds.warning(f"创建权重编辑器失败: {str(e)}")
return None
def create_expression_preview(expr_set):
"""创建表情预览
Args:
expr_set: 表情集节点
Returns:
str: 创建的预览节点名称
"""
try:
# 创建预览节点
preview = cmds.createNode("objectSet", name=f"{expr_set}_preview")
# 添加预览信息
cmds.addAttr(preview, ln="expression", dt="string")
cmds.setAttr(f"{preview}.expression", expr_set, type="string")
# 添加预览控制
cmds.addAttr(preview, ln="weight", at="double", min=0, max=1, dv=0)
cmds.setAttr(f"{preview}.weight", e=True, keyable=True)
# 添加时间控制
cmds.addAttr(preview, ln="time", at="time")
cmds.setAttr(f"{preview}.time", e=True, keyable=True)
# 添加播放控制
cmds.addAttr(preview, ln="play", at="bool")
cmds.setAttr(f"{preview}.play", e=True, keyable=True)
return preview
except Exception as e:
cmds.warning(f"创建表情预览失败: {str(e)}")
return None
def update_weight_editor(editor):
"""更新权重编辑器
Args:
editor: 编辑器节点
"""
try:
# 获取BlendShape节点
bs_node = cmds.getAttr(f"{editor}.blendShape")
# 获取所有目标
targets = bs_manager.get_all_targets(bs_node)
# 更新权重
for target in targets:
if cmds.objExists(f"{editor}.{target}"):
weight = bs_manager.get_weight(bs_node, target)
cmds.setAttr(f"{editor}.{target}", weight)
except Exception as e:
cmds.warning(f"更新权重编辑器失败: {str(e)}")
def update_expression_preview(preview):
"""更新表情预览
Args:
preview: 预览节点
"""
try:
# 获取表情集
expr_set = cmds.getAttr(f"{preview}.expression")
# 获取预览权重
weight = cmds.getAttr(f"{preview}.weight")
# 应用表情
apply_expression_set(expr_set, weight)
# 检查播放状态
if cmds.getAttr(f"{preview}.play"):
# 更新时间
current_time = cmds.getAttr(f"{preview}.time")
cmds.setAttr(f"{preview}.time", current_time + 1)
# 循环播放
if current_time >= cmds.playbackOptions(q=True, maxTime=True):
cmds.setAttr(f"{preview}.time", cmds.playbackOptions(q=True, minTime=True))
except Exception as e:
cmds.warning(f"更新表情预览失败: {str(e)}")
def create_weight_curve(bs_node, target):
"""创建权重曲线
Args:
bs_node: BlendShape节点
target: 目标名称
Returns:
str: 创建的动画曲线节点名称
"""
try:
# 创建动画曲线
curve = cmds.createNode("animCurve", name=f"{target}_weightCurve")
# 设置曲线类型
cmds.setAttr(f"{curve}.preInfinity", 1) # Cycle
cmds.setAttr(f"{curve}.postInfinity", 1) # Cycle
# 添加关键帧
cmds.setKeyframe(curve, time=0, value=0)
cmds.setKeyframe(curve, time=12, value=1)
cmds.setKeyframe(curve, time=24, value=0)
# 设置切线类型
cmds.keyTangent(curve, edit=True, time=(0,24), outTangentType="linear")
cmds.keyTangent(curve, edit=True, time=(0,24), inTangentType="linear")
return curve
except Exception as e:
cmds.warning(f"创建权重曲线失败: {str(e)}")
return None
def apply_weight_curve(curve, bs_node, target):
"""应用权重曲线
Args:
curve: 动画曲线节点
bs_node: BlendShape节点
target: 目标名称
"""
try:
# 获取当前时间
current_time = cmds.currentTime(q=True)
# 获取曲线值
weight = cmds.getValue(curve, time=current_time)
# 设置权重
bs_manager.set_weight(bs_node, target, weight)
except Exception as e:
cmds.warning(f"应用权重曲线失败: {str(e)}")
def create_expression_combination(name, expressions, weights=None):
"""创建表情组合
Args:
name: 组合名称
expressions: 表情集列表
weights: 权重列表(可选)
Returns:
str: 创建的组合节点名称
"""
try:
# 创建组合节点
combo = cmds.createNode("objectSet", name=f"{name}_exprCombo")
# 添加组合信息
cmds.addAttr(combo, ln="expressions", dt="string")
if weights:
expr_data = ";".join([f"{e}:{w}" for e, w in zip(expressions, weights)])
else:
expr_data = ";".join([f"{e}:1.0" for e in expressions])
cmds.setAttr(f"{combo}.expressions", expr_data, type="string")
# 添加总权重控制
cmds.addAttr(combo, ln="weight", at="double", min=0, max=1, dv=1)
cmds.setAttr(f"{combo}.weight", e=True, keyable=True)
return combo
except Exception as e:
cmds.warning(f"创建表情组合失败: {str(e)}")
return None
def apply_expression_combination(combo, weight=None):
"""应用表情组合
Args:
combo: 组合节点
weight: 总权重(可选)
"""
try:
# 获取组合信息
expr_data = cmds.getAttr(f"{combo}.expressions")
# 获取总权重
if weight is None:
weight = cmds.getAttr(f"{combo}.weight")
# 应用每个表情
for item in expr_data.split(";"):
expr_set, expr_weight = item.split(":")
apply_expression_set(expr_set, float(expr_weight) * weight)
except Exception as e:
cmds.warning(f"应用表情组合失败: {str(e)}")
def optimize_blendshape_weights(bs_node, options=None):
"""优化BlendShape权重
Args:
bs_node: BlendShape节点
options: 优化选项字典
"""
try:
if options is None:
options = {
"threshold": 0.001, # 权重阈值
"normalize": True, # 是否规范化
"remove_unused": True, # 是否移除未使用的目标
"clean_history": True # 是否清理历史
}
# 获取所有目标
targets = bs_manager.get_all_targets(bs_node)
# 移除未使用的目标
if options["remove_unused"]:
for target in targets:
weight = bs_manager.get_weight(bs_node, target)
if abs(weight) < options["threshold"]:
# 获取目标索引
target_index = -1
target_count = cmds.blendShape(bs_node, q=True, weightCount=True)
for i in range(target_count):
alias = cmds.aliasAttr(f"{bs_node}.weight[{i}]", q=True)
if alias == target:
target_index = i
break
if target_index >= 0:
# 移除目标
base_mesh = cmds.blendShape(bs_node, q=True, geometry=True)[0]
cmds.blendShape(bs_node, edit=True, remove=True,
target=(base_mesh, target_index, target, 1.0))
# 规范化权重
if options["normalize"]:
for target in targets:
weight = bs_manager.get_weight(bs_node, target)
if abs(weight) > options["threshold"]:
if abs(weight - 1.0) < options["threshold"]:
bs_manager.set_weight(bs_node, target, 1.0)
elif abs(weight) < options["threshold"]:
bs_manager.set_weight(bs_node, target, 0.0)
else:
normalized = round(weight, 3)
bs_manager.set_weight(bs_node, target, normalized)
# 清理历史
if options["clean_history"]:
base_mesh = cmds.blendShape(bs_node, q=True, geometry=True)[0]
cmds.delete(base_mesh, constructionHistory=True)
print(f"权重优化完成: {bs_node}")
except Exception as e:
cmds.warning(f"权重优化失败: {str(e)}")
def analyze_blendshape_weights(bs_node):
"""分析BlendShape权重
Args:
bs_node: BlendShape节点
Returns:
dict: 分析结果
"""
try:
results = {
"total_targets": 0,
"active_targets": 0,
"unused_targets": 0,
"full_weight_targets": 0,
"partial_weight_targets": 0,
"weight_distribution": {}
}
# 获取所有目标
targets = bs_manager.get_all_targets(bs_node)
results["total_targets"] = len(targets)
# 分析每个目标
for target in targets:
weight = bs_manager.get_weight(bs_node, target)
# 统计权重分布
weight_range = round(weight * 10) / 10 # 取一位小数
if weight_range in results["weight_distribution"]:
results["weight_distribution"][weight_range] += 1
else:
results["weight_distribution"][weight_range] = 1
# 统计目标类型
if abs(weight) < 0.001:
results["unused_targets"] += 1
elif abs(weight - 1.0) < 0.001:
results["full_weight_targets"] += 1
results["active_targets"] += 1
elif abs(weight) > 0.001:
results["partial_weight_targets"] += 1
results["active_targets"] += 1
return results
except Exception as e:
cmds.warning(f"分析权重失败: {str(e)}")
return None