import maya.cmds as cmds from scripts.config import data 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