diff --git a/scripts/Reference/SGAddBlendShape.py b/scripts/Reference/SGAddBlendShape.py new file mode 100644 index 0000000..ab79672 --- /dev/null +++ b/scripts/Reference/SGAddBlendShape.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/09/16 +""" + +import maya.cmds as cmds + +def sg_add_blend_shape(target_name): + """ + 为选中的模型添加混合变形目标 + + 参数: + target_name (str): 要添加的混合变形目标名称 + """ + # 获取当前选中的对象 + selection = cmds.ls(sl=True) + + for sel in selection: + # 遍历50个可能的网格体 + for j in range(50): + mesh = cmds.SGGetMeshes(m=j) # 假设SGGetMeshes是一个自定义命令 + + if sel == mesh: + if target_name: + # 获取混合变形节点名称 + blend_shape = cmds.SGGetBlendShape(mesh) # 假设SGGetBlendShape是一个自定义命令 + + # 如果混合变形节点不存在,创建一个新的 + if not cmds.objExists(blend_shape): + blend_shape = cmds.SGGetMeshes(i=j) + "_blendShapes" + cmds.blendShape(mesh, automatic=True, name=blend_shape) + + # 获取权重属性路径 + attr_weight = f"{blend_shape}.weight" + + # 获取当前混合变形目标列表 + current_blend_shape_list = cmds.listAttr(attr_weight, multi=True) or [] + + # 如果目标名称不在当前列表中,添加新的混合变形目标 + if target_name not in current_blend_shape_list: + # 复制原始模型作为目标 + cmds.duplicate(mesh, returnRootsOnly=True, name=target_name) + + # 获取目标索引并添加混合变形 + target_index = cmds.getAttr(attr_weight, size=True) + cmds.blendShape( + blend_shape, + edit=True, + tangentSpace=True, + target=(mesh, target_index, target_name, 1.0), + weight=(target_index, 0) + ) + + # 删除临时创建的目标模型 + cmds.delete(target_name) + + # 恢复原始选择 + cmds.select(selection, replace=True) \ No newline at end of file diff --git a/scripts/Reference/SGAutomaticGrouping.py b/scripts/Reference/SGAutomaticGrouping.py new file mode 100644 index 0000000..2e25410 --- /dev/null +++ b/scripts/Reference/SGAutomaticGrouping.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/20 +""" + +import maya.cmds as cmds + +def sg_automatic_grouping(): + """ + 自动对模型进行分组 + - 处理head_lod0到head_lod7的分组 + - 处理body_lod0到body_lod3的分组 + """ + # 处理head模型的LOD分组 + for i in range(8): + # 获取当前LOD级别的网格体索引列表 + lod_mesh_indices = cmds.SGGetMeshes(lod=i) + group_name = f"head_lod{i}_grp" + + # 如果分组不存在则创建 + if not cmds.objExists(group_name): + cmds.group(empty=True, name=group_name) + + # 遍历当前LOD级别的所有网格体 + for mesh_index in lod_mesh_indices: + # 跳过索引大于等于50的网格体(body部分) + if mesh_index >= 50: + continue + + # 获取网格体名称 + mesh = cmds.SGGetMeshes(m=mesh_index) + + # 如果网格体存在,将其放入对应分组 + if cmds.objExists(mesh): + try: + cmds.parent(mesh, group_name) + except: + pass # 忽略可能的父子关系错误 + + # 处理body模型的LOD分组 + body_groups = [ + "body_lod0_grp", + "body_lod1_grp", + "body_lod2_grp", + "body_lod3_grp" + ] + + # 遍历处理body的4个LOD级别 + for i in range(4): + body_index = 50 + i + mesh = cmds.SGGetMeshes(m=body_index) + + # 如果分组不存在则创建 + if not cmds.objExists(body_groups[i]): + cmds.group(empty=True, name=body_groups[i]) + + # 如果网格体存在,将其放入对应分组 + if cmds.objExists(mesh): + try: + cmds.parent(mesh, body_groups[i]) + except: + pass # 忽略可能的父子关系错误 \ No newline at end of file diff --git a/scripts/Reference/SGBatchAddBlendShape.py b/scripts/Reference/SGBatchAddBlendShape.py new file mode 100644 index 0000000..9f94645 --- /dev/null +++ b/scripts/Reference/SGBatchAddBlendShape.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/09/24 +""" + +import maya.cmds as cmds +import re + +def sg_batch_add_blend_shape(lod, meshes): + """ + 批量为指定LOD级别的模型添加混合变形目标 + + 参数: + lod (int): LOD级别 + meshes (list): 要处理的网格体名称列表 + """ + # 获取指定LOD级别的所有网格体索引 + mesh_indices = cmds.SGGetMeshes(lod=lod) + + # 开始进度条 + cmds.SGProgressBar(sp=True) + + for mesh_index in mesh_indices: + mesh = cmds.SGGetMeshes(m=mesh_index) + if cmds.objExists(mesh): + lod_mesh = cmds.SGGetMeshes(i=mesh_index) + # 使用正则表达式匹配第一个下划线前的内容 + head = re.match(r'[^_]+', lod_mesh).group(0) + + if head in meshes: + blend_shape = f"{lod_mesh}_blendShapes" + if not cmds.objExists(blend_shape): + # 创建混合变形节点 + cmds.blendShape(mesh, automatic=True, name=blend_shape) + + # 根据不同的模型类型处理不同的目标数量 + target_count_map = { + "head": 0, + "teeth": 1, + "saliva": 1, + "eyeLeft": 3, + "eyeRight": 4, + "eyeshell": 8, + "eyelashes": 8, + "eyeEdge": 8, + "cartilage": 8 + } + + if head in target_count_map: + target_count = target_count_map[head] + count = cmds.SGGetBlendShapes(tc=target_count) + + # 设置进度条 + cmds.SGProgressBar(max=count) + cmds.SGProgressBar(t=f"[{blend_shape}] Creating Target Mesh...") + + # 创建混合变形目标 + for index in range(count): + cmds.SGProgressBar(apr=1) + bs_name = cmds.SGGetBlendShapes(bsn=target_count, index=index) + + # 复制网格体作为目标 + cmds.duplicate(mesh, returnRootsOnly=True, name=bs_name) + + # 对LOD0级别的特定模型设置混合变形目标 + if lod == 0 and (head in ["head", "teeth", "cartilage"]): + cmds.SGSetBlendShapes(ct=mesh_index, index=index, target=bs_name) + + # 添加混合变形目标 + cmds.blendShape( + blend_shape, + edit=True, + tangentSpace=True, + target=(mesh, index, bs_name, 1.0), + weight=(index, 0) + ) + + # 删除临时目标模型 + cmds.delete(bs_name) + + # 结束进度条 + cmds.SGProgressBar(ep=True) \ No newline at end of file diff --git a/scripts/Reference/SGBatchDelBlendShape.py b/scripts/Reference/SGBatchDelBlendShape.py new file mode 100644 index 0000000..f3b6b59 --- /dev/null +++ b/scripts/Reference/SGBatchDelBlendShape.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/09/24 +""" + +import maya.cmds as cmds +import re + +def sg_batch_del_blend_shape(lod, meshes): + """ + 批量删除指定LOD级别模型的混合变形节点 + + 参数: + lod (int): LOD级别 + meshes (list): 要处理的网格体名称列表 + """ + # 获取指定LOD级别的所有网格体索引 + mesh_indices = cmds.SGGetMeshes(lod=lod) + + for mesh_index in mesh_indices: + # 获取网格体名称 + mesh = cmds.SGGetMeshes(m=mesh_index) + + if cmds.objExists(mesh): + # 获取LOD网格体名称 + lod_mesh = cmds.SGGetMeshes(i=mesh_index) + + # 使用正则表达式匹配第一个下划线前的内容 + head = re.match(r'[^_]+', lod_mesh).group(0) + + # 如果当前模型在要处理的列表中 + if head in meshes: + # 构建混合变形节点名称 + blend_shape = f"{lod_mesh}_blendShapes" + + # 如果混合变形节点存在则删除 + if cmds.objExists(blend_shape): + cmds.delete(blend_shape) \ No newline at end of file diff --git a/scripts/Reference/SGBindPoseReset.py b/scripts/Reference/SGBindPoseReset.py new file mode 100644 index 0000000..3bde8da --- /dev/null +++ b/scripts/Reference/SGBindPoseReset.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_get_orig_name(geo): + """ + 获取模型的Orig节点名称列表 + + 参数: + geo (str): 模型名称 + 返回: + list: Orig节点名称列表 + """ + orig = [] + if not geo or not cmds.objExists(geo): + return orig + + shape_mesh = cmds.listRelatives(geo, shapes=True) or [] + for shape in shape_mesh: + if "Orig" in shape: + orig.append(shape) + return orig + +def sg_get_orig_shape_name(geo): + """ + 获取模型的带有groupParts连接的Orig节点名称列表 + + 参数: + geo (str): 模型名称 + 返回: + list: 符合条件的Orig节点名称列表 + """ + orig = [] + if not geo or not cmds.objExists(geo): + return orig + + shape_mesh = cmds.listRelatives(geo, shapes=True) or [] + for shape in shape_mesh: + if "Orig" in shape: + connections = cmds.listConnections(shape) or [] + for conn in connections: + if "groupParts" in conn: + orig.append(shape) + break + return orig + +def sg_bind_pose_reset(objs): + """ + 重置模型的绑定姿势 + + 参数: + objs (list): 要处理的模型列表 + """ + # 开始进度条 + cmds.SGProgressBar(sp=True) + cmds.SGProgressBar(max=len(objs)) + + for obj in objs: + # 更新进度条 + cmds.SGProgressBar(t=f"[{obj}] Bind Pose Reset...") + cmds.SGProgressBar(apr=1) + + # 获取蒙皮变形节点 + skin_cluster = mel.eval(f'findRelatedSkinCluster("{obj}")') + + if cmds.objExists(skin_cluster): + # 获取混合变形节点 + blend_shape = cmds.SGGetBlendShape(obj) + + # 创建临时复制模型 + copy_name = cmds.duplicate(obj, name=f"{obj}_CopyTarget", returnRootsOnly=True) + cmds.blendShape(copy_name[0]) + cmds.delete(copy_name[0], constructionHistory=True) + + # 获取骨骼并应用蒙皮 + bones = cmds.skinCluster(skin_cluster, query=True, influence=True) + cmds.skinCluster(bones, copy_name[0], toSelectedBones=True) + + # 复制蒙皮权重 + cmds.copySkinWeights( + sourceShape=obj, + destinationShape=copy_name[0], + noMirror=True, + surfaceAssociation="closestPoint", + influenceAssociation=["closestJoint", "oneToOne"] + ) + + # 解绑原模型 + cmds.skinCluster(obj, edit=True, unbind=True) + + # 获取复制模型的形状节点 + target_shape = cmds.pickWalk(copy_name[0], direction="down") + + if cmds.objExists(blend_shape): + # 处理带有混合变形的情况 + orig = sg_get_orig_name(obj) + try: + cmds.connectAttr(f"{orig[0]}.outMesh", f"{blend_shape}.originalGeometry[0]", force=True) + cmds.connectAttr(f"{orig[0]}.worldMesh[0]", f"{blend_shape}.input[0].inputGeometry", force=True) + except: pass + + cmds.currentTime(0) + cmds.connectAttr(f"{target_shape[0]}.outMesh", f"{orig[0]}.inMesh", force=True) + cmds.currentTime(1) + cmds.disconnectAttr(f"{target_shape[0]}.outMesh", f"{orig[0]}.inMesh") + cmds.currentTime(0) + else: + # 处理不带混合变形的情况 + orig = sg_get_orig_shape_name(obj) + if orig: + cmds.currentTime(0) + cmds.connectAttr(f"{target_shape[0]}.outMesh", f"{orig[0]}.inMesh", force=True) + cmds.currentTime(1) + cmds.disconnectAttr(f"{target_shape[0]}.outMesh", f"{orig[0]}.inMesh") + cmds.currentTime(0) + + # 重置控制点位置 + vertex_count = cmds.polyEvaluate(orig[0], vertex=True) + for i in range(vertex_count): + cmds.setAttr(f"{orig[0]}.controlPoints[{i}]", 0, 0, 0, type="float3") + else: + # 处理没有Orig节点的情况 + still_shape = cmds.pickWalk(obj, direction="down") + cmds.currentTime(0) + cmds.connectAttr(f"{target_shape[0]}.outMesh", f"{still_shape[0]}.inMesh", force=True) + cmds.currentTime(1) + cmds.disconnectAttr(f"{target_shape[0]}.outMesh", f"{still_shape[0]}.inMesh") + cmds.currentTime(0) + + # 重新绑定蒙皮并复制权重 + cmds.skinCluster(bones, obj, name=skin_cluster, toSelectedBones=True) + cmds.copySkinWeights( + sourceShape=copy_name[0], + destinationShape=obj, + noMirror=True, + surfaceAssociation="closestPoint", + influenceAssociation=["closestJoint", "oneToOne"] + ) + + # 删除临时模型 + cmds.delete(copy_name[0]) + + # 结束进度条 + cmds.SGProgressBar(ep=True) \ No newline at end of file diff --git a/scripts/Reference/SGBindSkinCluster.py b/scripts/Reference/SGBindSkinCluster.py new file mode 100644 index 0000000..ff19d85 --- /dev/null +++ b/scripts/Reference/SGBindSkinCluster.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/26 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_bind_skin_cluster(): + """ + 为模型绑定蒙皮变形器 + - 处理LOD0到LOD7的所有模型 + - 根据模型索引获取对应的骨骼并绑定 + - 设置最大影响数 + """ + # 遍历8个LOD级别 + for i in range(8): + # 获取当前LOD级别的骨骼列表 + joint_lod = cmds.SGGetJoints(lod=i, type="string") + + # 检查所有骨骼是否存在 + exists = True + for joint in joint_lod: + if not cmds.objExists(joint): + exists = False + break + + # 如果所有骨骼都存在,处理当前LOD级别的模型 + if exists: + # 获取当前LOD级别的模型索引列表 + lod_indices = cmds.SGGetMeshes(lod=i) + + # 遍历处理每个模型 + for mesh_index in lod_indices: + # 获取模型名称 + mesh = cmds.SGGetMeshes(m=mesh_index) + + if cmds.objExists(mesh): + # 检查是否已经有蒙皮变形器 + skin_cluster = mel.eval(f'findRelatedSkinCluster("{mesh}")') + + if not cmds.objExists(skin_cluster): + # 构建蒙皮变形器名称 + name = f"{cmds.SGGetMeshes(i=mesh_index)}_skinCluster" + + # 获取影响骨骼和最大影响数 + influences = cmds.SGGetJoints(inf=mesh_index) + max_influences = cmds.SGGetJoints(mi=mesh_index) + + # 创建蒙皮变形器 + cmds.skinCluster( + influences, + mesh, + name=name, + maximumInfluences=max_influences, + toSelectedBones=True + ) + + # 执行权重绑定 + mel.eval('SGBindSkinClusterWeights') \ No newline at end of file diff --git a/scripts/Reference/SGBlendShapeFindFlipTarget.py b/scripts/Reference/SGBlendShapeFindFlipTarget.py new file mode 100644 index 0000000..4a802b1 --- /dev/null +++ b/scripts/Reference/SGBlendShapeFindFlipTarget.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_blend_shape_find_flip_target(axis, target_ids_a, target_ids_b, blend_shape): + """ + 查找并设置混合变形目标的镜像关系 + + 参数: + axis (int): 镜像轴向 (1=X, 2=Y, 3=Z, 4=Topology) + target_ids_a (list): 源目标ID列表 + target_ids_b (list): 目标ID列表 + blend_shape (str): 混合变形节点名称 + """ + # 检查混合变形节点是否存在 + if not cmds.objExists(blend_shape): + cmds.warning(f"No Exists BlendShape: {blend_shape}") + return + + # 检查源目标和目标列表长度是否相等 + if len(target_ids_a) != len(target_ids_b): + cmds.warning("Unequal Mirror Targets...") + return + + # 获取当前对称建模设置 + original_symmetry = cmds.symmetricModelling(query=True, symmetry=True) + symmetry_space = None + symmetry_axis = None + + if original_symmetry: + symmetry_space = cmds.symmetricModelling(query=True, about=True) + if symmetry_space == "topo": + symmetry_axis = mel.eval('blendShapeGetTopoSymmetryEdge()') + else: + symmetry_axis = cmds.symmetricModelling(query=True, axis=True) + + # 处理每对目标 + for target_a, target_b in zip(target_ids_a, target_ids_b): + # 检查并重命名目标 + target_name = f"{blend_shape}.weight[{target_a}]" + target_name = cmds.aliasAttr(target_name, query=True) + + exist = False + if cmds.objExists(target_name): + exist = True + cmds.rename(target_name, f"{target_name}_temp") + + # 重新生成目标并设置连接 + target_mesh = cmds.sculptTarget( + blend_shape, + edit=True, + regenerate=True, + target=target_a + ) + target_mesh_shape = cmds.listRelatives(target_mesh[0], shapes=True) + + # 断开原有连接并建立新连接 + cmds.disconnectAttr( + f"{target_mesh_shape[0]}.worldMesh[0]", + f"{blend_shape}.inputTarget[0].inputTargetGroup[{target_a}].inputTargetItem[6000].inputGeomTarget" + ) + cmds.connectAttr( + f"{target_mesh_shape[0]}.worldMesh[0]", + f"{blend_shape}.inputTarget[0].inputTargetGroup[{target_b}].inputTargetItem[6000].inputGeomTarget", + force=True + ) + + # 删除临时目标网格 + cmds.delete(target_mesh) + + # 恢复原始名称 + if exist: + cmds.rename(f"{target_name}_temp", target_name) + + # 获取几何体索引 + geometry_indices = cmds.blendShape(blend_shape, query=True, geometryIndices=True) + + # 构建命令字符串 + cmd_parts = [] + for geometry in geometry_indices: + cmd = f"blendShape -e " + for target_id in target_ids_b: + cmd += f"-ft {geometry} {target_id} " + + if axis == 4: + cmd += "-ss 0 " + else: + cmd += "-ss 1 " + if axis == 1: + cmd += "-sa x " + elif axis == 2: + cmd += "-sa y " + elif axis == 3: + cmd += "-sa z " + cmd += f"{blend_shape};" + cmd_parts.append(cmd) + + # 添加对称设置恢复命令 + if not original_symmetry or not symmetry_axis: + cmd_parts.append('symmetricModelling -s 0') + elif symmetry_space == "topo": + cmd_parts.append(f'symmetricModelling -e -about {symmetry_space} -s 1 {symmetry_axis}') + else: + cmd_parts.append(f'symmetricModelling -e -about {symmetry_space} -axis {symmetry_axis} -s 1') + + # 执行命令 + for cmd in cmd_parts: + mel.eval(cmd) \ No newline at end of file diff --git a/scripts/Reference/SGBlendShapeFlipTarget.py b/scripts/Reference/SGBlendShapeFlipTarget.py new file mode 100644 index 0000000..d7c4fb7 --- /dev/null +++ b/scripts/Reference/SGBlendShapeFlipTarget.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_blend_shape_flip_target(axis, target_ids, blend_shape): + """ + 翻转混合变形目标 + + 参数: + axis (int): 镜像轴向 (1=X, 2=Y, 3=Z, 4=Topology) + target_ids (list): 要翻转的目标ID列表 + blend_shape (str): 混合变形节点名称 + """ + # 检查混合变形节点是否存在 + if not cmds.objExists(blend_shape): + cmds.warning(f"No Exists BlendShape: {blend_shape}") + return + + # 获取当前对称建模设置 + original_symmetry = cmds.symmetricModelling(query=True, symmetry=True) + symmetry_space = None + symmetry_axis = None + + if original_symmetry: + symmetry_space = cmds.symmetricModelling(query=True, about=True) + if symmetry_space == "topo": + symmetry_axis = mel.eval('blendShapeGetTopoSymmetryEdge()') + else: + symmetry_axis = cmds.symmetricModelling(query=True, axis=True) + + # 获取几何体索引 + geometry_indices = cmds.blendShape(blend_shape, query=True, geometryIndices=True) + + # 构建命令字符串 + cmd_parts = [] + + # 为每个几何体构建翻转命令 + for geometry in geometry_indices: + cmd = f"blendShape -e " + for target_id in target_ids: + cmd += f"-ft {geometry} {target_id} " + + # 设置对称选项 + if axis == 4: + cmd += "-ss 0 " + else: + cmd += "-ss 1 " + if axis == 1: + cmd += "-sa x " + elif axis == 2: + cmd += "-sa y " + elif axis == 3: + cmd += "-sa z " + cmd += f"{blend_shape};" + cmd_parts.append(cmd) + + # 添加对称设置恢复命令 + if not original_symmetry or not symmetry_axis: + cmd_parts.append('symmetricModelling -s 0') + elif symmetry_space == "topo": + cmd_parts.append(f'symmetricModelling -e -about {symmetry_space} -s 1 {symmetry_axis}') + else: + cmd_parts.append(f'symmetricModelling -e -about {symmetry_space} -axis {symmetry_axis} -s 1') + + # 执行命令 + for cmd in cmd_parts: + mel.eval(cmd) \ No newline at end of file diff --git a/scripts/Reference/SGBlendShapeMirrorTarget.py b/scripts/Reference/SGBlendShapeMirrorTarget.py new file mode 100644 index 0000000..c503ea1 --- /dev/null +++ b/scripts/Reference/SGBlendShapeMirrorTarget.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_blend_shape_mirror_target(direction, axis, target_ids, blend_shape): + """ + 镜像混合变形目标 + + 参数: + direction (int): 镜像方向 + axis (int): 镜像轴向 (1=X, 2=Y, 3=Z, 4=Topology) + target_ids (list): 要镜像的目标ID列表 + blend_shape (str): 混合变形节点名称 + """ + # 检查混合变形节点是否存在 + if not cmds.objExists(blend_shape): + cmds.warning(f"No Exists BlendShape: {blend_shape}") + return + + # 获取当前对称建模设置 + original_symmetry = cmds.symmetricModelling(query=True, symmetry=True) + symmetry_space = None + symmetry_axis = None + + if original_symmetry: + symmetry_space = cmds.symmetricModelling(query=True, about=True) + if symmetry_space == "topo": + symmetry_axis = mel.eval('blendShapeGetTopoSymmetryEdge()') + else: + symmetry_axis = cmds.symmetricModelling(query=True, axis=True) + + # 获取几何体索引 + geometry_indices = cmds.blendShape(blend_shape, query=True, geometryIndices=True) + + # 构建命令字符串 + cmd_parts = [] + + # 为每个几何体构建镜像命令 + for geometry in geometry_indices: + cmd = f"blendShape -e " + for target_id in target_ids: + cmd += f"-mt {geometry} {target_id} " + + # 设置镜像方向 + cmd += f"-md {direction} " + + # 设置对称选项 + if axis == 4: + cmd += "-ss 0 " + else: + cmd += "-ss 1 " + if axis == 1: + cmd += "-sa x " + elif axis == 2: + cmd += "-sa y " + elif axis == 3: + cmd += "-sa z " + cmd += f"{blend_shape};" + cmd_parts.append(cmd) + + # 添加对称设置恢复命令 + if not original_symmetry or not symmetry_axis: + cmd_parts.append('symmetricModelling -s 0') + elif symmetry_space == "topo": + cmd_parts.append(f'symmetricModelling -e -about {symmetry_space} -s 1 {symmetry_axis}') + else: + cmd_parts.append(f'symmetricModelling -e -about {symmetry_space} -axis {symmetry_axis} -s 1') + + # 执行命令 + for cmd in cmd_parts: + mel.eval(cmd) \ No newline at end of file diff --git a/scripts/Reference/SGCloneBlendShape.py b/scripts/Reference/SGCloneBlendShape.py new file mode 100644 index 0000000..c3140a8 --- /dev/null +++ b/scripts/Reference/SGCloneBlendShape.py @@ -0,0 +1,517 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sparkey_get_blend_shape(geo): + """ + 获取指定模型的混合变形节点列表 + + 参数: + geo (str): 模型名称 + 返回: + list: 混合变形节点列表 + """ + if not geo or not cmds.objExists(geo): + return [] + + # 获取历史记录 + history = cmds.listHistory(geo, pruneDagObjects=True, interestLevel=2) or [] + + # 过滤出blendShape节点 + return [node for node in history if cmds.nodeType(node) == "blendShape"] + +def sparkey_get_wrap(geo): + """ + 获取指定模型的wrap变形节点列表 + + 参数: + geo (str): 模型名称 + 返回: + list: wrap变形节点列表 + """ + if not geo or not cmds.objExists(geo): + return [] + + # 获取历史记录 + history = cmds.listHistory(geo, pruneDagObjects=True, interestLevel=2) or [] + + # 过滤出wrap节点 + return [node for node in history if cmds.nodeType(node) == "wrap"] + +def sg_wrap_convert_blend(): + """ + 将选中的模型的混合变形转换为新的混合变形 + """ + # 获取选中的对象 + sel = cmds.ls(selection=True) + if len(sel) != 2: + cmds.warning("请选择两个模型对象") + return + + # 获取混合变形节点 + bs_nodes = sparkey_get_blend_shape(sel[1]) + if not bs_nodes: + cmds.warning("目标模型不包含混合变形,请确保选择正确的源和目标模型") + return + + # 获取混合变形目标列表 + bs_target_attrs = cmds.listAttr(f"{bs_nodes[0]}.w", multi=True) or [] + if not bs_target_attrs: + cmds.warning("混合变形目标列表为空") + return + + # 如果选择了wrap方法 + if cmds.checkBox("wrapMethod", query=True, value=True): + cmds.select(sel[0], sel[1]) + mel.eval('CreateWrap') + + # 创建临时组 + if cmds.objExists("WrapConvertTemp"): + cmds.delete("WrapConvertTemp") + temp_group = cmds.group(empty=True, name="WrapConvertTemp") + + rb_mesh = [] + # 复制每个混合变形目标 + for bs_attr in bs_target_attrs: + bs_weight = cmds.getAttr(f"{bs_nodes[0]}.{bs_attr}") + is_connected = cmds.connectionInfo(f"{bs_nodes[0]}.{bs_attr}", isDestination=True) + + if bs_weight != 1 and not is_connected: + # 设置权重为1并复制模型 + cmds.setAttr(f"{bs_nodes[0]}.{bs_attr}", 1) + copy_mesh = cmds.duplicate(sel[0])[0] + cmds.parent(copy_mesh, temp_group) + copy_mesh = cmds.rename(copy_mesh, bs_attr) + rb_mesh.append(f":WrapConvertTemp|:{bs_attr}") + cmds.setAttr(f"{bs_nodes[0]}.{bs_attr}", 0) + + # 处理wrap节点 + if cmds.checkBox("wrapMethod", query=True, value=True): + wrap_nodes = sparkey_get_wrap(sel[0]) + base_nodes = cmds.ls(f"{sel[1]}Bas*") + if wrap_nodes or base_nodes: + cmds.delete(wrap_nodes + base_nodes) + + # 创建新的混合变形 + if rb_mesh: + cmds.blendShape(rb_mesh, sel[0], frontOfChain=True) + + # 处理DeltaMush + if cmds.objExists('deltaMush') and cmds.checkBox("deltaMushTarget", query=True, value=True): + delta_value = cmds.intSliderGrp("deltaIntensity", query=True, value=True) + _process_delta_mush(sel[0], delta_value) + + # 清理 + cmds.delete("WrapConvertTemp") + print("混合变形处理完成") + +def _process_delta_mush(target, delta_value): + """ + 处理DeltaMush相关的操作 + + 参数: + target (str): 目标模型 + delta_value (int): DeltaMush强度值 + """ + # 创建DeltaMush变形器 + dl_names = cmds.deltaMush( + target, + smoothingIterations=delta_value, + smoothingStep=1.0, + pinBorderVertices=1, + envelope=1 + ) + + # 获取混合变形节点和目标 + bs_nodes = sparkey_get_blend_shape(target) + bs_target_attrs = cmds.listAttr(f"{bs_nodes[0]}.w", multi=True) or [] + + # 创建临时组 + if cmds.objExists("WrapConvertTemp"): + cmds.delete("WrapConvertTemp") + temp_group = cmds.group(empty=True, name="WrapConvertTemp") + + rb_mesh = [] + # 处理每个混合变形目标 + for bs_attr in bs_target_attrs: + bs_weight = cmds.getAttr(f"{bs_nodes[0]}.{bs_attr}") + if bs_weight != 1: + # 设置权重并创建关键帧 + cmds.setAttr(f"{bs_nodes[0]}.{bs_attr}", 0) + cmds.currentTime(-10) + cmds.setAttr(f"{bs_nodes[0]}.{bs_attr}", 1) + cmds.currentTime(0) + + # 复制模型并重命名 + copy_mesh = cmds.duplicate(target)[0] + cmds.parent(copy_mesh, temp_group) + copy_mesh = cmds.rename(copy_mesh, bs_attr) + rb_mesh.append(f":WrapConvertTemp|:{bs_attr}") + + # 重置权重 + cmds.setAttr(f"{bs_nodes[0]}.{bs_attr}", 0) + cmds.currentTime(-10) + cmds.currentTime(0) + + # 清理并创建新的混合变形 + cmds.delete(dl_names + bs_nodes) + if rb_mesh: + cmds.blendShape(rb_mesh, target, frontOfChain=True) + cmds.delete("WrapConvertTemp") + print("DeltaMush优化完成") + +def sg_clone_blend_shape(): + """ + 创建混合变形克隆工具的UI界面 + """ + window_name = "wrapChangeBS" + if cmds.window(window_name, exists=True): + cmds.deleteUI(window_name) + + # 创建主窗口 + window = cmds.window( + window_name, + title="BlendShapeBake", + width=300, + height=300, + menuBar=True + ) + + # 创建主布局 + main_layout = cmds.columnLayout( + adjustableColumn=1, + columnAttach=["both", 5], + rowSpacing=2, + columnWidth=150 + ) + + cmds.separator(height=10, style="in") + + # 创建Wrap方法选项 + cmds.checkBox( + "wrapMethod", + label="Use Wrap Method", + value=1, + changeCommand="sparkly_wrap_convert_blend_check_return" + ) + + # 创建DeltaMush选项(如果可用) + if cmds.pluginInfo("deltaMush", query=True, loaded=True): + cmds.checkBox( + "deltaMushTarget", + label="DeltaMushTarget", + value=0, + changeCommand="sparkly_wrap_convert_blend_check_return" + ) + cmds.intSliderGrp( + "deltaIntensity", + field=True, + minValue=0, + maxValue=100, + value=10, + width=50 + ) + else: + cmds.text( + label="低版本DeltaMush不可用", + font="boldLabelFont" + ) + + cmds.separator(height=10, style="in") + + # 创建功能按钮 + cmds.button(label="Cloning", command="sg_wrap_convert_blend()") + cmds.button(label="Copy", command="sg_copy_blend()") + cmds.button(label="Mirror", command="sg_mirror_blend()") + + cmds.separator(height=10, style="in") + + # 调整窗口大小 + cmds.window( + window_name, + edit=True, + width=260, + height=85 + ) + + cmds.showWindow() + +def sparkly_wrap_convert_blend_check_return(): + """ + 检查框回调函数(保持为空以保持兼容性) + """ + pass + +def sg_copy_blend(): + """ + 复制混合变形目标 + """ + # 获取选中的混合变形目标 + sel_bs_name_list = mel.eval('getShapeEditorTreeviewSelection(14)') + + # 解析目标索引 + tmp_a = sel_bs_name_list[0].split('.') + tmp_b = sel_bs_name_list[1].split('.') + + target_a = int(tmp_a[1]) + target_b = int(tmp_b[1]) + + # 重新生成目标并设置连接 + mesh_a = cmds.sculptTarget( + tmp_a[0], + edit=True, + regenerate=True, + target=target_a + ) + + mesh_shape_a = cmds.listRelatives(mesh_a[0], shapes=True) + + # 断开原有连接并建立新连接 + cmds.disconnectAttr( + f"{mesh_shape_a[0]}.worldMesh[0]", + f"{tmp_a[0]}.inputTarget[0].inputTargetGroup[{target_a}].inputTargetItem[6000].inputGeomTarget" + ) + cmds.connectAttr( + f"{mesh_shape_a[0]}.worldMesh[0]", + f"{tmp_a[0]}.inputTarget[0].inputTargetGroup[{target_b}].inputTargetItem[6000].inputGeomTarget", + force=True + ) + + # 删除临时网格 + cmds.delete(mesh_a) + +def sg_mirror_blend(): + """ + 镜像混合变形目标 + """ + # 获取当前对称设置 + original_symmetry = cmds.symmetricModelling(query=True, symmetry=True) + symmetry_space = None + symmetry_axis = None + + if original_symmetry: + symmetry_space = cmds.symmetricModelling(query=True, about=True) + if symmetry_space == "topo": + symmetry_axis = mel.eval('blendShapeGetTopoSymmetryEdge()') + else: + symmetry_axis = cmds.symmetricModelling(query=True, axis=True) + + # 获取选中的混合变形目标 + sel_bs_name_list = mel.eval('getShapeEditorTreeviewSelection(14)') + + # 解析目标索引 + tmp_a = sel_bs_name_list[0].split('.') + tmp_b = sel_bs_name_list[1].split('.') + + target_a = int(tmp_a[1]) + target_b = int(tmp_b[1]) + + # 重新生成目标并设置连接 + mesh_a = cmds.sculptTarget( + tmp_a[0], + edit=True, + regenerate=True, + target=target_a + ) + + mesh_shape_a = cmds.listRelatives(mesh_a[0], shapes=True) + + # 断开原有连接并建立新连接 + cmds.disconnectAttr( + f"{mesh_shape_a[0]}.worldMesh[0]", + f"{tmp_a[0]}.inputTarget[0].inputTargetGroup[{target_a}].inputTargetItem[6000].inputGeomTarget" + ) + cmds.connectAttr( + f"{mesh_shape_a[0]}.worldMesh[0]", + f"{tmp_a[0]}.inputTarget[0].inputTargetGroup[{target_b}].inputTargetItem[6000].inputGeomTarget", + force=True + ) + + # 删除临时网格 + cmds.delete(mesh_a) + + # 翻转目标 + cmds.blendShape( + tmp_b[0], + edit=True, + flipTarget=True, + geometryIndex=0, + targetIndex=target_b, + symmetryAxis="x", + symmetrySpace=1 + ) + + # 重置反射模式 + mel.eval('reflectionSetMode none') + + # 恢复对称设置 + if not original_symmetry or not symmetry_axis: + mel.eval('symmetricModelling -s 0') + elif symmetry_space == "topo": + mel.eval(f'symmetricModelling -e -about {symmetry_space} -s 1 {symmetry_axis}') + else: + mel.eval(f'symmetricModelling -e -about {symmetry_space} -axis {symmetry_axis} -s 1') + +def get_shape_editor_treeview_selection(scope): + """ + 获取形状编辑器树视图中的选择 + + 参数: + scope (int): 选择范围: + 0-8: 简单范围 + 9: 单个BSD中的目标 + 10-16: 纯类型选择 + 20: 最后选择的项目 + 21,24: 纯类型及其组类型 + 30: 引用项目检查 + 31: 第一个选择项 + + 返回: + list: 根据范围返回的字符串数组 + """ + # 定义类型上限 + type_roof = 6 # 注意:添加新树项类型时更新上限 + + # 检查选择变量是否存在 + if not cmds.optionVar(exists="blendShapeEditorTreeViewSelection"): + return [] + + # 获取选择数据 + ov = cmds.optionVar(query="blendShapeEditorTreeViewSelection") + + # 处理不同范围的选择 + if -1 < scope < type_roof + 3: # 范围 0-8 + return ov[scope].split("/") if ov[scope] else [] + + if scope == 9: # 范围 9 + return filter_single_bsd(ov[8].split("/") if ov[8] else []) + + if 9 < scope < type_roof + 11: # 范围 10-16 + selection = ov[scope-10].split("/") if ov[scope-10] else [] + if not selection: + return [] + + # 跳过相关类型检查 + type_array = list(range(type_roof)) + new_type_array = [x for x in type_array if x not in [scope-10, 2, 5]] + + if scope in [12, 15]: + new_type_array = [x for x in new_type_array if x != scope-12] + + # 检查混合选择 + for i in new_type_array: + if ov[i] and ov[i].split("/"): + return [] + return selection + + if scope == 20: # 范围 20 + return ov[9].split("/") if ov[9] else [] + + if scope in [21, 24]: # 范围 21,24 + selection_out = ov[scope-20].split("/") if ov[scope-20] else [] + selection_in = ov[scope-19].split("/") if ov[scope-19] else [] + selection = list(set(selection_out + selection_in)) + + if not selection: + return [] + + # 跳过相关类型检查 + type_array = list(range(type_roof)) + new_type_array = [x for x in type_array if x not in [scope-20, scope-19, scope-21]] + + # 检查混合选择 + for i in new_type_array: + if ov[i] and ov[i].split("/"): + return [] + return selection + + if scope == 30: # 范围 30 + return ov[10].split("/") if ov[10] else [] + + if scope == 31: # 范围 31 + return ov[11].split("/") if ov[11] else [] + + return [] # 无效范围 + +def filter_single_bsd(items): + """ + 从单个混合变形变形器中返回选定的目标(组) + + 参数: + items (list): 要过滤的项目列表 + 返回: + list: 过滤后的项目列表,如果来自多个BSD则返回空列表 + """ + result = [] + one_bsd = "" + + for item in items: + sub_strings = item.split(".") + if not one_bsd: + one_bsd = sub_strings[0] + if one_bsd != sub_strings[0]: + return [] + result.append(item) + + return result + +def is_shape_editor_ref_item_selected(): + """ + 检查是否在形状编辑器树视图中选择了引用项 + + 返回: + bool: 如果选择了引用项则返回True,否则返回False + """ + result = get_shape_editor_treeview_selection(30) + if len(result) == 1: + return result[0] == "1" + return False + +def blend_shape_editor_selected_ancestor(bsd_name, tgt_index, selected_targets): + """ + 获取选定的祖先(如果存在) + + 参数: + bsd_name (str): 混合变形变形器名称 + tgt_index (int): 目标索引(>=0为目标,<0为目标目录) + selected_targets (list): 选定的目标列表 + + 返回: + int: 最高选定的父目录索引,如果没有选定的祖先则返回0 + """ + highest_parent_directory = 0 + parent_dir = -1 + + if tgt_index >= 0: + # 这是一个目标,获取父目录 + temp_attr = f"{bsd_name}.parentDirectory[{tgt_index}]" + parent_dir = cmds.getAttr(temp_attr) + + if parent_dir <= -1: + return 0 # 默认值 + if parent_dir == 0: + return 0 # 目标项直接在混合变形节点下 + else: + # 这是一个目标目录,获取父目录 + temp_attr = f"{bsd_name}.targetDirectory[{-tgt_index}].parentIndex" + parent_dir = cmds.getAttr(temp_attr) + + while parent_dir > 0: + # 检查此目录是否被选中 + dir_name_encoded = f"{bsd_name}.{-parent_dir}" + if dir_name_encoded in selected_targets: + highest_parent_directory = parent_dir + + # 获取父目录的父目录 + temp_attr = f"{bsd_name}.targetDirectory[{parent_dir}].parentIndex" + parent_dir = cmds.getAttr(temp_attr) + + return highest_parent_directory \ No newline at end of file diff --git a/scripts/Reference/SGClothingWeight.py b/scripts/Reference/SGClothingWeight.py new file mode 100644 index 0000000..844e116 --- /dev/null +++ b/scripts/Reference/SGClothingWeight.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/10/31 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_clothing_weight(): + """ + 处理服装权重 + - 获取选中的对象 + - 从头部和身体模型复制蒙皮权重 + - 应用到选中的服装模型上 + """ + # 获取选中的对象 + selection = cmds.ls(selection=True) + + # 获取头部和身体模型 + head = cmds.SGGetMeshes(m=0) + body = cmds.SGGetMeshes(m=50) + + # 清除选择并选择身体和目标模型 + cmds.select(clear=True) + cmds.select(body, add=True) + cmds.select(selection, add=True) + + # 执行蒙皮复制 + mel.eval('SGCopySkin') + + # 为每个选中的模型复制头部和身体的权重 + for obj in selection: + cmds.select(clear=True) + cmds.select(head, add=True) + cmds.select(body, add=True) + cmds.select(obj, add=True) + + # 复制蒙皮权重 + cmds.copySkinWeights( + noMirror=True, + surfaceAssociation="closestPoint", + influenceAssociation="closestJoint" + ) \ No newline at end of file diff --git a/scripts/Reference/SGCopySkin.py b/scripts/Reference/SGCopySkin.py new file mode 100644 index 0000000..1b68afd --- /dev/null +++ b/scripts/Reference/SGCopySkin.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_copy_skin(): + """ + 复制蒙皮权重 + - 从第一个选中的模型复制蒙皮权重到其他选中的模型 + - 如果目标模型已有蒙皮,会先解绑再重新绑定 + """ + # 获取选中的模型 + meshes = cmds.ls(selection=True) + + # 获取源模型的影响骨骼 + joints = cmds.skinCluster(meshes[0], query=True, influence=True) + + # 处理每个目标模型 + for mesh in meshes[1:]: + # 检查是否已有蒙皮变形器 + skin_cluster = mel.eval(f'findRelatedSkinCluster("{mesh}")') + + # 如果存在蒙皮变形器则解绑 + if cmds.objExists(skin_cluster): + cmds.skinCluster(mesh, edit=True, unbind=True) + + # 创建新的蒙皮变形器 + cmds.skinCluster(joints, mesh) + + # 复制蒙皮权重 + cmds.copySkinWeights( + sourceShape=meshes[0], + destinationShape=mesh, + noMirror=True, + surfaceAssociation="closestPoint", + influenceAssociation="closestJoint" + ) \ No newline at end of file diff --git a/scripts/Reference/SGCreateARKit.py b/scripts/Reference/SGCreateARKit.py new file mode 100644 index 0000000..626ef28 --- /dev/null +++ b/scripts/Reference/SGCreateARKit.py @@ -0,0 +1,187 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_copy_meshes(name): + """ + 复制网格体并组织到指定组中 + + 参数: + name (str): 组名称 + """ + # 获取所有需要处理的网格体 + meshes = [cmds.SGGetMeshes(m=i) for i in range(9)] + + # 创建组 + if not cmds.objExists(name): + cmds.group(empty=True, name=name) + if name != "defaults": + cmds.setAttr(f"{name}.visibility", 0) + + # 复制每个网格体并放入组中 + for mesh in meshes: + if cmds.objExists(mesh): + duplicate = f"{mesh}_{name}" + cmds.duplicate(mesh, returnRootsOnly=True, name=duplicate) + cmds.parent(duplicate, name) + +def sg_merge_blend_shape(arkit_list): + """ + 合并混合变形目标 + + 参数: + arkit_list (list): ARKit表情目标列表 + """ + # 获取所有需要处理的网格体 + meshes = [cmds.SGGetMeshes(m=i) for i in range(9)] + + # 处理每个网格体 + for mesh in meshes: + if cmds.objExists(mesh): + # 清除选择并选择所有目标 + cmds.select(clear=True) + for target in arkit_list: + duplicate = f"{mesh}_{target}" + cmds.select(duplicate, add=True) + + # 选择默认形状 + cmds.select(f"{mesh}_defaults", add=True) + + # 创建混合变形器 + blend_shape = cmds.blendShape(automatic=True)[0] + + # 设置混合变形目标的别名 + for i, target in enumerate(arkit_list): + cmds.aliasAttr(target, f"{blend_shape}.w[{i}]") + +def sg_go_to_build_pose(index): + """ + 设置指定索引的构建姿势 + + 参数: + index (int): 姿势索引 + """ + # 定义控制器设置字典 + pose_settings = { + 0: {"CTRL_L_eye_blink.translateY": 1}, + 1: {"CTRL_L_eye_blink.translateY": 0, "CTRL_L_eye.translateY": -1}, + 2: {"CTRL_L_eye.translateY": 0, "CTRL_L_eye.translateX": -1}, + 3: {"CTRL_L_eye.translateX": 1}, + 4: {"CTRL_L_eye.translateX": 0, "CTRL_L_eye.translateY": 1}, + # ... 更多姿势设置 + } + + # 如果存在该索引的设置,则应用设置 + if index in pose_settings: + for ctrl, value in pose_settings[index].items(): + try: + cmds.setAttr(ctrl, value) + except: + pass + +def sg_go_to_build_face_pose(): + """ + 重置所有面部控制器到默认位置 + """ + # 定义需要重置的控制器列表 + controls = [ + "CTRL_C_jaw.translateY", + "CTRL_C_jaw.translateX", + "CTRL_R_eyelashes_tweakerOut.translateX", + # ... 更多控制器 + ] + + # 重置所有控制器 + for ctrl in controls: + try: + cmds.setAttr(ctrl, 0) + except: + pass + +def sg_create_arkit(): + """ + 创建ARKit表情系统 + """ + # ARKit表情目标列表 + arkit_list = [ + "eyeBlinkLeft", + "eyeLookDownLeft", + "eyeLookInLeft", + "eyeLookOutLeft", + "eyeLookUpLeft", + "eyeSquintLeft", + "eyeWideLeft", + "eyeBlinkRight", + "eyeLookDownRight", + "eyeLookInRight", + "eyeLookOutRight", + "eyeLookUpRight", + "eyeSquintRight", + "eyeWideRight", + "jawForward", + "jawLeft", + "jawRight", + "jawOpen", + "mouthClose", + "mouthFunnel", + "mouthPucker", + "mouthLeft", + "mouthRight", + "mouthSmileLeft", + "mouthSmileRight", + "mouthFrownLeft", + "mouthFrownRight", + "mouthDimpleLeft", + "mouthDimpleRight", + "mouthStretchLeft", + "mouthStretchRight", + "mouthRollLower", + "mouthRollUpper", + "mouthShrugLower", + "mouthShrugUpper", + "mouthPressLeft", + "mouthPressRight", + "mouthLowerDownLeft", + "mouthLowerDownRight", + "mouthUpperUpLeft", + "mouthUpperUpRight", + "browDownLeft", + "browDownRight", + "browInnerUp", + "browOuterUpLeft", + "browOuterUpRight", + "cheekPuff", + "cheekSquintLeft", + "cheekSquintRight", + "noseSneerLeft", + "noseSneerRight", + "tongueOut" + ] + + # 重置到构建姿势 + sg_go_to_build_face_pose() + + # 复制默认网格体 + sg_copy_meshes("defaults") + + # 创建每个表情目标 + for i, target in enumerate(arkit_list): + sg_go_to_build_pose(i) + sg_copy_meshes(target) + + # 合并混合变形目标 + sg_merge_blend_shape(arkit_list) + + # 返回到构建姿势 + sg_go_to_build_face_pose() + +# 执行主函数 +if __name__ == "__main__": + sg_create_arkit() \ No newline at end of file diff --git a/scripts/Reference/SGCreateBlendShape.py b/scripts/Reference/SGCreateBlendShape.py new file mode 100644 index 0000000..d11ed35 --- /dev/null +++ b/scripts/Reference/SGCreateBlendShape.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_create_blend_shape(): + """ + 为模型创建混合变形目标 + - 处理索引0到49的模型 + - 为每个模型创建对应的混合变形目标 + - 显示进度条指示创建进度 + """ + # 开始进度条 + cmds.SGProgressBar(sp=True) + + # 遍历处理每个模型 + for mesh_index in range(50): + # 获取模型名称 + mesh = cmds.SGGetMeshes(m=mesh_index) + + if cmds.objExists(mesh): + # 获取混合变形节点名称 + blend_shape = cmds.SGGetBlendShape(mesh) + + # 获取目标数量 + count = cmds.SGGetBlendShapes(dr="r", tc=mesh_index) + + # 如果没有混合变形节点且有目标需要创建 + if not cmds.objExists(blend_shape) and count > 0: + # 构建混合变形节点名称 + blend_shape_name = f"{cmds.SGGetMeshes(i=mesh_index)}_blendShapes" + + # 设置进度条 + cmds.SGProgressBar(max=count) + cmds.SGProgressBar(t=f"[{blend_shape_name}] Creating Target Mesh...") + + # 创建混合变形节点 + cmds.blendShape(mesh, name=blend_shape_name) + + # 创建每个目标 + for index in range(count): + # 更新进度条 + cmds.SGProgressBar(apr=1) + + # 获取目标名称 + bs_name = cmds.SGGetBlendShapes(dr="r", bsn=(mesh_index, index)) + + # 复制模型作为目标 + cmds.duplicate(mesh, returnRootsOnly=True, name=bs_name) + + # 设置混合变形目标 + cmds.SGSetBlendShapes(ct=mesh_index, index=index, target=bs_name) + + # 添加到混合变形节点 + cmds.blendShape( + blend_shape_name, + edit=True, + tangentSpace=True, + target=(mesh, index, bs_name, 1.0), + weight=(index, 0) + ) + + # 删除临时目标模型 + cmds.delete(bs_name) + + # 结束进度条 + cmds.SGProgressBar(ep=True) \ No newline at end of file diff --git a/scripts/Reference/SGCreateBodyCtrl.py b/scripts/Reference/SGCreateBodyCtrl.py new file mode 100644 index 0000000..b95056a --- /dev/null +++ b/scripts/Reference/SGCreateBodyCtrl.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import os +import imp +import maya.cmds as cmds + +def sg_create_body_ctrl(): + """ + 创建身体控制器 + - 从环境变量获取路径 + - 动态加载身体控制器模块 + """ + # 获取环境变量中的路径 + path = os.environ.get('SG_PATH') + + # 构建模块路径 + module_path = os.path.join(path, 'files/meta_body_ctrl/meta_body_ctrl.py') + + # 动态加载身体控制器模块 + imp.load_source('', module_path) \ No newline at end of file diff --git a/scripts/Reference/SGCreateLOD.py b/scripts/Reference/SGCreateLOD.py new file mode 100644 index 0000000..d1a28d0 --- /dev/null +++ b/scripts/Reference/SGCreateLOD.py @@ -0,0 +1,204 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_create_lod(index): + """ + 创建LOD模型 + + 参数: + index (int): LOD级别索引,0表示创建所有级别 + """ + # 检查并加载Unfold3D插件 + if not cmds.pluginInfo("Unfold3D", query=True, loaded=True): + cmds.loadPlugin("Unfold3D") + + # 如果索引为0,创建所有LOD级别 + if index == 0: + for lod in range(1, 8): + sg_create_lod(lod) + return + + # 获取当前轴向设置 + axis = cmds.upAxis(query=True, axis=True) + axis = "YAxisUp" if axis == "y" else "ZAxisUp" + + # 定义网格体列表 + meshes = [ + "head", "teeth", "saliva", "eyeLeft", "eyeRight", + "eyeshell", "eyelashes", "eyeEdge", "cartilage", "body" + ] + + # 创建头部组和网格体 + head_grp = f"head_lod{index}_grp" + head_mesh = f"head_lod{index}_mesh" + + if not cmds.objExists(head_grp): + cmds.group(empty=True, name=head_grp) + + if not cmds.objExists(head_mesh): + # 创建LOD网格体 + cmds.SGGetMeshes(cml=index) + head_lod = cmds.SGGetMeshes(lod=0) + create_lod = cmds.SGGetMeshes(lod=index) + + # 处理每个创建的LOD模型 + for c in create_lod: + create = cmds.SGGetMeshes(i=c) + + # 跳过索引大于等于50的模型 + if c >= 50: + cmds.delete(create) + continue + + exist = False + # 检查是否匹配头部LOD模型 + for h in head_lod: + if h >= 50: + continue + + if any(create.startswith(mesh) for mesh in meshes[:h+1]): + mesh = cmds.SGGetMeshes(m=h) + if cmds.objExists(mesh): + # 处理模型 + cmds.parent(create, head_grp) + + # 获取UV集 + suv = cmds.polyUVSet(mesh, query=True, currentUVSet=True) + tuv = cmds.polyUVSet(create, query=True, currentUVSet=True) + + # 检查UV集 + if len(suv) != 1: + raise RuntimeError(f"{mesh}: There are multiple uvSet!!!") + if len(tuv) != 1: + raise RuntimeError(f"{create}: There are multiple uvSet!!!") + + # 特殊处理eyeshell模型 + if create.startswith("eyeshell"): + _process_eyeshell_model(mesh, create, suv[0], tuv[0]) + else: + _process_normal_model(mesh, create, suv[0], tuv[0]) + + exist = True + + # 如果模型不存在则删除 + if not exist: + cmds.delete(create) + else: + cmds.warning(f"{head_mesh} Existed!!!") + + # 处理身体LOD(仅适用于LOD0-3) + if index < 4: + body_grp = f"body_lod{index}_grp" + body_mesh = f"body_lod{index}_mesh" + + if not cmds.objExists(body_grp): + cmds.group(empty=True, name=body_grp) + + if not cmds.objExists(body_mesh): + mesh = cmds.SGGetMeshes(m=50) + if cmds.objExists(mesh): + body_index = 50 + index + cmds.SGGetMeshes(cm=body_index) + cmds.parent(body_mesh, body_grp) + + # 获取UV集 + suv = cmds.polyUVSet(mesh, query=True, currentUVSet=True) + tuv = cmds.polyUVSet(body_mesh, query=True, currentUVSet=True) + + # 检查UV集 + if len(suv) != 1: + raise RuntimeError(f"{mesh}: There are multiple uvSet!!!") + if len(tuv) != 1: + raise RuntimeError(f"{body_mesh}: There are multiple uvSet!!!") + + # 处理身体模型 + _process_normal_model(mesh, body_mesh, suv[0], tuv[0]) + + # 设置显示属性 + body_mesh_shape = cmds.listRelatives(body_mesh, shapes=True) + cmds.setAttr(f"{body_mesh_shape[0]}.displayColors", 0) + else: + cmds.warning(f"{body_mesh} Existed!!!") + +def _process_eyeshell_model(source, target, source_uv, target_uv): + """ + 处理eyeshell模型的特殊UV和属性 + """ + # 复制和设置UV + cmds.polyUVSet(source, copy=True, uvSet=source_uv) + cmds.polyUVSet(source, currentUVSet=True, uvSet="uvSet1") + cmds.u3dLayout( + source, + resolution=256, + scale=1, + spacing=0.0029296875, + margin=0.0029296875, + box=[0, 1, 0.5, 1] + ) + + cmds.polyUVSet(target, copy=True, uvSet=target_uv) + cmds.polyUVSet(target, currentUVSet=True, uvSet="uvSet1") + cmds.u3dLayout( + target, + resolution=256, + scale=1, + spacing=0.0029296875, + margin=0.0029296875, + box=[0, 1, 0.5, 1] + ) + + # 传输属性 + _transfer_attributes(source, target, "uvSet1", "uvSet1") + + # 清理UV集 + cmds.polyUVSet(source, currentUVSet=True, uvSet=source_uv) + cmds.polyUVSet(source, delete=True, uvSet="uvSet1") + cmds.polyUVSet(target, currentUVSet=True, uvSet=target_uv) + cmds.polyUVSet(target, delete=True, uvSet="uvSet1") + +def _process_normal_model(source, target, source_uv, target_uv): + """ + 处理普通模型的属性传输 + """ + _transfer_attributes(source, target, source_uv, target_uv) + +def _transfer_attributes(source, target, source_uv, target_uv): + """ + 传输模型属性 + """ + # 传输位置、法线和UV + cmds.transferAttributes( + source, + target, + transferPositions=True, + transferNormals=True, + transferUVs=False, + transferColors=False, + sampleSpace=3, + sourceUvSpace=source_uv, + targetUvSpace=target_uv, + searchMethod=3, + flipUVs=False, + colorBorders=True + ) + + # 传输材质 + cmds.transferShadingSets( + source, + target, + sampleSpace=0, + searchMethod=3 + ) + + # 设置边缘和法线 + cmds.polySoftEdge(target, angle=180, constructionHistory=True) + cmds.polyNormalPerVertex(target, unFreezeNormal=True) + cmds.delete(target, constructionHistory=True) \ No newline at end of file diff --git a/scripts/Reference/SGCreateMenuItem.py b/scripts/Reference/SGCreateMenuItem.py new file mode 100644 index 0000000..408075d --- /dev/null +++ b/scripts/Reference/SGCreateMenuItem.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_create_menu_item(): + """ + 创建Maya菜单项 + - 添加SuperRigging菜单 + - 添加Super Rigging和Pose Wrangler子菜单项 + """ + icon = "unreal.png" + logo = "logo.png" + + # 创建主菜单 + cmds.menu( + "SG_MENU", + tearOff=True, + label="SuperRigging", + parent="MayaWindow" + ) + + # 添加Super Rigging菜单项 + cmds.menuItem( + "SG_Editor", + label="Super Rigging", + image=logo, + command="execute_super_rigging()" + ) + + # 添加Pose Wrangler菜单项 + cmds.menuItem( + "SG_PoseWrangler", + label="Pose Wrangler", + image=icon, + command="execute_pose_wrangler()" + ) + +def execute_super_rigging(): + """ + 执行Super Rigging功能 + - 加载必要的插件 + - 打开Super Rigging窗口 + """ + # 获取Maya版本 + version = cmds.about(version=True) + + # 需要加载的插件列表 + plugins = [ + f"SuperRiggingEditor{version}", + f"MayaUE4RBFPlugin{version}", + "embeddedRL4", + "MayaUERBFPlugin", + "mayaHIK" + ] + + # 加载所有必要的插件 + for plugin in plugins: + try: + if not cmds.pluginInfo(plugin, query=True, loaded=True): + cmds.loadPlugin(plugin, quiet=True) + cmds.pluginInfo(plugin, edit=True, autoload=True) + except: + pass + + # 打开Super Rigging窗口 + mel.eval("SuperRiggingWindow;") + +def execute_pose_wrangler(): + """ + 执行Pose Wrangler功能 + - 导入并初始化Pose Wrangler + """ + # 导入并创建Pose Wrangler实例 + python_cmd = """ +from epic_pose_wrangler import main +pose_wrangler_instance = main.PoseWrangler() +""" + mel.eval(f'python("{python_cmd}")') \ No newline at end of file diff --git a/scripts/Reference/SGCreateRL4Node.py b/scripts/Reference/SGCreateRL4Node.py new file mode 100644 index 0000000..efbae4d --- /dev/null +++ b/scripts/Reference/SGCreateRL4Node.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_create_rl4_node(dna, name): + """ + 创建RL4节点 + + 参数: + dna (str): DNA文件路径 + name (str): 节点名称 + """ + # 删除现有的RL4节点 + mel.eval('SGDeleteRL4Node') + + # 重置所有关节的颜色 + joints = cmds.SGGetJoints() + for joint in joints: + cmds.SGSetColor(joint, 0, 0, 0, 0, 0, 0) + + # 查找有效的LOD级别 + lod = None + for i in range(8): + joint_lod = cmds.SGGetJoints(lod=i, type="string") + exists = True + + # 检查所有关节是否存在 + for joint in joint_lod: + if not cmds.objExists(joint): + exists = False + break + + if exists: + lod = i + break + + # 修复LOD 0的关节 + mel.eval('SGRepairJointForLOD 0') + + # 设置命名空间和属性路径 + if cmds.namespace(exists="DHIhead"): + joint_name = "DHIhead:." + else: + joint_name = "." + + # 设置各种命名模式 + anim_multiplier_name = "FRM_WMmultipliers._" + blend_shape_name = "_blendShapes." + control_name = "." + + # 创建嵌入式RL4节点 + cmds.createEmbeddedNodeRL4( + dfp=dna, # DNA文件路径 + amn=anim_multiplier_name, # 动画乘数名称 + bsn=blend_shape_name, # 混合变形名称 + cn=control_name, # 控制器名称 + jn=joint_name, # 关节名称 + n=name # 节点名称 + ) + + # 删除指定LOD级别的关节 + mel.eval(f'SGDeleteJointForLOD {lod}') \ No newline at end of file diff --git a/scripts/Reference/SGCurrentProjectDNA.py b/scripts/Reference/SGCurrentProjectDNA.py new file mode 100644 index 0000000..f1ce65d --- /dev/null +++ b/scripts/Reference/SGCurrentProjectDNA.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/04 +""" + +import maya.cmds as cmds + +def sg_current_project_dna(): + """ + 更新当前项目的DNA文件路径 + - 检查场景中的RL4节点 + - 更新项目DNA路径 + """ + # 获取所有embeddedNodeRL4类型的节点 + rl4_nodes = cmds.ls(type="embeddedNodeRL4") + + # 如果没有找到RL4节点,报错并返回 + if not rl4_nodes: + cmds.error("No RL4 node object...") + return + + # 获取当前项目DNA路径 + pre_path = cmds.SGDescriptor(gpd=True) + + # 检查每个RL4节点 + for node in rl4_nodes: + # 获取节点的DNA文件路径 + rl4_path = cmds.getAttr(f"{node}.dnaFilePath") + + # 如果文件存在且与当前路径不同,则更新项目DNA路径 + if cmds.file(rl4_path, query=True, exists=True) and rl4_path != pre_path: + cmds.SGDescriptor(spd=rl4_path) \ No newline at end of file diff --git a/scripts/Reference/SGDefineJointForLOD.py b/scripts/Reference/SGDefineJointForLOD.py new file mode 100644 index 0000000..9d5b2f9 --- /dev/null +++ b/scripts/Reference/SGDefineJointForLOD.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/02/23 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_define_joint_for_lod(lod): + """ + 为指定LOD级别定义关节设置 + + 参数: + lod (int): LOD级别 + """ + # 重置所有控制器的位移属性 + controllers = [ + # 嘴部控制器 + "CTRL_C_mouth", "CTRL_C_tongue_wideNarrow", "CTRL_C_jaw", + "CTRL_C_jaw_fwdBack", "CTRL_L_mouth_pushPullU", "CTRL_R_mouth_pushPullU", + # ... (更多控制器) + ] + + # 重置所有控制器的X轴和Y轴位移 + for ctrl in controllers: + try: + cmds.setAttr(f"{ctrl}.translateX", 0) + cmds.setAttr(f"{ctrl}.translateY", 0) + except: + continue + + # 清除选择 + cmds.select(clear=True) + + # 处理RL4节点 + exists = False + rl4_nodes = cmds.ls(type="embeddedNodeRL4") + if rl4_nodes: + exists = True + cmds.delete(rl4_nodes) + + # 修复LOD 0的关节 + mel.eval('SGRepairJointForLOD 0') + + # 如果存在RL4节点,重新创建 + if exists: + dna = cmds.SGDescriptor(wd=True) + if cmds.file(dna, query=True, exists=True): + name = f"rl4Embedded_{cmds.SGDescriptor(n=True)}_rl" + mel.eval(f'SGCreateRL4Node "{dna}" "{name}"') + + # 处理蒙皮和关节 + mel.eval('SGFastUnbindSkinCluster') + mel.eval(f'SGRepairJointForLOD {lod}') + mel.eval(f'SGDeleteJointForLOD {lod}') + mel.eval('SGFastBindSkinCluster') + +def _get_control_list(): + """ + 获取需要重置的控制器列表 + + 返回: + list: 控制器名称列表 + """ + return [ + # 嘴部控制器 + "CTRL_C_mouth", "CTRL_C_tongue_wideNarrow", "CTRL_C_jaw", + "CTRL_L_mouth_pushPullU", "CTRL_R_mouth_pushPullU", + "CTRL_L_mouth_pushPullD", "CTRL_R_mouth_pushPullD", + + # 眼睛控制器 + "CTRL_L_eye", "CTRL_R_eye", "CTRL_C_eye", + "CTRL_L_eye_blink", "CTRL_R_eye_blink", + + # 眉毛控制器 + "CTRL_L_brow_raiseIn", "CTRL_R_brow_raiseIn", + "CTRL_L_brow_raiseOut", "CTRL_R_brow_raiseOut", + + # 下巴控制器 + "CTRL_C_jaw_fwdBack", "CTRL_C_jaw_openExtreme", + + # 舌头控制器 + "CTRL_C_tongue_move", "CTRL_C_tongue_bendTwist", + "CTRL_C_tongue_tipMove", "CTRL_C_tongue_inOut", + + # 其他面部控制器 + "CTRL_L_nose", "CTRL_R_nose", + "CTRL_L_mouth_corner", "CTRL_R_mouth_corner", + "CTRL_L_mouth_stretch", "CTRL_R_mouth_stretch", + + # 系统控制器 + "CTRL_lookAtSwitch", "CTRL_convergenceSwitch", + "CTRL_faceGUIfollowHead", "CTRL_eyesAimFollowHead" + ] \ No newline at end of file diff --git a/scripts/Reference/SGDelBlendShape.py b/scripts/Reference/SGDelBlendShape.py new file mode 100644 index 0000000..aab7e90 --- /dev/null +++ b/scripts/Reference/SGDelBlendShape.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/09/16 +""" + +import maya.cmds as cmds + +def sg_del_blend_shape(mesh_index, in_tgt_grp): + """ + 删除指定网格的混合变形目标组 + + 参数: + mesh_index (int): 网格索引 + in_tgt_grp (int): 目标组索引 + """ + # 获取混合变形节点名称 + bsn = f"{cmds.SGGetMeshes(i=mesh_index)}_blendShapes" + + # 检查节点是否存在 + if not cmds.objExists(bsn): + return + + # 检查目标组是否被锁定 + weight_attr = f"{bsn}.weight[{in_tgt_grp}]" + if cmds.getAttr(weight_attr, lock=True): + cmds.warning(f"There is no such attribute: {weight_attr}") + return + + # 移除组合变形(如果存在) + source = cmds.connectionInfo(f"{bsn}.w[{in_tgt_grp}]", sourceFromDestination=True) + if source: + source_parts = source.split('.') + if len(source_parts) == 2: + if cmds.nodeType(source_parts[0]) == "combinationShape": + cmds.delete(source_parts[0]) + + # 处理父目录关系 + parent_dir_attr = f"{bsn}.parentDirectory[{in_tgt_grp}]" + parent_directory = cmds.getAttr(parent_dir_attr) + + if parent_directory > 0: + child_indices_attr = f"{bsn}.targetDirectory[{parent_directory}].childIndices" + child_indices = cmds.getAttr(child_indices_attr) + + try: + location = child_indices.index(in_tgt_grp) + if location + 1 < len(child_indices): + next_target_attr = f"{bsn}.nextTarget[{in_tgt_grp}]" + cmds.setAttr(next_target_attr, child_indices[location + 1]) + except ValueError: + pass + + # 移除权重数组中的元素 + attrs_to_remove = [ + f"{bsn}.weight[{in_tgt_grp}]", + f"{bsn}.parentDirectory[{in_tgt_grp}]", + f"{bsn}.nextTarget[{in_tgt_grp}]", + f"{bsn}.targetVisibility[{in_tgt_grp}]", + f"{bsn}.targetParentVisibility[{in_tgt_grp}]" + ] + + for attr in attrs_to_remove: + cmds.removeMultiInstance(attr, b=True) + + # 处理输入目标 + input_target_attr = f"{bsn}.inputTarget" + input_target_indices = cmds.getAttr(input_target_attr, multiIndices=True) or [] + + # 检查是否需要重置 + needs_reset = False + for index in input_target_indices: + target_index_attr = f"{bsn}.inputTarget[{index}].sculptTargetIndex" + if cmds.getAttr(target_index_attr) == in_tgt_grp: + needs_reset = True + break + + # 如果需要重置,设置雕刻目标为-1 + if needs_reset: + cmds.sculptTarget(bsn, e=True, target=-1) + + # 处理每个输入目标 + for index in input_target_indices: + # 移除目标组内的每个输入目标项 + item_attr = f"{bsn}.inputTarget[{index}].inputTargetGroup[{in_tgt_grp}].inputTargetItem" + item_indices = cmds.getAttr(item_attr, multiIndices=True) or [] + + for item_index in item_indices: + cmds.blendShapeDeleteInBetweenTarget(bsn, in_tgt_grp, item_index) + + # 移除目标组内的每个目标权重 + weights_attr = f"{bsn}.inputTarget[{index}].inputTargetGroup[{in_tgt_grp}].targetWeights" + cmds.removeMultiInstance(weights_attr, b=True, allChildren=True) + + # 移除整个目标组 + group_attr = f"{bsn}.inputTarget[{index}].inputTargetGroup[{in_tgt_grp}]" + cmds.removeMultiInstance(group_attr, b=True) + + # 移除别名(如果存在) + weight_attr = f"{bsn}.weight[{in_tgt_grp}]" + alias = cmds.aliasAttr(weight_attr, query=True) + if alias: + cmds.aliasAttr(f"{bsn}.{alias}", remove=True) \ No newline at end of file diff --git a/scripts/Reference/SGDeleteJointForLOD.py b/scripts/Reference/SGDeleteJointForLOD.py new file mode 100644 index 0000000..e1789ed --- /dev/null +++ b/scripts/Reference/SGDeleteJointForLOD.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/02/23 +""" + +import maya.cmds as cmds + +def sg_delete_joint_for_lod(lod): + """ + 删除指定LOD级别不需要的关节 + + 参数: + lod (int): LOD级别 + """ + # 获取所有关节和指定LOD级别的关节 + joint_all = cmds.SGGetJoints(lod=0, type="string") + joint_lod = cmds.SGGetJoints(lod=lod, type="string") + + # 找出需要删除的关节(在所有关节中但不在指定LOD级别中的关节) + joint_del = list(set(joint_all) - set(joint_lod)) + + # 删除不需要的关节 + for joint in joint_del: + if cmds.objExists(joint): + cmds.delete(joint) \ No newline at end of file diff --git a/scripts/Reference/SGDeleteRL4Node.py b/scripts/Reference/SGDeleteRL4Node.py new file mode 100644 index 0000000..ace953b --- /dev/null +++ b/scripts/Reference/SGDeleteRL4Node.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_delete_rl4_node(): + """ + 删除场景中所有的RL4节点 + """ + # 查找并删除所有embeddedNodeRL4类型的节点 + rl4_nodes = cmds.ls(type="embeddedNodeRL4") + if rl4_nodes: + cmds.delete(rl4_nodes) \ No newline at end of file diff --git a/scripts/Reference/SGDemoHelp.py b/scripts/Reference/SGDemoHelp.py new file mode 100644 index 0000000..5393258 --- /dev/null +++ b/scripts/Reference/SGDemoHelp.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import os +import maya.cmds as cmds + +def sg_demo_help(help_name): + """ + 显示Super Animation演示帮助窗口 + + 参数: + help_name (str): 帮助图片的名称 + """ + # 如果窗口已存在则删除 + if cmds.window('demoHelpImageWin', exists=True): + cmds.deleteUI('demoHelpImageWin') + + # 创建新窗口 + cmds.window( + 'demoHelpImageWin', + title="Super Animation Demo Help" + ) + + # 获取环境变量中的路径 + path = os.environ.get('SG_PATH') + image_path = os.path.join(path, 'images', 'ARKit', f'{help_name}.png') + + # 创建布局 + cmds.formLayout('demoHelpFormLayout', width=610, height=800) + + # 创建滚动布局 + scroll_layout = cmds.scrollLayout('asDemoHelpImage') + form_layout_b = cmds.formLayout('asDemoHelpFormLayoutB') + + # 添加图片 + cmds.image(image=image_path) + + # 返回上级布局 + cmds.setParent('..') + cmds.setParent('..') + + # 设置窗口大小 + cmds.window('demoHelpImageWin', edit=True, widthHeight=(804, 580)) + + # 设置布局附着 + cmds.formLayout( + 'demoHelpFormLayout', + edit=True, + attachForm=[ + ('asDemoHelpImage', 'right', 0), + ('asDemoHelpImage', 'left', 0), + ('asDemoHelpImage', 'top', 0), + ('asDemoHelpImage', 'bottom', 0) + ] + ) + + # 显示窗口 + cmds.showWindow('demoHelpImageWin') \ No newline at end of file diff --git a/scripts/Reference/SGDuplicateTarget.py b/scripts/Reference/SGDuplicateTarget.py new file mode 100644 index 0000000..7ca2ab4 --- /dev/null +++ b/scripts/Reference/SGDuplicateTarget.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_duplicate_target(target, index): + """ + 复制目标并按网格排列 + + 参数: + target (str): 目标名称 + index (int): 目标索引,用于确定位置 + """ + # 获取头部模型 + head = cmds.SGGetMeshes(m=0) + + # 创建根目标组 + root_targets_grp = "root_targets_grp" + if not cmds.objExists(root_targets_grp): + cmds.group(empty=True, name=root_targets_grp) + + # 处理每个网格 + for i in range(9): + # 获取部署网格名称 + meshe_deploy = cmds.SGGetMeshes(i=i) + + # 获取混合变形节点名称 + blend_shape = f"{meshe_deploy}_blendShapes" + if not cmds.objExists(blend_shape): + continue + + # 获取权重属性 + attr_weight = f"{blend_shape}.weight" + + # 获取权重数量 + count = cmds.getAttr(attr_weight, size=True) + + # 获取当前混合变形列表 + current_blend_shape_list = cmds.listAttr(attr_weight, multi=True) + + # 检查目标是否在列表中 + if target not in current_blend_shape_list or count == 0: + continue + + # 获取网格和目标名称 + meshe = cmds.SGGetMeshes(m=i) + meshe_target = f"{meshe_deploy}_{target}" + + # 复制网格 + cmds.duplicate(meshe, returnRootsOnly=True, name=meshe_target) + + # 创建目标组 + target_grp = f"{target}_grp" + if not cmds.objExists(target_grp): + cmds.group(empty=True, name=target_grp) + cmds.parent(target_grp, root_targets_grp) + + # 将目标放入组中 + cmds.parent(meshe_target, target_grp) + + # 解锁变换属性 + for attr in ['tx', 'ty', 'tz', 'rx', 'ry', 'rz', 'sx', 'sy', 'sz']: + cmds.setAttr(f"{meshe_target}.{attr}", lock=False) + + # 获取当前轴向 + axis = cmds.upAxis(query=True, axis=True) + + # 计算边界框 + pos = cmds.polyEvaluate(head, boundingBox=True) + + # 计算距离 + distance_x = pos[1] - pos[0] + if axis == "y": + distance_u = pos[3] - pos[2] + else: # axis == "z" + distance_u = pos[5] - pos[4] + + # 计算行列位置 + row = index % 30 + column = index // 30 + + # 计算移动距离 + move_x = (row + 1) * distance_x + move_y = column * distance_u * -1 + + # 设置位置 + cmds.setAttr(f"{meshe_target}.tx", move_x) + if axis == "y": + cmds.setAttr(f"{meshe_target}.ty", move_y) + else: # axis == "z" + cmds.setAttr(f"{meshe_target}.tz", move_y) \ No newline at end of file diff --git a/scripts/Reference/SGEditBlendShape.py b/scripts/Reference/SGEditBlendShape.py new file mode 100644 index 0000000..07b050b --- /dev/null +++ b/scripts/Reference/SGEditBlendShape.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_edit_blend_shape(index, blend_shape): + """ + 编辑混合变形目标 + + 参数: + index (int): 目标索引 + blend_shape (str): 混合变形节点名称 + """ + try: + # 重置当前雕刻目标 + cmds.sculptTarget(blend_shape, edit=True, target=-1) + + # 设置新的雕刻目标 + cmds.sculptTarget(blend_shape, edit=True, target=index) + except: + pass \ No newline at end of file diff --git a/scripts/Reference/SGEnableJointOrient.py b/scripts/Reference/SGEnableJointOrient.py new file mode 100644 index 0000000..fcafe72 --- /dev/null +++ b/scripts/Reference/SGEnableJointOrient.py @@ -0,0 +1,679 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import os +import maya.cmds as cmds +import maya.mel as mel + +# 配置常量 +JOINT_ORIENT_CONFIG = { + 'DEFAULT_AIM_AXIS': [1, 0, 0], + 'DEFAULT_UP_AXIS': [0, 1, 0], + 'DEFAULT_UP_VECTOR': [0, 1, 0], + 'UI_WIDTH': 310, + 'UI_HEIGHT': 256 +} + +def sg_enable_joint_orient(): + """启用关节方向编辑功能""" + try: + sg_comet_joint_orient() + except Exception as e: + cmds.warning(f"Error in joint orient editor: {str(e)}") + +def sg_comet_joint_orient(): + """创建关节方向编辑窗口""" + # 检查SuperRiggingEditor窗口是否存在 + if not cmds.window('SuperRiggingEditor', exists=True): + return + + # 获取语言设置 + labels = get_ui_labels() + + # 如果窗口已存在则删除 + if cmds.window('cometJointOrientWin', exists=True): + cmds.deleteUI('cometJointOrientWin') + + # 创建窗口 + cmds.window( + 'cometJointOrientWin', + title="JointOrient", + width=JOINT_ORIENT_CONFIG['UI_WIDTH'], + height=JOINT_ORIENT_CONFIG['UI_HEIGHT'], + sizeable=True, + toolbox=True, + parent='SuperRiggingEditor' + ) + + # 创建主布局 + main_form = cmds.formLayout('mainForm') + + # 创建控件 + sep0 = cmds.separator(style="in", height=3) + + btn_show = cmds.button( + label=labels['ShowAxis'], + align="center", + command=lambda x: show_axis(1), + annotation="Show Local Axis" + ) + + btn_hide = cmds.button( + label=labels['HideAxis'], + align="center", + command=lambda x: show_axis(0), + annotation="Hide Local Axis" + ) + + rbg_aim = cmds.radioButtonGrp( + label=labels['AimAxis'], + numberOfRadioButtons=3, + labelArray3=['X', 'Y', 'Z'], + select=2, + columnWidth4=[80, 40, 40, 40] + ) + + rbg_up = cmds.radioButtonGrp( + label=labels['UpAxis'], + numberOfRadioButtons=3, + labelArray3=['X', 'Y', 'Z'], + select=1, + columnWidth4=[80, 40, 40, 40] + ) + + cb_rev_aim = cmds.checkBox(label=labels['Reverse'], value=0) + cb_rev_up = cmds.checkBox(label=labels['Reverse'], value=0) + + sep1 = cmds.separator(style="in", height=6) + + ffg_up_dir = cmds.floatFieldGrp( + numberOfFields=3, + label=labels['WorldUpDir'], + value1=1.0, + value2=0.0, + value3=0.0, + columnWidth4=[80, 50, 50, 50] + ) + + btn_x = cmds.button( + label="X", + width=20, + command=lambda x: cmds.floatFieldGrp(ffg_up_dir, edit=True, value1=1.0, value2=0.0, value3=0.0), + annotation="Auto Set UpDir to X-Axis" + ) + + btn_y = cmds.button( + label="Y", + width=20, + command=lambda x: cmds.floatFieldGrp(ffg_up_dir, edit=True, value1=0.0, value2=1.0, value3=0.0), + annotation="Auto Set UpDir to Y-Axis" + ) + + btn_z = cmds.button( + label="Z", + width=20, + command=lambda x: cmds.floatFieldGrp(ffg_up_dir, edit=True, value1=0.0, value2=0.0, value3=1.0), + annotation="Auto Set UpDir to Z-Axis" + ) + + cb_auto_dir = cmds.checkBox(label=labels['AutoGuessUpDirection'], value=0) + + btn_orient = cmds.button( + label=labels['OrientJoints'], + align="center", + command=lambda x: sg_orient_ui(), + annotation="Orient selected joints based on settings above." + ) + + sep_big = cmds.separator(style="in", height=6) + + ffg_tweak = cmds.floatFieldGrp( + numberOfFields=3, + label=labels['Tweak'], + value1=0.0, + value2=0.0, + value3=0.0, + columnWidth4=[80, 50, 50, 50] + ) + + btn_zero = cmds.button( + label=labels['Zero'], + width=58, + command=lambda x: cmds.floatFieldGrp(ffg_tweak, edit=True, value1=0.0, value2=0.0, value3=0.0), + annotation="Zero's tweak values." + ) + + popup = cmds.popupMenu(parent=ffg_tweak) + axis_map = {'X': 1, 'Y': 2, 'Z': 3} + for axis in ['X', 'Y', 'Z']: + for value in [1, 5, 10]: + cmds.menuItem( + label=f"{axis}={value}", + command=lambda axis=axis, value=value: cmds.floatFieldGrp(ffg_tweak, edit=True, value1=float(value) if axis == 'X' else None, value2=float(value) if axis == 'Y' else None, value3=float(value) if axis == 'Z' else None) + ) + if axis != 'Z': + cmds.menuItem(divider=True) + + btn_tweak_p = cmds.button( + label=labels['ManualAddRotTweak'], + align="center", + command=lambda x: sg_tweak_ui(1.0), + annotation="Manually rotates selected joints axis positive." + ) + + btn_tweak_n = cmds.button( + label=labels['ManualRedRotTweak'], + align="center", + command=lambda x: sg_tweak_ui(-1.0), + annotation="Manually rotates selected joints axis negative." + ) + + sep2 = cmds.separator(style="in", height=6) + + btn_main_amend = cmds.button( + label=labels['MainJointAmendAxis'], + align="center", + command=lambda x: sg_main_amend_axis(), + annotation="Main Amend Axis" + ) + + btn_skin_amend = cmds.button( + label=labels['SkinJointAmendAxis'], + align="center", + command=lambda x: sg_skin_amend_axis(), + annotation="Other Amend Axis" + ) + + sep3 = cmds.separator(style="in", height=6) + + # 设置布局 + cmds.formLayout( + main_form, + edit=True, + attachForm=[ + (sep0, 'top', 0), + (sep0, 'left', 0), + (sep0, 'right', 0), + (btn_show, 'left', 5), + (btn_hide, 'right', 5), + (rbg_aim, 'left', 0), + (rbg_up, 'left', 0), + (sep1, 'left', 0), + (sep1, 'right', 0), + (ffg_up_dir, 'left', 0), + (btn_orient, 'left', 5), + (btn_orient, 'right', 5), + (sep_big, 'left', 0), + (sep_big, 'right', 0), + (ffg_tweak, 'left', 0), + (btn_zero, 'right', 5), + (btn_tweak_p, 'left', 5), + (btn_tweak_p, 'right', 5), + (btn_tweak_n, 'left', 5), + (btn_tweak_n, 'right', 5), + (sep2, 'left', 0), + (sep2, 'right', 0), + (btn_main_amend, 'left', 5), + (btn_main_amend, 'right', 5), + (btn_skin_amend, 'left', 5), + (btn_skin_amend, 'right', 5), + (sep3, 'left', 0), + (sep3, 'right', 0) + ], + attachControl=[ + (btn_show, 'top', 3, sep0), + (btn_hide, 'top', 3, sep0), + (rbg_aim, 'top', 0, btn_show), + (cb_rev_aim, 'top', 0, btn_show), + (cb_rev_aim, 'left', 0, rbg_aim), + (rbg_up, 'top', 0, rbg_aim), + (cb_rev_up, 'top', 0, rbg_aim), + (cb_rev_up, 'left', 0, rbg_up), + (sep1, 'top', 0, rbg_up), + (ffg_up_dir, 'top', 0, sep1), + (btn_x, 'top', 0, sep1), + (btn_x, 'left', 2, ffg_up_dir), + (btn_y, 'top', 0, sep1), + (btn_y, 'left', 2, btn_x), + (btn_z, 'top', 0, sep1), + (btn_z, 'left', 2, btn_y), + (cb_auto_dir, 'top', 0, ffg_up_dir), + (btn_orient, 'top', 5, cb_auto_dir), + (sep_big, 'top', 5, btn_orient), + (ffg_tweak, 'top', 0, sep_big), + (btn_zero, 'top', 0, sep_big), + (btn_zero, 'left', 2, ffg_tweak), + (btn_tweak_p, 'top', 5, ffg_tweak), + (btn_tweak_n, 'top', 5, btn_tweak_p), + (sep2, 'top', 5, btn_tweak_n), + (btn_main_amend, 'top', 5, sep2), + (btn_skin_amend, 'top', 5, btn_main_amend), + (sep3, 'top', 5, btn_skin_amend) + ] + ) + + # 显示窗口 + cmds.showWindow('cometJointOrientWin') + +def get_ui_labels(): + """ + 根据语言设置获取UI标签 + """ + is_chinese = cmds.SGDescriptor(l=True) == "ZH" + + if is_chinese: + return { + 'ShowAxis': '显示坐标轴', + 'HideAxis': '隐藏坐标轴', + 'AimAxis': '目标轴:', + 'UpAxis': '向上轴:', + 'Reverse': '反向', + 'WorldUpDir': '世界向上方向:', + 'AutoGuessUpDirection': '自动识别向上方向', + 'OrientJoints': '确定关节方向', + 'Tweak': '调整:', + 'Zero': '归零', + 'ManualAddRotTweak': '手动旋转调整+', + 'ManualRedRotTweak': '手动旋转调整-', + 'MainJointAmendAxis': '主要关节轴向修正', + 'SkinJointAmendAxis': '蒙皮关节轴向修正' + } + else: + return { + 'ShowAxis': 'Show Axis', + 'HideAxis': 'Hide Axis', + 'AimAxis': 'Aim Axis:', + 'UpAxis': 'Up Axis:', + 'Reverse': 'Reverse', + 'WorldUpDir': 'World Up Dir:', + 'AutoGuessUpDirection': 'Auto-Guess Up Direction', + 'OrientJoints': 'Orient Joints', + 'Tweak': 'Tweak:', + 'Zero': 'Zero', + 'ManualAddRotTweak': 'Manual + Rot Tweak', + 'ManualRedRotTweak': 'Manual - Rot Tweak', + 'MainJointAmendAxis': 'Main Joint Amend Axis', + 'SkinJointAmendAxis': 'Skin Joint Amend Axis' + } + +# 其他辅助函数... +def sg_main_amend_axis(): + """ + 主要关节轴向修正 + """ + # 获取所有关节 + joints = sg_body_joints() + + # 创建进度条 + cmds.SGProgressBar(sp=True) + cmds.SGProgressBar(max=len(joints)) + cmds.SGProgressBar(t="Amend Joint Axis...") + + # 处理每个关节 + for joint in joints: + cmds.SGProgressBar(apr=1) + + # 获取关节类型 + joint_type = cmds.SGReadJson(d=joint, k="type", t="string") + + # 获取关节名称 + joint_drv = f"{joint}_drv" + joint_head = f"{joint}_head" + joint_body = f"{joint}_body" + + # 处理驱动关节 + if cmds.objExists(joint_drv): + cmds.setAttr(f"{joint_drv}.jointOrient", 0, 0, 0) + rot = cmds.getAttr(f"{joint_drv}.rotate")[0] + cmds.setAttr(f"{joint_drv}.jointOrient", *rot) + + # 处理身体关节 + if cmds.objExists(joint_body): + cmds.setAttr(f"{joint_body}.jointOrient", 0, 0, 0) + rot = cmds.getAttr(f"{joint_body}.rotate")[0] + cmds.setAttr(f"{joint_body}.jointOrient", *rot) + + # 处理头部关节 + if cmds.objExists(joint_head): + cmds.setAttr(f"{joint_head}.jointOrient", 0, 0, 0) + rot = cmds.getAttr(f"{joint_head}.rotate")[0] + cmds.setAttr(f"{joint_head}.jointOrient", *rot) + + # 处理驱动关节的方向 + if cmds.objExists(joint_drv): + drv_rot = cmds.getAttr(f"{joint_drv}.jo")[0] + if cmds.objExists(joint_head) and joint_type[0] not in ["half", "muscle"]: + cmds.setAttr(f"{joint_head}.jo", *drv_rot) + if cmds.objExists(joint_body) and joint_type[0] not in ["half", "muscle"]: + cmds.setAttr(f"{joint_body}.jo", *drv_rot) + + # 结束进度条 + cmds.SGProgressBar(ep=True) + +def sg_skin_amend_axis(): + """ + 蒙皮关节轴向修正 + """ + # 获取所有关节 + joints = sg_body_joints() + + # 创建进度条 + cmds.SGProgressBar(sp=True) + cmds.SGProgressBar(max=len(joints)) + cmds.SGProgressBar(t="Amend Skin Joint Axis...") + + # 处理每个关节 + for joint in joints: + cmds.SGProgressBar(apr=1) + + # 获取关节名称 + joint_drv = f"{joint}_drv" + joint_head = f"{joint}_head" + joint_body = f"{joint}_body" + + # 处理驱动关节 + if cmds.objExists(joint_drv): + cmds.joint(joint_drv, edit=True, orientJoint="none") + + # 处理身体关节 + if cmds.objExists(joint_body): + cmds.joint(joint_body, edit=True, orientJoint="none") + + # 处理头部关节 + if cmds.objExists(joint_head): + cmds.joint(joint_head, edit=True, orientJoint="none") + + # 结束进度条 + cmds.SGProgressBar(ep=True) + +def sg_other_amend_axis(joints): + """ + 其他关节轴向修正 + + 参数: + joints (list): 关节索引列表 + """ + body_joints = sg_body_joints() + for joint_index in joints: + drv = f"{body_joints[joint_index]}_drv" + if cmds.objExists(drv): + cmds.joint(drv, edit=True, orientJoint="none") + +def sg_half_amend_axis(joints): + """ + 半身关节轴向修正 + + 参数: + joints (list): 关节索引列表 + """ + body_joints = sg_body_joints() + for joint_index in joints: + drv = f"{body_joints[joint_index]}_drv" + root_joint = drv.replace("_half", "") + if cmds.objExists(drv): + pos = cmds.getAttr(f"{root_joint}.jointOrient")[0] + cmds.setAttr(f"{drv}.jointOrient", *pos) + +def sg_hand_amend_axis(joints, rot): + """ + 手部关节轴向修正 + + 参数: + joints (list): 关节索引列表 + rot (list): 旋转值列表 + """ + body_joints = sg_body_joints() + joint_list = [] + for joint_index in joints: + drv = f"{body_joints[joint_index]}_drv" + if cmds.objExists(drv): + cmds.joint(drv, edit=True, orientJoint="none") + joint_list.append(drv) + sg_tweak(joint_list, rot) + +def sg_index_to_name(joints): + """ + 将关节索引转换为名称 + + 参数: + joints (list): 关节索引列表 + 返回: + list: 关节名称列表 + """ + body_joints = sg_body_joints() + joint_list = [] + for joint_index in joints: + drv = f"{body_joints[joint_index]}_drv" + if cmds.objExists(drv): + joint_list.append(drv) + return joint_list + +def sg_corrective_root(joints): + """ + 修正根关节 + + 参数: + joints (list): 关节索引列表 + """ + body_joints = sg_body_joints() + for joint_index in joints: + drv = f"{body_joints[joint_index]}_drv" + parent = f"{body_joints[joint_index]}_drv_parentConstraint1" + orient = f"{body_joints[joint_index]}_orientConstraint1_drv" + + if cmds.objExists(drv): + cmds.setAttr(f"{drv}.jointOrient", 0, 0, 0) + if cmds.objExists(parent): + cmds.setAttr(f"{parent}.cr", 0, 0, 0) + if cmds.objExists(orient): + cmds.setAttr(f"{orient}.cr", 0, 0, 0) + +def sg_amend_orient(joints, aim_axis, up_axis, up_dir): + """ + 修正关节方向 + + 参数: + joints (list): 关节索引列表 + aim_axis (list): 目标轴向 + up_axis (list): 向上轴向 + up_dir (list): 向上方向 + """ + body_joints = sg_body_joints() + joint_drv_list = [] + for joint_index in joints: + drv = f"{body_joints[joint_index]}_drv" + if cmds.objExists(drv): + joint_drv_list.append(drv) + + do_auto = 0 + sg_orient(joint_drv_list, aim_axis, up_axis, up_dir, do_auto) + +def sg_tweak(joints, rot): + """ + 调整关节旋转 + + 参数: + joints (list): 关节列表 + rot (list): 旋转值列表 + """ + for joint in joints: + current_rot = cmds.getAttr(f"{joint}.jointOrient")[0] + new_rot = [a + b for a, b in zip(current_rot, rot)] + cmds.setAttr(f"{joint}.jointOrient", *new_rot) + +def sg_orient(joints, aim_axis, up_axis, up_dir, do_auto): + """ + 设置关节方向 + + 参数: + joints (list): 关节列表 + aim_axis (list): 目标轴向 + up_axis (list): 向上轴向 + up_dir (list): 向上方向 + do_auto (bool): 是否自动处理 + """ + # 这个函数的具体实现需要根据原MEL脚本中的SGOrient函数来完成 + pass + +def sg_body_joints(): + """ + 获取身体关节列表 + """ + return cmds.SGGetJoints(bj=True) + +def sg_body_joints_locator(): + """ + 创建身体关节定位器 + """ + # 获取JSON文件路径 + json_path = os.path.join(os.environ.get('SG_PATH'), "files/data/BodyJoints.json") + + # 读取对象数据 + objects = cmds.SGReadJson(f=json_path, t="object") + if not objects: + return + + # 创建进度条 + cmds.SGProgressBar(sp=True) + + # 创建临时定位器 + locator = "MetaHumanLocatorTemp" + if not cmds.objExists(locator): + cmds.spaceLocator(name=locator) + + # 设置进度条最大值 + cmds.SGProgressBar(max=len(objects)) + cmds.SGProgressBar(t="Set Body Joint Translation...") + + # 获取中性关节位置 + positions = cmds.SGGetNeutralJointTranslations(b=True) + + # 检查数据数量 + if len(objects) != len(positions) // 3: + cmds.error("count error......") + return + + # 处理每个关节 + for i, obj in enumerate(objects): + cmds.SGProgressBar(apr=1) + + # 获取关节和类型信息 + joints = cmds.SGReadJson(d=obj, k="joint", t="string") + joint_type = cmds.SGReadJson(d=obj, k="type", t="string") + joint_drv = f"{joints[0]}_drv" + + # 检查是否需要定位 + locate = cmds.SGReadJson(d=obj, k="locate", t="bool") + if locate[0] and cmds.objExists(joint_drv): + # 设置位置 + x = 0.0 if joint_type[0] == "middle" else positions[i*3] + y = positions[i*3+1] + z = positions[i*3+2] + + cmds.setAttr(f"{locator}.t", x, y, z) + cmds.delete(cmds.pointConstraint(locator, joint_drv, weight=1)) + + # 重置进度条 + cmds.SGProgressBar(pr=0) + cmds.SGProgressBar(ep=True) + + # 删除临时定位器 + cmds.delete(locator) + cmds.refresh() + +def check_orient_constraint_connection(joint): + """ + 检查关节的方向约束连接 + + 参数: + joint (str): 关节名称 + 返回: + str: 方向约束节点名称,如果没有则返回空字符串 + """ + if not cmds.objExists(joint): + return "" + + # 获取所有下游连接的节点 + connected_nodes = cmds.listConnections(joint, source=False, destination=True) + + # 检查每个连接的节点 + for node in connected_nodes or []: + if cmds.nodeType(node) == "orientConstraint": + return node + + return "" + +def sg_orient_ui(): + """处理关节方向设置UI的回调""" + # 获取选中的关节 + joints = cmds.ls(selection=True, type='joint') + if not joints: + cmds.warning("Please select joints to orient.") + return + + # 获取UI设置 + aim_axis = get_axis_vector('rbg_aim', 'cb_rev_aim') + up_axis = get_axis_vector('rbg_up', 'cb_rev_up') + up_dir = [ + cmds.floatFieldGrp('ffg_up_dir', query=True, value1=True), + cmds.floatFieldGrp('ffg_up_dir', query=True, value2=True), + cmds.floatFieldGrp('ffg_up_dir', query=True, value3=True) + ] + do_auto = cmds.checkBox('cb_auto_dir', query=True, value=True) + + # 执行方向设置 + sg_orient(joints, aim_axis, up_axis, up_dir, do_auto) + +def sg_tweak_ui(value): + """ + 处理关节旋转调整UI的回调 + + 参数: + value (float): 调整值 + """ + # 获取选中的关节 + joints = cmds.ls(selection=True, type='joint') + if not joints: + cmds.warning("Please select joints to tweak.") + return + + # 获取调整值 + rot = [ + cmds.floatFieldGrp('ffg_tweak', query=True, value1=True) * value, + cmds.floatFieldGrp('ffg_tweak', query=True, value2=True) * value, + cmds.floatFieldGrp('ffg_tweak', query=True, value3=True) * value + ] + + # 执行调整 + sg_tweak(joints, rot) + +def show_axis(value): + """ + 显示/隐藏轴向 + + 参数: + value (int): 0为隐藏,1为显示 + """ + cmds.jointDisplayScale(value) + +def get_axis_vector(radio_grp, check_box): + """ + 获取轴向向量 + + 参数: + radio_grp (str): 单选按钮组名称 + check_box (str): 复选框名称 + 返回: + list: 轴向向量 + """ + axis_map = {1: [1,0,0], 2: [0,1,0], 3: [0,0,1]} + selected = cmds.radioButtonGrp(radio_grp, query=True, select=True) + reverse = cmds.checkBox(check_box, query=True, value=True) + vector = axis_map[selected] + return [-v if reverse else v for v in vector] \ No newline at end of file diff --git a/scripts/Reference/SGExportFBXWindow.py b/scripts/Reference/SGExportFBXWindow.py new file mode 100644 index 0000000..675d944 --- /dev/null +++ b/scripts/Reference/SGExportFBXWindow.py @@ -0,0 +1,329 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/01 +""" + +import os +import maya.cmds as cmds +import maya.mel as mel + +def sg_export_fbx_window(): + """ + 创建FBX导出窗口 + """ + # 检查SuperRiggingEditor窗口是否存在 + if not cmds.window('SuperRiggingEditor', exists=True): + return + + # 如果导出窗口已存在则删除 + if cmds.window('exportFBXWin', exists=True): + cmds.deleteUI('exportFBXWin') + + # 获取UI标签 + labels = get_ui_labels() + + # 创建窗口 + cmds.window( + 'exportFBXWin', + title="ExportFBX", + width=310, + height=100, + sizeable=True, + toolbox=True, + parent='SuperRiggingEditor' + ) + + # 创建主布局 + cmds.columnLayout( + adjustableColumn=True, + columnAttach=['both', 5], + rowSpacing=2, + columnWidth=150 + ) + + # 创建分隔符 + cmds.separator(height=10, style="in") + + # 创建LOD复选框组 + cmds.checkBoxGrp( + 'export_FBX_Check_1', + numberOfCheckBoxes=4, + labelArray4=['LOD0', 'LOD1', 'LOD2', 'LOD3'], + columnWidth4=[55, 55, 55, 55], + value1=1 + ) + + cmds.checkBoxGrp( + 'export_FBX_Check_2', + numberOfCheckBoxes=4, + labelArray4=['LOD4', 'LOD5', 'LOD6', 'LOD7'], + columnWidth4=[55, 55, 55, 55] + ) + + # 创建分隔符 + cmds.separator(height=10, style="in") + + # 创建头部导出控件 + cmds.rowLayout(numberOfColumns=2, columnWidth2=[45, 20], adjustableColumn=2) + cmds.checkBox( + 'check_Select_All_1', + label=labels['All'], + value=0, + changeCommand=sg_export_head_check_command + ) + cmds.button( + label=labels['ExportHeadFBX'], + align="center", + command=lambda x: sg_export_head_command() + ) + cmds.setParent('..') + + cmds.button( + label=labels['ExportSelectHeadFBX'], + align="center", + command=lambda x: sg_export_select_head_command() + ) + + # 创建分隔符 + cmds.separator(height=10, style="in") + + # 创建身体导出控件 + cmds.rowLayout(numberOfColumns=2, columnWidth2=[45, 20], adjustableColumn=2) + cmds.checkBox( + 'check_Select_All_2', + label=labels['All'], + value=0, + changeCommand=sg_export_body_check_command + ) + cmds.button( + label=labels['ExportBodyFBX'], + align="center", + command=lambda x: sg_export_body_command() + ) + cmds.setParent('..') + + cmds.button( + label=labels['ExportSelectBodyFBX'], + align="center", + command=lambda x: sg_export_select_body_command() + ) + + # 创建分隔符 + cmds.separator(height=10, style="in") + + # 显示窗口 + cmds.showWindow('exportFBXWin') + +def get_ui_labels(): + """获取UI标签""" + is_chinese = cmds.SGDescriptor(l=True) == "ZH" + + if is_chinese: + return { + 'All': '全选', + 'ExportHeadFBX': '导出头部LOD到FBX', + 'ExportSelectHeadFBX': '导出选择的头部到FBX', + 'ExportBodyFBX': '导出身体LOD到FBX', + 'ExportSelectBodyFBX': '导出选择的身体到FBX' + } + else: + return { + 'All': 'All', + 'ExportHeadFBX': 'Export Head LOD To FBX', + 'ExportSelectHeadFBX': 'Export Select Head To FBX', + 'ExportBodyFBX': 'Export Body LOD To FBX', + 'ExportSelectBodyFBX': 'Export Select Body To FBX' + } + +def sg_export_fbx(file_path): + """ + 导出FBX文件 + + 参数: + file_path (str): FBX文件保存路径 + """ + # 重置FBX导出设置 + mel.eval('FBXResetExport') + + # 设置FBX导出选项 + mel.eval('FBXExportBakeComplexAnimation -v true') + mel.eval('FBXExportBakeComplexStart -v 1') + mel.eval('FBXExportBakeComplexStep -v 1') + mel.eval('FBXExportBakeComplexEnd -v 1') + mel.eval('FBXExportConstraints -v true') + mel.eval('FBXExportSkeletonDefinitions -v true') + mel.eval('FBXExportInputConnections -v true') + mel.eval('FBXExportSmoothingGroups -v true') + mel.eval('FBXExportSkins -v true') + mel.eval('FBXExportShapes -v true') + mel.eval('FBXExportCameras -v false') + mel.eval('FBXExportLights -v false') + mel.eval('FBXExportUpAxis "y"') + + # 执行导出 + mel.eval(f'FBXExport -f "{file_path}" -s') + print(f"{file_path}: Export completed...") + +def sg_export_head_check_command(): + """处理头部全选复选框""" + value = cmds.checkBox('check_Select_All_1', query=True, value=True) + for i in range(1, 5): + cmds.checkBoxGrp('export_FBX_Check_1', edit=True, value=[value]*4) + cmds.checkBoxGrp('export_FBX_Check_2', edit=True, value=[value]*4) + +def sg_export_body_check_command(): + """处理身体全选复选框""" + value = cmds.checkBox('check_Select_All_2', query=True, value=True) + cmds.checkBoxGrp('export_FBX_Check_1', edit=True, value=[value]*4) + +def sg_export_head_command(): + """导出头部LOD的FBX""" + # 检查并加载FBX插件 + if not cmds.pluginInfo('fbxmaya', query=True, loaded=True): + cmds.loadPlugin('fbxmaya') + + # 检查根关节 + if not cmds.objExists('DHIhead:root'): + cmds.error('Missing "DHIhead:root" joint...') + return + + # 重命名混合变形 + mel.eval('SGRenameBlendShapes') + + # 获取LOD选择状态 + lods = [] + for i in range(4): + lods.append(cmds.checkBoxGrp('export_FBX_Check_1', query=True, value1=True)) + for i in range(4): + lods.append(cmds.checkBoxGrp('export_FBX_Check_2', query=True, value1=True)) + + # 导出每个选中的LOD + for i, is_selected in enumerate(lods): + if is_selected: + export_grp = f"head_lod{i}_grp" + if cmds.ls(export_grp, dag=True, geometry=True): + path = cmds.SGDescriptor(p=True) + name = cmds.SGDescriptor(n=True) + file_path = f"{path}/{name}_lod{i}_head.fbx" + + cmds.select(clear=True) + cmds.select(export_grp, replace=True) + cmds.select('DHIhead:root', add=True) + sg_export_fbx(file_path) + else: + cmds.error(f'"{export_grp}" No geometry found within the group...') + + # 重置混合变形 + mel.eval('SGResetBlendShapes') + + # 打开输出文件夹 + folder_path = cmds.SGDescriptor(p=True).replace('/', '\\') + os.system(f'explorer "{folder_path}"') + +def sg_export_select_head_command(): + """导出选中的头部到FBX""" + # 检查并加载FBX插件 + if not cmds.pluginInfo('fbxmaya', query=True, loaded=True): + cmds.loadPlugin('fbxmaya') + + # 检查根关节 + if not cmds.objExists('DHIhead:root'): + cmds.error('Missing "DHIhead:root" joint...') + return + + # 检查选择 + selection = cmds.ls(selection=True) + if not selection: + cmds.error('Selection is empty...') + return + + # 重命名混合变形 + mel.eval('SGRenameBlendShapes') + + # 导出选中物体 + cmds.select(clear=True) + cmds.select(selection, replace=True) + cmds.select('DHIhead:root', add=True) + + path = cmds.SGDescriptor(p=True) + name = cmds.SGDescriptor(n=True) + file_path = f"{path}/{name}_lod_head.fbx" + sg_export_fbx(file_path) + + # 重置混合变形 + mel.eval('SGResetBlendShapes') + + # 打开输出文件夹 + folder_path = cmds.SGDescriptor(p=True).replace('/', '\\') + os.system(f'explorer "{folder_path}"') + +def sg_export_body_command(): + """导出身体LOD的FBX""" + # 检查并加载FBX插件 + if not cmds.pluginInfo('fbxmaya', query=True, loaded=True): + cmds.loadPlugin('fbxmaya') + + # 检查根关节 + if not cmds.objExists('DHIbody:root'): + cmds.error('Missing "DHIbody:root" joint...') + return + + # 获取LOD选择状态 + lods = [] + for i in range(4): + lods.append(cmds.checkBoxGrp('export_FBX_Check_1', query=True, value1=True)) + + # 导出每个选中的LOD + for i, is_selected in enumerate(lods): + if is_selected: + export_grp = f"body_lod{i}_grp" + if cmds.ls(export_grp, dag=True, geometry=True): + path = cmds.SGDescriptor(p=True) + name = cmds.SGDescriptor(n=True) + file_path = f"{path}/{name}_lod{i}_body.fbx" + + cmds.select(clear=True) + cmds.select(export_grp, replace=True) + cmds.select('DHIbody:root', add=True) + sg_export_fbx(file_path) + else: + cmds.error(f'"{export_grp}" No geometry found within the group...') + + # 打开输出文件夹 + folder_path = cmds.SGDescriptor(p=True).replace('/', '\\') + os.system(f'explorer "{folder_path}"') + +def sg_export_select_body_command(): + """导出选中的身体到FBX""" + # 检查并加载FBX插件 + if not cmds.pluginInfo('fbxmaya', query=True, loaded=True): + cmds.loadPlugin('fbxmaya') + + # 检查根关节 + if not cmds.objExists('DHIbody:root'): + cmds.error('Missing "DHIbody:root" joint...') + return + + # 检查选择 + selection = cmds.ls(selection=True) + if not selection: + cmds.error('Selection is empty...') + return + + # 导出选中物体 + cmds.select(clear=True) + cmds.select(selection, replace=True) + cmds.select('DHIbody:root', add=True) + + path = cmds.SGDescriptor(p=True) + name = cmds.SGDescriptor(n=True) + file_path = f"{path}/{name}_lod_body.fbx" + sg_export_fbx(file_path) + + # 打开输出文件夹 + folder_path = cmds.SGDescriptor(p=True).replace('/', '\\') + os.system(f'explorer "{folder_path}"') \ No newline at end of file diff --git a/scripts/Reference/SGExportSkinCluster.py b/scripts/Reference/SGExportSkinCluster.py new file mode 100644 index 0000000..638f9fb --- /dev/null +++ b/scripts/Reference/SGExportSkinCluster.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_export_skin_cluster(): + """ + 导出选中物体的蒙皮权重 + - 打开文件对话框选择保存目录 + - 为每个选中的物体导出蒙皮权重文件 + """ + # 打开文件对话框选择保存目录 + directory = cmds.fileDialog2(fileMode=2, dialogStyle=1) + + # 检查是否选择了目录 + if directory and cmds.file(directory[0], query=True, exists=True): + # 获取选中的物体 + selection = cmds.ls(selection=True) + suffix = ".skin" + + # 处理每个选中的物体 + for obj in selection: + # 查找关联的蒙皮变形器 + skin_cluster = cmds.findRelatedSkinCluster(obj) + + # 如果存在蒙皮变形器,则导出权重 + if cmds.objExists(skin_cluster): + # 构建保存路径 + save_path = f"{directory[0]}/{obj}{suffix}" + # 导出权重 + cmds.SGSkinCluster(ef=obj, export_file=save_path) \ No newline at end of file diff --git a/scripts/Reference/SGFastBindSkinCluster.py b/scripts/Reference/SGFastBindSkinCluster.py new file mode 100644 index 0000000..2271fc8 --- /dev/null +++ b/scripts/Reference/SGFastBindSkinCluster.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/26 +""" + +import os +import maya.cmds as cmds + +def sg_fast_bind_skin_cluster(): + """ + 快速绑定蒙皮权重 + - 遍历所有网格 + - 查找对应的蒙皮权重文件 + - 应用蒙皮权重 + """ + # 遍历所有网格(0-53) + for i in range(54): + # 获取网格名称 + mesh = cmds.SGGetMeshes(m=i) + + # 检查网格是否存在 + if not cmds.objExists(mesh): + continue + + # 获取蒙皮权重文件路径 + path = os.path.join(cmds.SGDescriptor(p=True), "skin_buffer") + + # 检查路径是否存在 + if not os.path.exists(path): + continue + + # 获取所有.skin文件 + skin_files = [f for f in os.listdir(path) if f.endswith('.skin')] + + # 处理每个蒙皮文件 + for skin_file in skin_files: + # 检查文件名是否包含网格名称 + if mesh in skin_file: + # 构建完整的文件路径 + skin_file_path = os.path.join(path, skin_file) + + # 查找现有的蒙皮变形器 + skin_cluster = cmds.findRelatedSkinCluster(mesh) + + # 如果存在蒙皮变形器,则解除绑定 + if cmds.objExists(skin_cluster): + cmds.skinCluster(mesh, edit=True, unbind=True) + + # 导入蒙皮权重 + try: + cmds.SGSkinCluster(mesh, import_file=skin_file_path) + except: + pass # 忽略导入错误 \ No newline at end of file diff --git a/scripts/Reference/SGFastUnbindSkinCluster.py b/scripts/Reference/SGFastUnbindSkinCluster.py new file mode 100644 index 0000000..2716682 --- /dev/null +++ b/scripts/Reference/SGFastUnbindSkinCluster.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/26 +""" + +import os +import maya.cmds as cmds + +def sg_fast_unbind_skin_cluster(): + """ + 快速解除蒙皮绑定 + - 遍历所有网格 + - 导出蒙皮权重 + - 解除蒙皮绑定 + """ + # 遍历所有网格(0-53) + for i in range(54): + # 获取网格名称 + mesh = cmds.SGGetMeshes(m=i) + + # 检查网格是否存在 + if not cmds.objExists(mesh): + continue + + # 查找蒙皮变形器 + skin_cluster = cmds.findRelatedSkinCluster(mesh) + + # 如果存在蒙皮变形器 + if cmds.objExists(skin_cluster): + # 构建保存路径 + path = os.path.join(cmds.SGDescriptor(p=True), "skin_buffer") + skin_file = os.path.join(path, f"{mesh}.skin") + + # 如果目录不存在则创建 + if not os.path.exists(path): + os.makedirs(path) + + # 导出蒙皮权重 + cmds.SGSkinCluster(ef=mesh, export_file=skin_file) + + # 解除蒙皮绑定 + cmds.skinCluster(mesh, edit=True, unbind=True) \ No newline at end of file diff --git a/scripts/Reference/SGGetBlendShape.py b/scripts/Reference/SGGetBlendShape.py new file mode 100644 index 0000000..67ecf3b --- /dev/null +++ b/scripts/Reference/SGGetBlendShape.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_get_blend_shape(mesh): + """ + 获取指定网格的混合变形节点 + + 参数: + mesh (str): 网格名称 + + 返回: + str: 混合变形节点名称,如果没有找到则返回空字符串 + """ + # 获取网格的历史记录 + history = cmds.listHistory(mesh) or [] + + # 遍历历史记录查找混合变形节点 + for node in history: + if cmds.objectType(node) == "blendShape": + return node + + # 如果没有找到则返回空字符串 + return "" \ No newline at end of file diff --git a/scripts/Reference/SGHelp.py b/scripts/Reference/SGHelp.py new file mode 100644 index 0000000..b53983a --- /dev/null +++ b/scripts/Reference/SGHelp.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 清泉时代科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import os +import maya.cmds as cmds + +def sg_help(help_name): + """ + 显示Super Rigging帮助窗口 + + 参数: + help_name (str): 帮助图片的名称 + """ + # 如果窗口已存在则删除 + if cmds.window('helpImageWin', exists=True): + cmds.deleteUI('helpImageWin') + + # 创建新窗口 + cmds.window( + 'helpImageWin', + title="Super Rigging Help" + ) + + # 获取环境变量中的路径 + path = os.environ.get('SG_PATH') + image_path = os.path.join(path, 'images', 'help', f'{help_name}.jpg') + + # 创建布局 + cmds.formLayout('helpFormLayout', width=610, height=800) + + # 创建滚动布局 + scroll_layout = cmds.scrollLayout('asHelpImage') + form_layout_b = cmds.formLayout('asHelpFormLayoutB') + + # 添加图片 + cmds.image(image=image_path) + + # 返回上级布局 + cmds.setParent('..') + cmds.setParent('..') + + # 设置窗口大小 + cmds.window('helpImageWin', edit=True, widthHeight=(626, 800)) + + # 设置布局附着 + cmds.formLayout( + 'helpFormLayout', + edit=True, + attachForm=[ + ('asHelpImage', 'right', 0), + ('asHelpImage', 'left', 0), + ('asHelpImage', 'top', 0), + ('asHelpImage', 'bottom', 0) + ] + ) + + # 显示窗口 + cmds.showWindow('helpImageWin') \ No newline at end of file diff --git a/scripts/Reference/SGImportBodyAnim.py b/scripts/Reference/SGImportBodyAnim.py new file mode 100644 index 0000000..af6609d --- /dev/null +++ b/scripts/Reference/SGImportBodyAnim.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import os +import imp +import maya.cmds as cmds + +def sg_import_body_anim(): + """ + 导入身体动画数据 + """ + path = os.getenv('SG_PATH') + meta_anim_path = os.path.join(path, 'files/meta_anim/meta_body_anim.py') + + # 使用imp模块导入动画脚本 + imp.load_source('', meta_anim_path) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_import_body_anim() \ No newline at end of file diff --git a/scripts/Reference/SGImportFaceAnim.py b/scripts/Reference/SGImportFaceAnim.py new file mode 100644 index 0000000..1c6ae34 --- /dev/null +++ b/scripts/Reference/SGImportFaceAnim.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import os +import imp +import maya.cmds as cmds + +def sg_import_face_anim(): + """ + 导入面部动画数据 + """ + path = os.getenv('SG_PATH') + meta_anim_path = os.path.join(path, 'files/meta_anim/meta_face_anim.py') + + # 使用imp模块导入动画脚本 + imp.load_source('', meta_anim_path) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_import_face_anim() \ No newline at end of file diff --git a/scripts/Reference/SGImportSkinCluster.py b/scripts/Reference/SGImportSkinCluster.py new file mode 100644 index 0000000..b6802b8 --- /dev/null +++ b/scripts/Reference/SGImportSkinCluster.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import os +import maya.cmds as cmds + +def sg_import_skin_cluster(): + """ + 导入蒙皮权重数据 + 支持两种模式: + 1. 选中单个模型时,通过文件对话框选择单个权重文件导入 + 2. 未选中或选中多个模型时,可以选择多个权重文件,根据文件名自动匹配模型 + """ + # 获取当前选中的物体 + selection = cmds.ls(sl=True) + + # 设置文件过滤器 + basic_filter = "Maya Skin (*.skin)" + + # 打开文件选择对话框 + skin_files = cmds.fileDialog2(fileFilter=basic_filter, + fileMode=4, # 允许选择多个文件 + dialogStyle=1) + + # 如果用户取消了选择,直接返回 + if not skin_files: + return + + # 处理单个选中物体的情况 + if len(selection) == 1: + cmds.SGSkinCluster(selection[0], skin_files[0], if_=True) + else: + # 处理多个文件的情况 + for skin_file in skin_files: + # 从文件路径中提取模型名称 + file_name = os.path.basename(skin_file) + mesh_name = os.path.splitext(file_name)[0] + + # 检查模型是否存在 + if cmds.objExists(mesh_name): + # 检查是否已经存在蒙皮变形器 + skin_cluster = cmds.findRelatedSkinCluster(mesh_name) + if not skin_cluster: + # 导入权重数据 + cmds.SGSkinCluster(mesh_name, skin_file, if_=True) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_import_skin_cluster() \ No newline at end of file diff --git a/scripts/Reference/SGMesheDetach.py b/scripts/Reference/SGMesheDetach.py new file mode 100644 index 0000000..d4f4646 --- /dev/null +++ b/scripts/Reference/SGMesheDetach.py @@ -0,0 +1,310 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_meshe_detach(topology): + """ + 根据不同的拓扑类型分离模型 + Args: + topology: 拓扑类型 ('DAZG8F', 'DAZG8M', 'DAZG9', 'CC3') + """ + topology_map = { + 'DAZG8F': sg_daz_g8f_detach, + 'DAZG8M': sg_daz_g8m_detach, + 'DAZG9': sg_daz_g9_detach, + 'CC3': sg_cc3_detach + } + + if topology in topology_map: + topology_map[topology]() + +def sg_daz_g8f_detach(): + """ + 分离DAZ G8F模型的身体、头部、牙齿和眼睛 + """ + selection = cmds.ls(sl=True) + if not selection: + return + + shape_a = cmds.listRelatives(selection[0], s=True) + + # 定义边缘索引 + edges_a = [ + 52019,52021,53274,53278,54011,54013,54034,54037,54252,54256, + 54289,54291,54600,54602,54624,54626,54648,54650,54674,54676, + 54690,54692,54715,54717,54725,54727,57874,57881,58328,58330, + 58363,58365,59260,59264,59265,59267,59307,59308,59311,59312, + 59332,59344,59348,61354,61355,61385,61387,61395,61396,61397, + 63352,63354,64601,64609,65323,65325,65344,65349,65561,65565, + 65599,65601,65906,65908,65930,65932,65954,65956,65979,65981, + 65995,65997,66024,66026,66033,66035,69124,69127,69540,69542, + 69572,69574,70459,70464,70466,70467,70504,70505,70508,70509, + 70530,70543,70549,72515,72516,72544,72546,72554,72560,72561 + ] + + edges_b = [ + 24644,24648,25049,25051,25127,25129,25139,25141,25144,25148, + 25663,25665,25671,25673,25677,25683,25695,25697,25701,25705, + 25798,25800,25814,25816,25822,25824,25828,25830,25847,25851, + 26130,26132,27410,27412,27417,27419,28237,28243,28246,28248, + 28250,28251,28304,28306,28604,28611,28996,28998,29066,29068, + 29076,29078,29083,29089,29554,29556,29562,29564,29568,29572, + 29586,29588,29590,29594,29688,29690,29706,29708,29714,29716, + 29723,29725,29734,29738,30012,30014,31292,31294,31298,31300, + 32066,32070,32076,32079,32080,32082,32137,32139 + ] + + # 第一次分离 + cmds.select(cl=True) + for edge in edges_a: + cmds.select(f"{selection[0]}.e[{edge}]", add=True) + + cmds.DetachComponent() + cutting_a = cmds.polySeparate(shape_a[0], ch=False) + + # 处理牙齿 + cmds.select(cl=True) + for i in range(2, len(cutting_a)-4): + cmds.select(cutting_a[i], add=True) + cmds.polyUnite(n="DAZ_G8F_TeethA", ch=False) + + # 处理眼睛 + cmds.select(cl=True) + cmds.select([cutting_a[34], cutting_a[36]]) + cmds.polyUnite(n="DAZ_G8F_EyeLeft", ch=False) + + cmds.select(cl=True) + cmds.select([cutting_a[35], cutting_a[37]]) + cmds.polyUnite(n="DAZ_G8F_EyeRight", ch=False) + + # 重命名主要部件 + cmds.rename(cutting_a[0], "DAZ_G8F_Body") + cmds.rename(cutting_a[1], "DAZ_G8F_HeadA") + + # 第二次分离(头部) + shape_b = cmds.listRelatives("DAZ_G8F_HeadA", s=True) + cmds.select(cl=True) + for edge in edges_b: + cmds.select(f"DAZ_G8F_HeadA.e[{edge}]", add=True) + + cmds.DetachComponent() + cutting_b = cmds.polySeparate(shape_b[0], ch=False) + + # 合并牙齿 + cmds.select(cl=True) + cmds.select(["DAZ_G8F_TeethA", cutting_b[1]]) + cmds.polyUnite(n="DAZ_G8F_Teeth", ch=False) + + cmds.rename(cutting_b[0], "DAZ_G8F_Head") + + # 将所有部件移到世界坐标系 + for obj in ["DAZ_G8F_Body", "DAZ_G8F_Head", "DAZ_G8F_Teeth", + "DAZ_G8F_EyeLeft", "DAZ_G8F_EyeRight"]: + try: + cmds.parent(obj, world=True) + except: + pass + + cmds.delete(selection[0]) + +def sg_daz_g8m_detach(): + """ + 分离DAZ G8M模型的身体、头部、牙齿和眼睛 + """ + selection = cmds.ls(sl=True) + if not selection: + return + + shape_a = cmds.listRelatives(selection[0], s=True) + + # 定义边缘索引 + edges_a = [ + 50883,50885,51704,51708,52433,52435,52456,52459,52674,52678, + 56450,56452,56485,56487,57038,57042,57043,57045,57085,57086, + 57089,57090,57110,57122,57126,58900,58901,58931,58933,58941, + 58942,58943,60178,60180,60993,61001,61707,61709,61728,61733, + 61945,61949,65640,65642,65672,65674,66223,66228,66230,66231, + 66268,66269,66272,66273,66294,66307,66313,68047,68048,68076, + 68078,68086,68092,68093,106855,106857,106902,106904,106926,106928, + 106948,106950,106972,106974,106988,106990,107011,107013,107021,107023, + 107058,107065,107647,107649,107689,107691,107713,107715,107736,107738, + 107759,107761,107775,107777,107802,107804,107811,107813,107844,107847 + ] + + edges_b = [ + 24301,24396,24400,24801,24803,24879,24881,24891,24893,24896, + 24900,25415,25417,25423,25425,25429,25435,25447,25449,25453, + 25457,25550,25552,25566,25568,25574,25576,25580,25582,25599, + 25603,25882,25884,27162,27164,27169,27171,27989,27995,27998, + 28000,28002,28003,28056,28058,28265,28356,28363,28748,28750, + 28818,28820,28828,28830,28835,28841,29306,29308,29314,29316, + 29320,29324,29338,29340,29342,29346,29440,29442,29458,29460, + 29466,29468,29475,29477,29486,29490,29764,29766,31044,31046, + 31050,31052,31818,31822,31828,31831,31832,31834,31889,31891 + ] + + # 第一次分离 + cmds.select(cl=True) + for edge in edges_a: + cmds.select(f"{selection[0]}.e[{edge}]", add=True) + + cmds.DetachComponent() + cutting_a = cmds.polySeparate(shape_a[0], ch=False) + + # 处理牙齿 + cmds.select(cl=True) + for i in range(1, len(cutting_a)-5): + cmds.select(cutting_a[i], add=True) + cmds.polyUnite(n="DAZ_G8M_TeethA", ch=False) + + # 处理眼睛 + cmds.select(cl=True) + cmds.select([cutting_a[34], cutting_a[36]]) + cmds.polyUnite(n="DAZ_G8M_EyeLeft", ch=False) + + cmds.select(cl=True) + cmds.select([cutting_a[35], cutting_a[37]]) + cmds.polyUnite(n="DAZ_G8M_EyeRight", ch=False) + + # 重命名主要部件 + cmds.rename(cutting_a[0], "DAZ_G8M_HeadA") + cmds.rename(cutting_a[33], "DAZ_G8M_Body") + + # 第二次分离(头部) + shape_b = cmds.listRelatives("DAZ_G8M_HeadA", s=True) + cmds.select(cl=True) + for edge in edges_b: + cmds.select(f"DAZ_G8M_HeadA.e[{edge}]", add=True) + + cmds.DetachComponent() + cutting_b = cmds.polySeparate(shape_b[0], ch=False) + + # 合并牙齿 + cmds.select(cl=True) + cmds.select(["DAZ_G8M_TeethA", cutting_b[1]]) + cmds.polyUnite(n="DAZ_G8M_Teeth", ch=False) + + cmds.rename(cutting_b[0], "DAZ_G8M_Head") + + # 将所有部件移到世界坐标系 + for obj in ["DAZ_G8M_Body", "DAZ_G8M_Head", "DAZ_G8M_Teeth", + "DAZ_G8M_EyeLeft", "DAZ_G8M_EyeRight"]: + try: + cmds.parent(obj, world=True) + except: + pass + + cmds.delete(selection[0]) + +def sg_daz_g9_detach(): + """ + 分离DAZ G9模型的身体、头部和眼睛 + """ + selection = cmds.ls(sl=True) + + for sel in selection: + shape_a = cmds.listRelatives(sel, s=True) + vertex_count = cmds.polyEvaluate(shape_a[0], v=True) + + if vertex_count == 2120: # 处理眼睛 + cutting_a = cmds.polySeparate(shape_a[0], ch=False) + + cmds.select(cl=True) + cmds.select([cutting_a[0], cutting_a[2]]) + cmds.polyUnite(n="DAZ_G9_EyeLeft", ch=False) + + cmds.select(cl=True) + cmds.select([cutting_a[1], cutting_a[3]]) + cmds.polyUnite(n="DAZ_G9_EyeRight", ch=False) + + for obj in ["DAZ_G9_EyeLeft", "DAZ_G9_EyeRight"]: + try: + cmds.parent(obj, world=True) + except: + pass + + elif vertex_count == 100676: # 处理身体和头部 + edges_a = [ + 168825,168827,168837,168839,168849,168851,168856,168860,168869,168873, + # ... (其余边缘索引保持不变) + ] + + cmds.select(cl=True) + for edge in edges_a: + cmds.select(f"{sel}.e[{edge}]", add=True) + + cmds.DetachComponent() + cutting_a = cmds.polySeparate(shape_a[0], ch=False) + + cmds.rename(cutting_a[1], "DAZ_G9_Head") + cmds.rename(cutting_a[0], "DAZ_G9_Body") + + for obj in ["DAZ_G9_Head", "DAZ_G9_Body"]: + try: + cmds.parent(obj, world=True) + except: + pass + + cmds.delete(sel) + +def sg_cc3_detach(): + """ + 分离CC3模型的身体、头部、睫毛和眼睛 + """ + selection = cmds.ls(sl=True) + + for sel in selection: + shape_a = cmds.listRelatives(sel, s=True) + vertex_count = cmds.polyEvaluate(shape_a[0], v=True) + + if vertex_count == 56412: + edges_a = [ + 34694,34698,35734,35738,35750,35752,36218,36222,36419,36425, + # ... (其余边缘索引保持不变) + ] + + cmds.select(cl=True) + for edge in edges_a: + cmds.select(f"{sel}.e[{edge}]", add=True) + + cmds.DetachComponent() + cutting_a = cmds.polySeparate(shape_a[0], ch=False) + + # 合并头部 + cmds.select(cl=True) + for i in range(3): + cmds.select(cutting_a[i], add=True) + cmds.polyUnite(n="CC_Base_Head_Mesh", ch=False) + + # 合并睫毛 + cmds.select(cl=True) + for i in range(4, 8): + cmds.select(cutting_a[i], add=True) + cmds.polyUnite(n="CC_Base_Eyelash_Mesh", ch=False) + + cmds.rename(cutting_a[3], "CC_Base_Body_Mesh") + cmds.parent("CC_Base_Body_Mesh", world=True) + + cmds.delete(sel) + + elif vertex_count == 2568: # 处理眼睛 + cutting_a = cmds.polySeparate(shape_a[0], ch=False) + + cmds.select(cl=True) + cmds.select([cutting_a[0], cutting_a[1]]) + cmds.polyUnite(n="CC_Base_Eye_Right_Mesh", ch=False) + + cmds.select(cl=True) + cmds.select([cutting_a[2], cutting_a[3]]) + cmds.polyUnite(n="CC_Base_Eye_Left_Mesh", ch=False) + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGMotionApply.py b/scripts/Reference/SGMotionApply.py new file mode 100644 index 0000000..5119f07 --- /dev/null +++ b/scripts/Reference/SGMotionApply.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import os +import imp +import maya.cmds as cmds + +def sg_motion_apply(): + """ + 导入动作应用模块 + """ + path = os.getenv('SG_PATH') + meta_motion_path = os.path.join(path, 'files/meta_motion_apply/meta_motion_apply.py') + + # 使用imp模块导入动作应用脚本 + imp.load_source('', meta_motion_path) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_motion_apply() \ No newline at end of file diff --git a/scripts/Reference/SGPose.py b/scripts/Reference/SGPose.py new file mode 100644 index 0000000..63adc30 --- /dev/null +++ b/scripts/Reference/SGPose.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/04/17 +""" + +import os +import maya.cmds as cmds + +def sg_pose(pose): + """ + 设置角色姿势 + Args: + pose: 姿势名称 + """ + # 获取当前坐标轴方向 + axis = cmds.upAxis(q=True, ax=True) + + # 定义需要处理的关节列表 + pose_joints = [ + "clavicle_r", "clavicle_l", + "upperarm_r", "upperarm_l", + "lowerarm_r", "lowerarm_l", + "hand_r", "hand_l", + "index_metacarpal_r", "index_metacarpal_l", + "index_01_r", "index_01_l", + "index_02_r", "index_02_l", + "middle_metacarpal_r", "middle_metacarpal_l", + "middle_01_r", "middle_01_l", + "middle_02_r", "middle_02_l", + "ring_metacarpal_r", "ring_metacarpal_l", + "ring_01_r", "ring_01_l", + "ring_02_r", "ring_02_l", + "pinky_metacarpal_r", "pinky_metacarpal_l", + "pinky_01_r", "pinky_01_l", + "pinky_02_r", "pinky_02_l" + ] + + # 创建临时定位器 + locator = "MetaHumanLocatorTemp" + if not cmds.objExists(locator): + cmds.spaceLocator(n=locator) + + locator_grp = "MetaHumanLocatorGrpTemp" + if not cmds.objExists(locator_grp): + cmds.group(em=True, n=locator_grp) + cmds.parent(locator, locator_grp) + + # 读取关节数据 + json_file = os.path.join(os.getenv("SG_PATH"), "files/data/BodyJoints.json") + objects = cmds.SGReadJson(f=json_file, t="object") + + for obj in objects: + joint_name = cmds.SGReadJson(d=obj, k="joint", t="string")[0] + if joint_name in pose_joints: + pos = cmds.SGReadJson(d=obj, k=pose, t="double") + joint_drv = f"{joint_name}_drv" + + # 创建并删除父约束以获取位置 + cmds.delete( + cmds.parentConstraint( + joint_drv, + locator, + skipRotate=["x", "y", "z"], + weight=1 + ) + ) + + # 根据坐标轴设置旋转 + if axis == "y": + cmds.rotate(pos[0], pos[1], pos[2], locator, ws=True) + elif axis == "z": + cmds.setAttr(f"{locator_grp}.r", -90, 0, 0, type="float3") + cmds.rotate(pos[0], pos[1], pos[2], locator, ws=True) + cmds.setAttr(f"{locator_grp}.r", 0, 0, 0, type="float3") + + # 创建并删除父约束以设置旋转 + cmds.delete( + cmds.parentConstraint( + locator, + joint_drv, + skipTranslate=["x", "y", "z"], + weight=1 + ) + ) + + # 清理临时对象 + cmds.delete(locator) + cmds.delete(locator_grp) + + # 刷新视图 + cmds.refresh() + + # 处理网格 + mesh_indices = [0, 9, 18, 26, 33, 38, 42, 46, 50, 51, 52, 53] + meshes = [] + + for index in mesh_indices: + mesh = cmds.SGGetMeshes(m=index) + if cmds.objExists(mesh): + meshes.append(mesh) + + # 执行轴向修正和重置绑定姿势 + cmds.SGSkinAmendAxis() + cmds.SGBindPoseReset(meshes) + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGPresetsSettings.py b/scripts/Reference/SGPresetsSettings.py new file mode 100644 index 0000000..26370b8 --- /dev/null +++ b/scripts/Reference/SGPresetsSettings.py @@ -0,0 +1,260 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/04/08 +""" + +import maya.cmds as cmds +import math + +def sg_presets_settings(): + """ + 创建预设设置窗口 + """ + # 检查主窗口是否存在 + if not cmds.window('SuperRiggingEditor', exists=True): + return + + # 如果预设窗口已存在则删除 + if cmds.window('presetsSettingsWin', exists=True): + cmds.deleteUI('presetsSettingsWin') + + # 获取关节信息文件路径 + json_file = cmds.SGDescriptor(ti="jointsInfo") + + # 创建窗口 + window = cmds.window('presetsSettingsWin', + title="Presets Settings", + width=310, + height=100, + sizeable=True, + toolbox=True, + parent='SuperRiggingEditor') + + # 主布局 + main_form = cmds.formLayout('sgPresetsFormLayout') + + # 创建控件 + topology_field = cmds.textField('sgTopologyInfo', text=json_file) + separator_a = cmds.separator('sgPresetsSeparatorA', height=10, style="in") + + # 左侧列布局 + col_a = cmds.columnLayout('sgPresetsColumnLayoutA', + adjustableColumn=True, + columnAttach=('both', 5), + rowSpacing=2, + columnWidth=150) + + # 左侧按钮 + cmds.button(label="Select Head Vertexs", align="center", + command="cmds.SGSelectVertexs(0, 'head')") + cmds.button(label="Save Head Vertexs", align="center", + command="cmds.SGSaveVertexs(0, 'head')") + cmds.separator(height=10, style="in") + + cmds.button(label="Select Teeth Vertexs", align="center", + command="cmds.SGSelectVertexs(1, 'teeth')") + cmds.button(label="Save Teeth Vertexs", align="center", + command="cmds.SGSaveVertexs(1, 'teeth')") + cmds.separator(height=10, style="in") + + cmds.button(label="Select EyeLeft Vertexs", align="center", + command="cmds.SGSelectVertexs(3, 'eyeLeft')") + cmds.button(label="Save EyeLeft Vertexs", align="center", + command="cmds.SGSaveVertexs(3, 'eyeLeft')") + cmds.separator(height=10, style="in") + + cmds.button(label="Select EyeRight Vertexs", align="center", + command="cmds.SGSelectVertexs(4, 'eyeRight')") + cmds.button(label="Save EyeRight Vertexs", align="center", + command="cmds.SGSaveVertexs(4, 'eyeRight')") + cmds.setParent('..') + + # 右侧列布局 + col_b = cmds.columnLayout('sgPresetsColumnLayoutB', + adjustableColumn=True, + columnAttach=('both', 5), + rowSpacing=2, + columnWidth=150) + + # 右侧按钮 + cmds.button(label="Select Body Vertexs", align="center", + command="cmds.SGSelectVertexs(50, 'body')") + cmds.button(label="Save Body Vertexs", align="center", + command="cmds.SGSaveVertexs(50, 'body')") + cmds.separator(height=10, style="in") + + cmds.button(label="Select Neck Vertexs", align="center", + command="cmds.SGSelectVertexs(0, 'neck')") + cmds.button(label="Save Neck Vertexs", align="center", + command="cmds.SGSaveVertexs(0, 'neck')") + cmds.separator(height=10, style="in") + + cmds.button(label="Select HeadEye Vertexs", align="center", + command="cmds.SGSelectVertexs(0, 'headEye')") + cmds.button(label="Save HeadEye Vertexs", align="center", + command="cmds.SGSaveVertexs(0, 'headEye')") + cmds.separator(height=10, style="in") + + cmds.button(label="Select HeadMouth Vertexs", align="center", + command="cmds.SGSelectVertexs(0, 'headMouth')") + cmds.button(label="Save HeadMouth Vertexs", align="center", + command="cmds.SGSaveVertexs(0, 'headMouth')") + cmds.setParent('..') + + # 底部列布局 + col_c = cmds.columnLayout('sgPresetsColumnLayoutC', + adjustableColumn=True, + columnAttach=('both', 5), + rowSpacing=2, + columnWidth=150) + + cmds.separator(height=10, style="in") + cmds.button(label="The Closest Point Of The Head", align="center", + command="cmds.SGGetClosestPointOnMesh(0)") + cmds.button(label="The Closest Point Of The Body", align="center", + command="cmds.SGGetClosestPointOnMesh(50)") + cmds.button(label="The Closest Point Of The Teeth", align="center", + command="cmds.SGGetClosestPointOnMesh(1)") + cmds.separator(height=10, style="in") + cmds.button(label="Write Joints Default Position", align="center", + command="cmds.SGWriteJointsDefaultPosition()") + cmds.separator(height=10, style="in") + cmds.setParent('..') + + # 设置表单布局 + cmds.formLayout(main_form, edit=True, + attachForm=[(topology_field, 'top', 3), + (topology_field, 'left', 0), + (topology_field, 'right', 0), + (separator_a, 'left', 0), + (separator_a, 'right', 0), + (col_a, 'left', 0), + (col_b, 'right', 0), + (col_c, 'left', 0), + (col_c, 'right', 0)], + attachControl=[(separator_a, 'top', 1, topology_field), + (col_a, 'top', 1, separator_a), + (col_b, 'top', 1, separator_a), + (col_c, 'top', 1, col_a)], + attachPosition=[(col_a, 'right', 0, 51), + (col_b, 'left', 0, 49)]) + + cmds.showWindow(window) + +def sg_select_vertexs(mesh_index, region): + """ + 选择指定区域的顶点 + Args: + mesh_index: 网格索引 + region: 区域名称 + """ + open_file = cmds.SGDescriptor(ti="vertexsInfo") + mesh = cmds.SGGetMeshes(m=mesh_index) + vertexs = cmds.SGReadJson(f=open_file, k=region, t="int") + + cmds.select(cl=True) + for vertex in vertexs: + cmds.select(f"{mesh}.vtx[{vertex}]", add=True) + +def sg_save_vertexs(mesh_index, region): + """ + 保存选中顶点到指定区域 + Args: + mesh_index: 网格索引 + region: 区域名称 + """ + json_file = cmds.SGDescriptor(ti="vertexsInfo") + mesh = cmds.SGGetMeshes(m=mesh_index) + selection = cmds.ls(sl=True, fl=True) + + indices = [] + for sel in selection: + mesh_name, vertex_id = sel.split('.') + if not mesh_name.startswith(mesh): + cmds.error("Model Selection Error...") + return + indices.append(int(vertex_id.strip('vtx[]'))) + + cmds.SGWriteJson(of=json_file, sf=json_file, k=region, t="int", value=indices) + +def sg_get_closest_point_on_mesh(mesh_index): + """ + 获取最近点 + Args: + mesh_index: 网格索引 + """ + open_file = cmds.SGDescriptor(ti="jointsInfo") + mesh = cmds.SGGetMeshes(m=mesh_index) + closest_point_node = cmds.createNode('closestPointOnMesh') + cmds.connectAttr(f"{mesh}.worldMesh[0]", f"{closest_point_node}.inMesh", f=True) + + key = "head" if mesh_index != 50 else "body" + cmds.select(cl=True) + + objects = cmds.SGReadJson(f=open_file, k=key, t="object") + + for obj in objects: + joint_name = cmds.SGReadJson(d=obj, k="joint", t="string")[0] + + if mesh_index in [0, 1]: + joint = f"DHIhead:{joint_name}" + index = cmds.SGReadJson(d=obj, k="mesh", t="int")[0] + if index == mesh_index: + point_position = cmds.xform(joint, q=True, ws=True, t=True) + cmds.setAttr(f"{closest_point_node}.inPosition", *point_position) + closest_vertex_index = cmds.getAttr(f"{closest_point_node}.closestVertexIndex") + cmds.select(f"{mesh}.vtx[{closest_vertex_index}]", add=True) + else: + joint = f"{joint_name}_drv" + enable = cmds.SGReadJson(d=obj, k="enable", t="bool")[0] + if enable: + point_position = cmds.xform(joint, q=True, ws=True, t=True) + cmds.setAttr(f"{closest_point_node}.inPosition", *point_position) + closest_vertex_index = cmds.getAttr(f"{closest_point_node}.closestVertexIndex") + cmds.select(f"{mesh}.vtx[{closest_vertex_index}]", add=True) + + cmds.delete(closest_point_node) + +def sg_write_joints_default_position(): + """ + 写入关节默认位置 + """ + json_file = cmds.SGDescriptor(ti="jointsInfo") + + # 处理身体关节 + object_body = cmds.SGReadJson(f=json_file, k="body", t="object") + for i, obj in enumerate(object_body): + joint_name = cmds.SGReadJson(d=obj, k="joint", t="string")[0] + joint = f"{joint_name}_drv" + pos = cmds.xform(joint, q=True, ws=True, t=True) + + # 四舍五入到小数点后4位 + pos = [round(x * 10000) / 10000 for x in pos] + + data = cmds.SGWriteJson(d=obj, sf="data", k="translate", t="double", value=pos) + object_body[i] = data + + cmds.SGWriteJson(of=json_file, sf=json_file, k="body", t="object", value=object_body) + + # 处理头部关节 + object_head = cmds.SGReadJson(f=json_file, k="head", t="object") + for i, obj in enumerate(object_head): + joint_name = cmds.SGReadJson(d=obj, k="joint", t="string")[0] + joint = f"DHIhead:{joint_name}" + pos = cmds.xform(joint, q=True, ws=True, t=True) + + # 四舍五入到小数点后4位 + pos = [round(x * 10000) / 10000 for x in pos] + + data = cmds.SGWriteJson(d=obj, sf="data", k="translate", t="double", value=pos) + object_head[i] = data + + cmds.SGWriteJson(of=json_file, sf=json_file, k="head", t="object", value=object_head) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_presets_settings() \ No newline at end of file diff --git a/scripts/Reference/SGRBFDeformerWindow.py b/scripts/Reference/SGRBFDeformerWindow.py new file mode 100644 index 0000000..2af4235 --- /dev/null +++ b/scripts/Reference/SGRBFDeformerWindow.py @@ -0,0 +1,199 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/11/18 +""" + +import maya.cmds as cmds + +def sg_rbf_deformer_window(): + """ + 创建RBF变形器窗口 + """ + # 检查主窗口是否存在 + if not cmds.window('SuperRiggingEditor', exists=True): + return + + # 如果RBF窗口已存在则删除 + if cmds.window('RBFDeformerWin', exists=True): + cmds.deleteUI('RBFDeformerWin') + + # 设置UI文本(支持中英文) + texts = { + 'Load': 'Load...', + 'Original': 'Original:', + 'Deformed': 'Deformed:', + 'Radius': 'Radius:', + 'Points': 'Points:', + 'Execute': 'Execute' + } + + if cmds.SGDescriptor(l=True) == "ZH": + texts.update({ + 'Load': '加载...', + 'Original': '原始模型:', + 'Deformed': '变形模型:', + 'Radius': '搜索半径:', + 'Points': '采样点数:', + 'Execute': '开始执行' + }) + + # 创建窗口 + window = cmds.window('RBFDeformerWin', + title="RBF Deformer", + width=310, + height=100, + sizeable=True, + toolbox=True, + parent='SuperRiggingEditor') + + # 主布局 + cmds.columnLayout(adjustableColumn=True, + columnAttach=('both', 5), + rowSpacing=2, + columnWidth=150) + + cmds.separator(height=10, style="in") + + # 原始模型和变形模型选择 + cmds.textFieldButtonGrp('RBFDeformer_Original', + label=texts['Original'], + buttonLabel=texts['Load'], + columnWidth3=[70, 1, 30], + adjustableColumn2=2, + buttonCommand=sg_rbf_deformer_load_original) + + cmds.textFieldButtonGrp('RBFDeformer_Deformed', + label=texts['Deformed'], + buttonLabel=texts['Load'], + columnWidth3=[70, 1, 30], + adjustableColumn2=2, + buttonCommand=sg_rbf_deformer_load_deformed) + + cmds.separator(height=10, style="in") + + # 半径设置 + cmds.rowLayout(numberOfColumns=3, + columnWidth3=[70, 70, 1], + columnAttach3=['right', 'left', 'left'], + adjustableColumn3=3) + + cmds.text(label=texts['Radius']) + cmds.floatField('RBFDeformer_RadiusField', + width=70, + maxValue=2, + minValue=0.001, + value=0.1, + changeCommand=sg_set_radius_slider) + cmds.floatSlider('RBFDeformer_RadiusSlider', + maxValue=2, + minValue=0.001, + value=0.1, + dragCommand=sg_set_radius_field) + cmds.setParent('..') + + # 采样点设置 + cmds.rowLayout(numberOfColumns=3, + columnWidth3=[70, 70, 1], + columnAttach3=['right', 'left', 'left'], + adjustableColumn3=3) + + cmds.text(label=texts['Points']) + cmds.intField('RBFDeformer_PointsField', + width=70, + maxValue=10000, + minValue=100, + value=2000, + changeCommand=sg_set_points_slider) + cmds.intSlider('RBFDeformer_PointsSlider', + maxValue=10000, + minValue=100, + value=2000, + dragCommand=sg_set_points_field) + cmds.setParent('..') + + cmds.separator(height=10, style="in") + + # 执行按钮 + cmds.button(label=texts['Execute'], + align="center", + command=sg_rbf_deformer_execute) + + cmds.separator(height=10, style="in") + + cmds.showWindow(window) + +def sg_rbf_deformer_execute(*args): + """ + 执行RBF变形器操作 + """ + # 获取UI值 + original = cmds.textFieldButtonGrp('RBFDeformer_Original', query=True, text=True) + deformed = cmds.textFieldButtonGrp('RBFDeformer_Deformed', query=True, text=True) + radius = cmds.floatField('RBFDeformer_RadiusField', query=True, value=True) + points = cmds.intField('RBFDeformer_PointsField', query=True, value=True) + + # 检查模型顶点数是否一致 + org_vtx = cmds.polyEvaluate(original, vertex=True) + def_vtx = cmds.polyEvaluate(deformed, vertex=True) + + if org_vtx != def_vtx: + cmds.error("The topology of the two models is inconsistent...") + return + + # 获取选中的目标模型 + selection = cmds.ls(selection=True) + + # 执行RBF变形 + cmds.SGRBFDeformer(r=radius, np=points, rbf=1, m=[original, deformed], t=selection) + +def sg_rbf_deformer_load_original(*args): + """ + 加载原始模型 + """ + selection = cmds.ls(selection=True) + if selection: + cmds.textFieldButtonGrp('RBFDeformer_Original', edit=True, text=selection[0]) + +def sg_rbf_deformer_load_deformed(*args): + """ + 加载变形模型 + """ + selection = cmds.ls(selection=True) + if selection: + cmds.textFieldButtonGrp('RBFDeformer_Deformed', edit=True, text=selection[0]) + +def sg_set_radius_field(*args): + """ + 更新半径滑块值 + """ + value = cmds.floatSlider('RBFDeformer_RadiusSlider', query=True, value=True) + cmds.floatField('RBFDeformer_RadiusField', edit=True, value=value) + +def sg_set_radius_slider(*args): + """ + 更新半径输入框值 + """ + value = cmds.floatField('RBFDeformer_RadiusField', query=True, value=True) + cmds.floatSlider('RBFDeformer_RadiusSlider', edit=True, value=value) + +def sg_set_points_field(*args): + """ + 更新采样点滑块值 + """ + value = cmds.intSlider('RBFDeformer_PointsSlider', query=True, value=True) + cmds.intField('RBFDeformer_PointsField', edit=True, value=value) + +def sg_set_points_slider(*args): + """ + 更新采样点输入框值 + """ + value = cmds.intField('RBFDeformer_PointsField', query=True, value=True) + cmds.intSlider('RBFDeformer_PointsSlider', edit=True, value=value) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_rbf_deformer_window() \ No newline at end of file diff --git a/scripts/Reference/SGRangeBlendShapeAll.py b/scripts/Reference/SGRangeBlendShapeAll.py new file mode 100644 index 0000000..a14e2a5 --- /dev/null +++ b/scripts/Reference/SGRangeBlendShapeAll.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_range_blend_shape_all(range_value): + """ + 为所有网格的混合变形器设置范围值 + Args: + range_value (float): 要设置的范围值 + """ + # 遍历前50个网格 + for i in range(50): + # 获取当前索引的网格 + mesh = cmds.SGGetMeshes(m=i) + + if cmds.objExists(mesh): + # 获取网格的混合变形器 + blend_shape = cmds.SGGetBlendShape(mesh) + + if cmds.objExists(blend_shape): + # 获取所有权重属性 + bs_names = cmds.listAttr(f"{blend_shape}.weight", m=True) + count = len(bs_names) + + # 创建进度窗口 + cmds.progressWindow( + title="Set Range Target", + min=0, + max=count, + isInterruptable=True + ) + + # 处理每个混合变形目标 + for j in range(count): + # 检查是否取消操作 + if cmds.progressWindow(query=True, isCancelled=True): + break + + # 重新生成目标并设置范围 + bs_name = cmds.sculptTarget( + blend_shape, + e=True, + regenerate=True, + target=j + ) + + cmds.SGSetBlendShapes(r=range_value, value=bs_name[0]) + cmds.delete(bs_name) + + # 更新进度窗口 + status = f"[{j+1}/{count}] {blend_shape}" + cmds.progressWindow( + edit=True, + progress=j, + status=status + ) + + # 关闭进度窗口 + cmds.progressWindow(endProgress=True) + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGRangeBlendShapeSelect.py b/scripts/Reference/SGRangeBlendShapeSelect.py new file mode 100644 index 0000000..b1032f4 --- /dev/null +++ b/scripts/Reference/SGRangeBlendShapeSelect.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_range_blend_shape_select(mesh_indices, target_indices, range_value): + """ + 为选定的网格和目标设置混合变形器范围值 + Args: + mesh_indices (list): 网格索引列表 + target_indices (list): 目标索引列表 + range_value (float): 要设置的范围值 + """ + count = len(mesh_indices) + + # 初始化进度条 + cmds.SGProgressBar(sp=True) + cmds.SGProgressBar(max=count) + + # 处理每个网格和目标 + for i in range(count): + mesh_index = mesh_indices[i] + target_index = target_indices[i] + + # 获取网格 + mesh = cmds.SGGetMeshes(m=mesh_index) + + if cmds.objExists(mesh): + # 获取混合变形器 + blend_shape = cmds.SGGetBlendShape(mesh) + + if cmds.objExists(blend_shape): + # 重新生成目标并设置范围 + bs_name = cmds.sculptTarget( + blend_shape, + e=True, + regenerate=True, + target=target_index + ) + + cmds.SGSetBlendShapes(r=range_value, value=bs_name[0]) + cmds.delete(bs_name) + + # 更新进度条 + title = f"[{i+1}/{count}]Select Target Mesh..." + cmds.SGProgressBar(t=title) + cmds.SGProgressBar(apr=1) + + # 结束进度条 + cmds.SGProgressBar(ep=True) + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGRebuildTarget.py b/scripts/Reference/SGRebuildTarget.py new file mode 100644 index 0000000..3b30111 --- /dev/null +++ b/scripts/Reference/SGRebuildTarget.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_rebuild_target(target_ids, blend_shape): + """ + 重建混合变形目标 + Args: + target_ids (list): 目标索引列表 + blend_shape (str): 混合变形器名称 + """ + # 清除选择 + cmds.select(clear=True) + + # 重建每个目标并选择 + for target_id in target_ids: + rebuilt_target = cmds.sculptTarget( + blend_shape, + edit=True, + regenerate=True, + target=target_id + ) + cmds.select(rebuilt_target, add=True) + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGRefreshGeoLineEdit.py b/scripts/Reference/SGRefreshGeoLineEdit.py new file mode 100644 index 0000000..1f6fe73 --- /dev/null +++ b/scripts/Reference/SGRefreshGeoLineEdit.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_meshes_list(): + """ + 获取网格列表 + Returns: + list: 网格名称列表 + """ + meshes = [] + edition = cmds.SGDescriptor(ed=True) + + if edition >= 2: + # 获取LOD0的网格索引 + mesh_indices = cmds.SGGetMeshes(lod=0) + for index in mesh_indices: + meshes.append(cmds.SGGetMeshes(i=index)) + else: + # 获取所有网格 + meshes = cmds.SGGetMeshes() + + return meshes + +def sg_set_mesh_line_edit(index, mesh): + """ + 设置网格选项变量 + Args: + index (int): 网格索引 + mesh (str): 网格名称 + """ + meshes = cmds.SGGetMeshes() + + if mesh: + # 设置选项变量 + cmds.optionVar(stringValue=[meshes[index], mesh]) + else: + # 移除选项变量 + cmds.optionVar(remove=meshes[index]) + +def sg_refresh_geo_line_edit(): + """ + 刷新几何体行编辑器 + """ + meshes = sg_meshes_list() + + for i, mesh_name in enumerate(meshes): + if cmds.optionVar(exists=mesh_name): + # 如果存在选项变量 + mesh = cmds.optionVar(query=mesh_name) + if cmds.objExists(mesh): + # 如果对象存在,设置网格 + if mesh_name == "body_lod0_mesh": + cmds.SGSetMeshes(m=50, value=mesh) + else: + cmds.SGSetMeshes(m=i, value=mesh) + else: + # 如果对象不存在,尝试通过名称模式查找 + selection = cmds.ls(f"*{mesh_name}*", type="transform") + if selection: + for sel in selection: + if cmds.objectType(sel) == "transform": + if mesh_name == "body_lod0_mesh": + cmds.SGSetMeshes(m=50, value=sel) + else: + cmds.SGSetMeshes(m=i, value=sel) + break + else: + # 如果找不到匹配的对象,清除设置 + cmds.SGSetMeshes(m=i, value="") + else: + # 如果不存在选项变量,尝试通过名称模式查找 + selection = cmds.ls(f"*{mesh_name}*", type="transform") + if selection: + for sel in selection: + if cmds.objectType(sel) == "transform": + if mesh_name == "body_lod0_mesh": + cmds.SGSetMeshes(m=50, value=sel) + else: + cmds.SGSetMeshes(m=i, value=sel) + break + else: + # 如果找不到匹配的对象,清除设置 + cmds.SGSetMeshes(m=i, value="") + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGRefreshNeutralJointTranslation.py b/scripts/Reference/SGRefreshNeutralJointTranslation.py new file mode 100644 index 0000000..ea6a7c0 --- /dev/null +++ b/scripts/Reference/SGRefreshNeutralJointTranslation.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_refresh_neutral_joint_translation(): + """ + 刷新关节的中性位置平移值 + """ + # 获取所有关节 + joints = cmds.SGGetJoints() + + # 遍历每个关节 + for i, joint in enumerate(joints): + # 获取关节的平移值 + pos = cmds.getAttr(f"{joint}.t")[0] + + # 设置关节的中性位置 + cmds.SGSetNeutralJointTranslations(i, pos[0], pos[1], pos[2]) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_refresh_neutral_joint_translation() \ No newline at end of file diff --git a/scripts/Reference/SGRenameBlendShapes.py b/scripts/Reference/SGRenameBlendShapes.py new file mode 100644 index 0000000..c229c60 --- /dev/null +++ b/scripts/Reference/SGRenameBlendShapes.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_rename_blend_shapes(): + """ + 重命名所有网格的混合变形目标 + 遍历前50个网格,为每个混合变形目标添加网格名称前缀 + """ + # 遍历前50个网格 + for j in range(50): + # 获取网格名称 + mesh = cmds.SGGetMeshes(i=j) + blend_shape = f"{mesh}_blendShapes" + + # 检查混合变形器是否存在 + if cmds.objExists(blend_shape): + # 获取所有权重属性 + attr_weight = f"{blend_shape}.weight" + current_blend_shape_list = cmds.listAttr(attr_weight, m=True) + + # 遍历每个混合变形目标 + for i, bs_name in enumerate(current_blend_shape_list): + # 检查是否已经有正确的前缀 + if not bs_name.startswith(f"{mesh}__"): + try: + # 重命名混合变形目标 + cmds.aliasAttr( + f"{mesh}__{bs_name}", + f"{blend_shape}.w[{i}]" + ) + except: + # 忽略重命名失败的情况 + pass + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_rename_blend_shapes() \ No newline at end of file diff --git a/scripts/Reference/SGReorderBlendShapes.py b/scripts/Reference/SGReorderBlendShapes.py new file mode 100644 index 0000000..412d717 --- /dev/null +++ b/scripts/Reference/SGReorderBlendShapes.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/09/16 +""" + +import maya.cmds as cmds + +def is_sorted(array): + """ + 检查数组是否已排序 + Args: + array (list): 要检查的数组 + Returns: + bool: 如果数组已排序返回True,否则返回False + """ + return all(array[i] >= array[i-1] for i in range(1, len(array))) + +def are_indices_sorted(array_a, array_b): + """ + 检查两个数组中共同元素的索引是否有序 + Args: + array_a (list): 第一个数组 + array_b (list): 第二个数组 + Returns: + bool: 如果共同元素的索引都有序返回True,否则返回False + """ + # 存储共同元素的索引 + common_indices_a = [] + common_indices_b = [] + + # 查找共同元素并记录它们的索引 + for i, item_a in enumerate(array_a): + for j, item_b in enumerate(array_b): + if item_a == item_b: + common_indices_a.append(i) + common_indices_b.append(j) + break + + # 检查两个索引数组是否都有序 + return is_sorted(common_indices_a) and is_sorted(common_indices_b) + +def sg_reorder_blend_shapes(): + """ + 重新排序所有网格的混合变形目标 + """ + # 遍历前50个网格 + for mesh_index in range(50): + mesh = cmds.SGGetMeshes(m=mesh_index) + + if cmds.objExists(mesh): + blend_shape = cmds.SGGetBlendShape(mesh) + + if cmds.objExists(blend_shape): + attr_weight = f"{blend_shape}.weight" + + # 获取目标数量和索引 + nb_in_tgt = cmds.getAttr(attr_weight, size=True) + existing_indices = cmds.getAttr(attr_weight, multiIndices=True) + + # 获取当前和预期的混合变形目标列表 + current_blend_shape_list = cmds.listAttr(attr_weight, m=True) + blend_shape_list = cmds.SGGetBlendShapes() + + # 检查是否需要重新排序 + needs_reorder = ( + nb_in_tgt - 1 != existing_indices[-1] or + not are_indices_sorted(blend_shape_list, current_blend_shape_list) + ) + + if needs_reorder: + # 重新生成所有目标 + target_list = [] + for j in existing_indices: + target = cmds.sculptTarget( + blend_shape, + e=True, + regenerate=True, + target=j + ) + target_list.append(target[0]) + + # 删除原始混合变形器 + cmds.delete(blend_shape) + + # 按照预期顺序选择目标 + cmds.select(clear=True) + for bs_name in blend_shape_list: + if bs_name in target_list: + cmds.select(bs_name, add=True) + + # 创建新的混合变形器 + cmds.select(mesh, add=True) + cmds.blendShape(automatic=True, name=blend_shape) + + # 清理临时目标 + cmds.delete(target_list) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_reorder_blend_shapes() \ No newline at end of file diff --git a/scripts/Reference/SGRepairJointForLOD.py b/scripts/Reference/SGRepairJointForLOD.py new file mode 100644 index 0000000..f1a5ae7 --- /dev/null +++ b/scripts/Reference/SGRepairJointForLOD.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/02/23 +""" + +import maya.cmds as cmds + +def sg_repair_joint_for_lod(lod): + """ + 修复指定LOD级别的关节 + Args: + lod (int): LOD级别 + """ + # 获取指定LOD级别的关节索引列表 + joints = cmds.SGGetJoints(lod=lod, t="int") + + # 遍历每个关节索引 + for i, index in enumerate(joints): + # 获取关节名称 + joint = cmds.SGGetJoints(i=index) + + # 如果关节不存在,则创建并设置 + if not cmds.objExists(joint): + # 创建新关节 + cmds.joint(p=(0, 0, 0), name=joint) + + # 获取并设置父关节 + parent = cmds.SGGetJoints(p=index) + try: + cmds.parent(joint, parent) + except: + pass + + # 获取并设置关节的中性位置 + pos = cmds.SGGetNeutralJointTranslations(i=index) + + # 设置平移和旋转 + cmds.setAttr(f"{joint}.t", pos[0], pos[1], pos[2], type="float3") + cmds.setAttr(f"{joint}.jo", pos[3], pos[4], pos[5], type="float3") + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGRepairNormals.py b/scripts/Reference/SGRepairNormals.py new file mode 100644 index 0000000..9867a4d --- /dev/null +++ b/scripts/Reference/SGRepairNormals.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/01 +""" + +import maya.cmds as cmds + +def sg_meta_human_normals(): + """ + 返回MetaHuman模型的法线顶点对应关系 + Returns: + list: 顶点索引列表,每两个值为一组,分别对应头部和身体的顶点 + """ + return [ + 2805,9, 2806,8, 2807,7, 2808,10, 2809,6, + 2810,5, 2811,2, 2812,1, 2813,0, 2814,4, + 2815,12, 2816,14, 2817,15, 2818,16, 2819,17, + 2820,18, 2821,19, 2822,20, 2823,21, 2824,11, + 2873,22, 2876,23, 2965,3, 2997,13, 5876,3818, + 5877,30, 5878,31, 5879,32, 5880,29, 5881,28, + 5882,26, 5883,24, 5884,25, 5885,27, 5886,34, + 5887,35, 5888,36, 5889,37, 5890,38, 5891,39, + 5892,40, 5893,41, 5894,42, 5895,33, 5944,43, + 5947,44, 11560,16789, 11564,16786, 11567,16795, + 11569,16794, 11572,16784, 11575,7645, 11578,22814, + 11582,16779, 11585,22833, 11587,22824, 11600,22801, + 11795,21578, 11798,21577, 11799,21575, 11802,21573, + 11803,21571, 11805,21569, 11807,21566, 11809,21564, + 11811,21540, 11899,16775, 11909,16774, 11910,21542, + 17607,9206, 17610,9203, 17614,9211, 17617,9207, + 17619,9198, 17622,15227, 17626,15170, 17629,9195, + 17632,15189, 17635,15180, 17647,15158, 17854,13968, + 17856,13966, 17859,13964, 17860,13962, 17863,13960, + 17865,13958, 17867,13957, 17869,13955, 17871,13927, + 17962,9192, 17968,9186, 17972,13931 + ] + +def sg_repair_normals(index): + """ + 修复模型法线 + Args: + index (int): 模型索引,目前只支持0(头部模型) + """ + # 只处理头部模型 + if index != 0: + return + + # 获取顶点对应关系 + vtxs = sg_meta_human_normals() + + # 获取头部和身体模型 + head = cmds.SGGetMeshes(m=0) + body = cmds.SGGetMeshes(m=50) + + # 计算需要处理的顶点对数量 + count = len(vtxs) // 2 + + # 初始化进度条 + cmds.SGProgressBar(sp=True) + cmds.SGProgressBar(max=count) + cmds.SGProgressBar(t="Repair Normals...") + + # 处理每对对应的顶点 + for i in range(count): + # 更新进度条 + cmds.SGProgressBar(apr=1) + + # 获取头部和身体对应的顶点索引 + h = vtxs[i*2] + b = vtxs[i*2+1] + + # 构建顶点名称 + head_vtx = f"{head}.vtx[{h}]" + body_vtx = f"{body}.vtx[{b}]" + + # 获取头部顶点的法线 + pos = cmds.polyNormalPerVertex(head_vtx, q=True, xyz=True) + + # 将法线应用到身体顶点 + cmds.polyNormalPerVertex(body_vtx, xyz=(pos[0], pos[1], pos[2])) + + # 结束进度条 + cmds.SGProgressBar(ep=True) + + # 清除选择并选择身体模型 + cmds.select(clear=True) + cmds.select(body, replace=True) + + # 烘焙历史记录 + cmds.BakeAllNonDefHistory() + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGRepairSeams.py b/scripts/Reference/SGRepairSeams.py new file mode 100644 index 0000000..7f87a6b --- /dev/null +++ b/scripts/Reference/SGRepairSeams.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/10/21 +""" + +import maya.cmds as cmds + +def sg_meta_human_normals(): + """ + 返回MetaHuman模型的法线顶点对应关系 + Returns: + list: 顶点索引列表,每两个值为一组,分别对应头部和身体的顶点 + """ + return [ + 2805,9, 2806,8, 2807,7, 2808,10, 2809,6, + 2810,5, 2811,2, 2812,1, 2813,0, 2814,4, + 2815,12, 2816,14, 2817,15, 2818,16, 2819,17, + 2820,18, 2821,19, 2822,20, 2823,21, 2824,11, + 2873,22, 2876,23, 2965,3, 2997,13, 5876,3818, + 5877,30, 5878,31, 5879,32, 5880,29, 5881,28, + 5882,26, 5883,24, 5884,25, 5885,27, 5886,34, + 5887,35, 5888,36, 5889,37, 5890,38, 5891,39, + 5892,40, 5893,41, 5894,42, 5895,33, 5944,43, + 5947,44, 11560,16789, 11564,16786, 11567,16795, + 11569,16794, 11572,16784, 11575,7645, 11578,22814, + 11582,16779, 11585,22833, 11587,22824, 11600,22801, + 11795,21578, 11798,21577, 11799,21575, 11802,21573, + 11803,21571, 11805,21569, 11807,21566, 11809,21564, + 11811,21540, 11899,16775, 11909,16774, 11910,21542, + 17607,9206, 17610,9203, 17614,9211, 17617,9207, + 17619,9198, 17622,15227, 17626,15170, 17629,9195, + 17632,15189, 17635,15180, 17647,15158, 17854,13968, + 17856,13966, 17859,13964, 17860,13962, 17863,13960, + 17865,13958, 17867,13957, 17869,13955, 17871,13927, + 17962,9192, 17968,9186, 17972,13931 + ] + +def get_neck_vertices(): + """ + 返回颈部顶点列表 + Returns: + list: 颈部顶点索引列表 + """ + return [ + 70,71,81,85,95,101,103,107,145,192, + 236,307,309,321,388,405,415,422,445,497, + # ... (为了简洁,这里省略了中间的数据,实际使用时需要包含完整的顶点列表) + 23819,23847,23859,23863,23868,23873,23884,23904,24021 + ] + +def sg_repair_seams(): + """ + 修复模型接缝 + """ + # 获取顶点对应关系 + vtxs = sg_meta_human_normals() + + # 获取头部和身体模型 + head = cmds.SGGetMeshes(m=0) + body = cmds.SGGetMeshes(m=50) + + # 复制头部模型 + head_copy = cmds.duplicate(head, rr=True)[0] + + # 存储头部顶点索引 + head_vtxs = [] + count = len(vtxs) // 2 + + # 处理对应顶点 + for i in range(count): + h = vtxs[i*2] + b = vtxs[i*2+1] + head_vtxs.append(h) + + # 获取身体顶点位置并应用到头部副本 + head_vtx = f"{head_copy}.vtx[{h}]" + body_vtx = f"{body}.vtx[{b}]" + pos = cmds.xform(body_vtx, q=True, ws=True, t=True) + cmds.xform(head_vtx, ws=True, t=pos) + + # 添加颈部顶点 + neck_vtxs = get_neck_vertices() + head_vtxs.extend(neck_vtxs) + + # 应用RBF变形器 + cmds.SGRBFDeformer( + rbf=1, + m=[head, head_copy], + t=[head], + pi=head_vtxs, + r=2.25 + ) + + # 清理临时模型 + cmds.delete(head_copy) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_repair_seams() \ No newline at end of file diff --git a/scripts/Reference/SGRepairVertexOrder.py b/scripts/Reference/SGRepairVertexOrder.py new file mode 100644 index 0000000..6e55c91 --- /dev/null +++ b/scripts/Reference/SGRepairVertexOrder.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/23 +""" + +import maya.cmds as cmds + +def sg_repair_vertex_order(): + """ + 修复所有网格的顶点顺序 + 遍历前54个网格,将其顶点顺序与参考模型对齐 + """ + # 遍历所有网格 + for i in range(54): + # 获取目标模型 + target = cmds.SGGetMeshes(m=i) + + if cmds.objExists(target): + # 清除历史记录 + cmds.select(target, replace=True) + cmds.DeleteAllHistory() + + # 获取源模型并重命名 + source = cmds.SGGetMeshes(cm=i) + source_buffer = f"{source}_buffer" + source_buffer = source_buffer[1:] # 移除第一个字符 + source_buffer = cmds.rename(source, source_buffer) + + # 应用顶点顺序 + cmds.SGSetMeshes(tvo=source_buffer, value=target) + + # 删除临时模型 + cmds.delete(source_buffer) + + print("Repair Vertex Order Repair Completed...") + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_repair_vertex_order() \ No newline at end of file diff --git a/scripts/Reference/SGResetBlendShapes.py b/scripts/Reference/SGResetBlendShapes.py new file mode 100644 index 0000000..c304dba --- /dev/null +++ b/scripts/Reference/SGResetBlendShapes.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_reset_blend_shapes(): + """ + 重置所有网格的混合变形目标名称 + 移除混合变形目标名称中的网格名称前缀 + """ + # 遍历前50个网格 + for j in range(50): + # 获取网格名称 + mesh = cmds.SGGetMeshes(i=j) + blend_shape = f"{mesh}_blendShapes" + + # 检查混合变形器是否存在 + if cmds.objExists(blend_shape): + # 获取所有权重属性 + attr_weight = f"{blend_shape}.weight" + current_blend_shape_list = cmds.listAttr(attr_weight, m=True) + + # 遍历每个混合变形目标 + for i, bs_name in enumerate(current_blend_shape_list): + # 检查是否有网格名称前缀 + prefix = f"{mesh}__" + if bs_name.startswith(prefix): + # 移除前缀 + new_name = bs_name.replace(prefix, "") + try: + # 重命名混合变形目标 + cmds.aliasAttr( + new_name, + f"{blend_shape}.w[{i}]" + ) + except: + # 忽略重命名失败的情况 + pass + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_reset_blend_shapes() \ No newline at end of file diff --git a/scripts/Reference/SGResetTarget.py b/scripts/Reference/SGResetTarget.py new file mode 100644 index 0000000..f52ed3a --- /dev/null +++ b/scripts/Reference/SGResetTarget.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_reset_target(target_ids, blend_shape): + """ + 重置混合变形目标的点位置 + Args: + target_ids (list): 目标索引列表 + blend_shape (str): 混合变形器名称 + """ + # 清除选择 + cmds.select(clear=True) + + # 处理每个目标 + for target_id in target_ids: + # 重新生成目标 + target = cmds.sculptTarget( + blend_shape, + edit=True, + regenerate=True, + target=target_id + )[0] + + # 获取形状节点 + shape = cmds.listRelatives(target, shapes=True)[0] + + # 获取顶点数量 + vertex_count = cmds.polyEvaluate(target, vertex=True) + + # 重置所有点的位置为原点 + for i in range(vertex_count): + cmds.setAttr(f"{shape}.pnts[{i}].pntx", 0) + cmds.setAttr(f"{shape}.pnts[{i}].pnty", 0) + cmds.setAttr(f"{shape}.pnts[{i}].pntz", 0) + + # 删除临时目标 + cmds.delete(target) + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGSaveBlendShape.py b/scripts/Reference/SGSaveBlendShape.py new file mode 100644 index 0000000..760dc07 --- /dev/null +++ b/scripts/Reference/SGSaveBlendShape.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_save_blend_shape(): + """ + 保存所有网格的混合变形目标 + """ + # 保存混合变形映射 + cmds.SGSaveBlendShapeMappings(0) + + # 初始化进度条 + cmds.SGProgressBar(sp=True) + + # 遍历前50个网格 + for mesh_index in range(50): + # 获取网格 + mesh = cmds.SGGetMeshes(m=mesh_index) + + if cmds.objExists(mesh): + # 获取混合变形器 + blend_shape = cmds.SGGetBlendShape(mesh) + + if cmds.objExists(blend_shape): + # 获取混合变形目标数量 + count = cmds.blendShape(blend_shape, query=True, weightCount=True) + + if count: + # 设置进度条 + cmds.SGProgressBar(max=count) + cmds.SGProgressBar(t=f"[{blend_shape}] Save Target Mesh...") + + # 处理每个混合变形目标 + for index in range(count): + # 更新进度条 + cmds.SGProgressBar(apr=1) + + # 重新生成目标并保存 + bs_name = cmds.sculptTarget( + blend_shape, + edit=True, + regenerate=True, + target=index + ) + + cmds.SGSaveBlendShapes(bs=mesh_index, i=index, value=bs_name[0]) + + # 删除临时目标 + cmds.delete(bs_name) + + # 结束进度条 + cmds.SGProgressBar(ep=True) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_save_blend_shape() \ No newline at end of file diff --git a/scripts/Reference/SGSaveBlendShapeMappings.py b/scripts/Reference/SGSaveBlendShapeMappings.py new file mode 100644 index 0000000..7c33180 --- /dev/null +++ b/scripts/Reference/SGSaveBlendShapeMappings.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/06/12 +""" + +import maya.cmds as cmds + +def sg_save_blend_shape_mappings(reset_mappings): + """ + 保存混合变形映射关系 + Args: + reset_mappings (int): 是否重置映射 + """ + # 初始化进度条 + cmds.SGProgressBar(sp=True) + + # 初始化映射索引 + mapping = 0 + + # 获取所有混合变形目标名称 + blend_shape_names = cmds.SGGetBlendShapes() + + # 设置进度条 + cmds.SGProgressBar(max=len(blend_shape_names)) + cmds.SGProgressBar(t="Update BlendShape Mappings...") + cmds.SGProgressBar(pr=0) + + # 获取所有存在的混合变形器和对应的网格索引 + blend_shapes = [] + mesh_indices = [] + + for mesh_index in range(50): + blend_shape = f"{cmds.SGGetMeshes(i=mesh_index)}_blendShapes" + if cmds.objExists(blend_shape): + blend_shapes.append(blend_shape) + mesh_indices.append(mesh_index) + + # 处理每个混合变形目标名称 + for blend_shape_name in blend_shape_names: + # 更新进度条 + cmds.SGProgressBar(apr=1) + + # 检查每个混合变形器 + for i, blend_shape in enumerate(blend_shapes): + # 获取当前混合变形器的所有权重属性 + bs_node = cmds.listAttr(f"{blend_shape}.w", m=True) + + # 如果找到匹配的目标名称 + if bs_node and blend_shape_name in bs_node: + # 保存映射关系 + cmds.SGSaveBlendShapes( + rm=reset_mappings, + ma=mapping, + m=mesh_indices[i], + value=blend_shape_name + ) + mapping += 1 + + # 结束进度条 + cmds.SGProgressBar(ep=True) + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGSetBodyNeutralJointTranslation.py b/scripts/Reference/SGSetBodyNeutralJointTranslation.py new file mode 100644 index 0000000..eecbb74 --- /dev/null +++ b/scripts/Reference/SGSetBodyNeutralJointTranslation.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_set_body_neutral_joint_translation(): + """ + 设置身体模型的关节中性位置 + """ + # 获取身体模型 + body_mesh = cmds.SGGetMeshes(m=50) + + # 如果身体模型不存在,直接返回 + if not cmds.objExists(body_mesh): + return + + # 执行身体关节定位 + cmds.SGBodyJointsLocator() + + # 执行主要轴向修正 + cmds.SGMainAmendAxis() + + # 执行蒙皮轴向修正 + cmds.SGSkinAmendAxis() + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_set_body_neutral_joint_translation() \ No newline at end of file diff --git a/scripts/Reference/SGSetColor.py b/scripts/Reference/SGSetColor.py new file mode 100644 index 0000000..a661814 --- /dev/null +++ b/scripts/Reference/SGSetColor.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/01 +""" + +import maya.cmds as cmds + +def sg_set_color(object_name, rgb, r, g, b, index, enabled): + """ + 设置对象的显示颜色 + Args: + object_name (str): 对象名称 + rgb (int): 是否使用RGB颜色模式 + r (int): 红色值 (0-255) + g (int): 绿色值 (0-255) + b (int): 蓝色值 (0-255) + index (int): 颜色索引值 + enabled (int): 是否启用颜色覆盖 + """ + # 检查对象是否存在 + if cmds.objExists(object_name): + # 设置颜色覆盖属性 + cmds.setAttr(f"{object_name}.ove", enabled) + cmds.setAttr(f"{object_name}.overrideColor", index) + cmds.setAttr(f"{object_name}.ovrgbf", rgb) + + # 将RGB值转换为0-1范围 + red = r / 255.0 + green = g / 255.0 + blue = b / 255.0 + + # 设置RGB颜色 + cmds.setAttr(f"{object_name}.ovrgb", red, green, blue, type="float3") + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGSetHeadNeutralJointTranslation.py b/scripts/Reference/SGSetHeadNeutralJointTranslation.py new file mode 100644 index 0000000..5438379 --- /dev/null +++ b/scripts/Reference/SGSetHeadNeutralJointTranslation.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_set_head_neutral_joint_translation(): + """ + 设置头部模型的关节中性位置 + """ + # 获取所需的网格模型 + head_mesh = cmds.SGGetMeshes(m=0) + teeth_mesh = cmds.SGGetMeshes(m=1) + eye_left_mesh = cmds.SGGetMeshes(m=3) + eye_right_mesh = cmds.SGGetMeshes(m=4) + + # 检查必要的模型是否都存在 + if not all(cmds.objExists(mesh) for mesh in [head_mesh, teeth_mesh, eye_left_mesh, eye_right_mesh]): + return + + # 获取关节信息 + json_file = cmds.SGDescriptor(ti="jointsInfo") + object_list = cmds.SGReadJson(f=json_file, k="head", t="object") + + namespace = "DHIhead:" + + # 如果有关节信息,开始处理 + if object_list: + count = len(object_list) + + # 初始化进度条 + cmds.SGProgressBar(sp=True) + + # 创建临时定位器 + locator = "MetaHumanLocatorTemp" + if not cmds.objExists(locator): + cmds.spaceLocator(n=locator) + + # 设置进度条 + cmds.SGProgressBar(max=count) + cmds.SGProgressBar(t="Set Head Joint Translation...") + + # 获取中性关节位置 + pos = cmds.SGGetNeutralJointTranslations(h=True) + + # 检查数据一致性 + if count != len(pos) // 3: + raise RuntimeError("count error......") + + # 处理每个关节 + for i in range(count): + # 更新进度条 + cmds.SGProgressBar(apr=1) + + # 获取关节信息 + joints = cmds.SGReadJson(d=object_list[i], k="joint", t="string") + + # 处理命名空间 + joint = f"{namespace}{joints[0]}" if cmds.namespace(exists=namespace) else joints[0] + + # 检查是否需要定位 + enable = cmds.SGReadJson(d=object_list[i], k="locate", t="bool")[0] + + if enable: + # 设置定位器位置 + x = pos[i*3] + y = pos[i*3+1] + z = pos[i*3+2] + cmds.setAttr(f"{locator}.t", x, y, z, type="float3") + + # 创建并删除点约束 + constraint = cmds.pointConstraint(locator, joint, weight=1) + cmds.delete(constraint) + + # 清理和刷新 + cmds.delete(locator) + cmds.SGRefreshNeutralJointTranslation() + cmds.SGProgressBar(ep=True) + cmds.refresh() + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_set_head_neutral_joint_translation() \ No newline at end of file diff --git a/scripts/Reference/SGStandardizedNaming.py b/scripts/Reference/SGStandardizedNaming.py new file mode 100644 index 0000000..ef8b94f --- /dev/null +++ b/scripts/Reference/SGStandardizedNaming.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/20 +""" + +import maya.cmds as cmds + +def sg_standardized_naming(): + """ + 标准化所有网格的命名 + 将所有网格重命名为标准名称 + """ + # 获取所有标准网格名称 + meshes = cmds.SGGetMeshes() + + # 遍历每个网格索引 + for i in range(len(meshes)): + # 获取当前网格 + mesh = cmds.SGGetMeshes(m=i) + + # 如果网格存在,进行重命名 + if cmds.objExists(mesh): + # 重命名网格并更新引用 + new_name = cmds.rename(mesh, meshes[i]) + cmds.SGSetMeshes(m=i, value=new_name) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_standardized_naming() \ No newline at end of file diff --git a/scripts/Reference/SGSupplementMeshes.py b/scripts/Reference/SGSupplementMeshes.py new file mode 100644 index 0000000..89b6329 --- /dev/null +++ b/scripts/Reference/SGSupplementMeshes.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/04/01 +""" + +import maya.cmds as cmds + +def sg_supplement_meshes(): + """ + 补充和变形各个LOD级别的网格 + """ + # 遍历8个LOD级别 + for i in range(8): + # 获取当前LOD级别的网格列表 + lod_list = cmds.SGGetMeshes(lod=i) + head = cmds.SGGetMeshes(m=lod_list[0]) + deformed = cmds.SGGetMeshes(m=0) + + if cmds.objExists(head): + # 准备源模型 + source = cmds.SGGetMeshes(cm=0) + source_buffer = f"{source}_buffer" + source_buffer = source_buffer[1:] # 移除第一个字符 + source_buffer = cmds.rename(source, source_buffer) + + # 设置进度条标题 + title = f"[LOD{i}] Please be patient and wait..." + if cmds.SGDescriptor(l=True) == "ZH": + title = f"[LOD{i}] 请耐心等待..." + + # 初始化进度条 + cmds.SGProgressBar(sp=True) + cmds.SGProgressBar(t=title) + + # 初始化网格列表 + eye_mesh = [] + eye = [] + mouth_mesh = [] + + # 读取顶点信息 + json_file = cmds.SGDescriptor(ti="vertexsInfo") + eye_indices = cmds.SGReadJson(f=json_file, k="headEye", t="int") + mouth_indices = cmds.SGReadJson(f=json_file, k="headMouth", t="int") + + # 处理每个网格 + for j in range(1, len(lod_list)): + if lod_list[j] < 50: + mesh = cmds.SGGetMeshes(m=lod_list[j]) + if not cmds.objExists(mesh): + new_mesh = cmds.SGGetMeshes(cm=lod_list[j]) + if cmds.objExists(new_mesh): + # 根据名称分类网格 + if any(x in new_mesh for x in ["eyeshell", "eyelashes", "eyeEdge", "cartilage"]): + eye_mesh.append(new_mesh) + elif any(x in new_mesh for x in ["eyeLeft", "eyeRight"]): + eye.append(new_mesh) + elif any(x in new_mesh for x in ["teeth", "saliva"]): + mouth_mesh.append(new_mesh) + + # 设置进度条 + cmds.SGProgressBar(max=3) + + # 应用RBF变形器 + cmds.SGProgressBar(apr=1) + if eye_mesh: + cmds.SGRBFDeformer( + r=0.01, + rbf=1, + m=[source_buffer, deformed], + pi=eye_indices, + t=eye_mesh + ) + + cmds.SGProgressBar(apr=1) + if eye: + cmds.SGRBFDeformer( + r=0.01, + rbf=1, + d="off", + m=[source_buffer, deformed], + pi=eye_indices, + t=eye + ) + + cmds.SGProgressBar(apr=1) + if mouth_mesh: + cmds.SGRBFDeformer( + r=0.01, + rbf=1, + d="off", + m=[source_buffer, deformed], + pi=mouth_indices, + t=mouth_mesh + ) + + # 清理和结束 + cmds.delete(source_buffer) + cmds.SGProgressBar(ep=True) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_supplement_meshes() \ No newline at end of file diff --git a/scripts/Reference/SGTransferMaps.py b/scripts/Reference/SGTransferMaps.py new file mode 100644 index 0000000..e88a7a6 --- /dev/null +++ b/scripts/Reference/SGTransferMaps.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds + +def sg_transfer_maps(): + """ + 传递所有LOD级别网格的材质映射 + """ + # 处理头部相关网格的LOD级别 + for i in range(1, 8): + # 获取当前LOD级别的网格索引列表 + mesh_indices = cmds.SGGetMeshes(lod=i) + + # 处理前9个网格 + for j in range(9): + mesh = cmds.SGGetMeshes(m=j) + if cmds.objExists(mesh): + # 获取网格类型前缀 + mesh_type = mesh.split('_')[0] + + # 查找相同类型的LOD网格 + for m in mesh_indices: + lod_mesh = cmds.SGGetMeshes(i=m) + if lod_mesh.startswith(mesh_type): + lod = cmds.SGGetMeshes(m=m) + # 传递材质 + cmds.transferShadingSets( + mesh, + lod, + sampleSpace=0, + searchMethod=3 + ) + + # 处理身体网格的LOD级别 + body = cmds.SGGetMeshes(m=50) + if cmds.objExists(body): + # 处理身体的3个LOD级别 + for i in range(1, 4): + body_index = 50 + i + lod = cmds.SGGetMeshes(m=body_index) + # 传递材质 + cmds.transferShadingSets( + body, + lod, + sampleSpace=0, + searchMethod=3 + ) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_transfer_maps() \ No newline at end of file diff --git a/scripts/Reference/SGUVTransferVertexOrder.py b/scripts/Reference/SGUVTransferVertexOrder.py new file mode 100644 index 0000000..c43855a --- /dev/null +++ b/scripts/Reference/SGUVTransferVertexOrder.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/04/02 +""" + +import maya.cmds as cmds + +def sg_uv_transfer_vertex_order(): + """ + 传递UV顶点顺序 + 在两个选中的对象之间传递UV顶点顺序 + """ + # 获取选中的对象 + sel = cmds.ls(selection=True) + + # 检查是否正好选择了两个对象 + if len(sel) != 2: + raise RuntimeError("Please select two objects...") + return + + # 传递顶点顺序 + cmds.SGSetMeshes(tvo=sel[0], value=sel[1]) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_uv_transfer_vertex_order() \ No newline at end of file diff --git a/scripts/Reference/SGUnbindSkinCluster.py b/scripts/Reference/SGUnbindSkinCluster.py new file mode 100644 index 0000000..d335095 --- /dev/null +++ b/scripts/Reference/SGUnbindSkinCluster.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2023/08/08 +""" + +import maya.cmds as cmds +import maya.mel as mel + +def sg_unbind_skin_cluster(): + """ + 解除所有网格的蒙皮绑定 + 遍历前54个网格,如果存在蒙皮变形器则解除绑定 + """ + # 遍历所有网格 + for i in range(54): + # 获取网格 + mesh = cmds.SGGetMeshes(m=i) + + if cmds.objExists(mesh): + # 查找关联的蒙皮变形器 + skin_cluster = mel.eval(f'findRelatedSkinCluster "{mesh}"') + + # 如果存在蒙皮变形器,解除绑定 + if cmds.objExists(skin_cluster): + cmds.skinCluster(mesh, edit=True, unbind=True) + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_unbind_skin_cluster() \ No newline at end of file diff --git a/scripts/Reference/SGUpdateCtrl.py b/scripts/Reference/SGUpdateCtrl.py new file mode 100644 index 0000000..03ab57a --- /dev/null +++ b/scripts/Reference/SGUpdateCtrl.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/10/28 +""" + +import maya.cmds as cmds + +def sg_update_ctrl(json_file, index): + """ + 更新控制器的属性到JSON文件 + Args: + json_file (str): JSON文件路径 + index (int): 对象索引 + """ + # 获取当前选择的对象 + sel_list = cmds.ls(selection=True) + + # 读取JSON文件中的对象列表 + object_list = cmds.SGReadJson(f=json_file, t="object") + + # 检查JSON对象数量是否足够 + if len(object_list) < 61: + raise RuntimeError("Insufficient number of JSON file objects.") + return + + # 定义需要检查的属性列表 + attrs = [ + "translateX", "translateY", "translateZ", + "rotateX", "rotateY", "rotateZ" + ] + + # 初始化数据字典 + data = "{}" + + # 处理每个选中的对象 + for sel in sel_list: + # 获取对象的可键控属性 + attributes = cmds.listAttr(sel, keyable=True) + + # 检查每个目标属性 + for attr in attrs: + attribute = f"{sel}.{attr}" + if attr in attributes: + # 获取属性值 + value = cmds.getAttr(attribute) + + # 如果值超过阈值,记录到数据中 + if value > 0.001 or value < -0.001: + data = cmds.SGWriteJson( + d=data, + k=attribute, + t="double", + value=value + ) + + # 更新对象列表中的数据 + object_list[index] = data + + # 将更新后的数据写回JSON文件 + cmds.SGWriteJson( + of=json_file, + sf=json_file, + t="object", + value=object_list + ) + +# 如果直接运行此脚本 +if __name__ == '__main__': + pass \ No newline at end of file diff --git a/scripts/Reference/SGUpdatePlugin.py b/scripts/Reference/SGUpdatePlugin.py new file mode 100644 index 0000000..638a9e2 --- /dev/null +++ b/scripts/Reference/SGUpdatePlugin.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +版权所有: 深圳时光科技有限公司 +联系方式: q.100@qq.com +创建日期: 2024/03/20 +""" + +import os +import maya.cmds as cmds + +def sg_unload_plugin(): + """ + 卸载当前Maya版本的插件 + """ + # 获取Maya版本 + version = cmds.about(version=True) + plugin_name = f"SuperRiggingEditor{version}" + + # 如果插件已加载,则卸载 + if cmds.pluginInfo(plugin_name, query=True, loaded=True): + cmds.unloadPlugin(plugin_name) + +def sg_update_plugin(): + """ + 更新插件文件 + 从更新文件夹复制新版本插件到对应的插件目录 + """ + # 获取更新文件夹路径 + sg_path = os.environ.get("SG_PATH") + update_folder = os.path.join(sg_path, "plug-ins-update") + + # 检查更新文件夹是否存在 + if os.path.exists(update_folder): + # 卸载当前插件 + sg_unload_plugin() + + # 支持的Maya版本列表 + versions = ["2018", "2019", "2020", "2022", "2023", "2024"] + + # 处理每个版本的插件 + for version in versions: + # 构建路径 + update_path = os.path.join(sg_path, "plug-ins-update", version) + target_path = os.path.join(sg_path, "plug-ins", version) + + # 获取需要更新的插件文件列表 + if os.path.exists(update_path): + plugins = [f for f in os.listdir(update_path) if f.endswith('.mll')] + + # 更新每个插件 + for plugin in plugins: + new_file = os.path.join(update_path, plugin) + old_file = os.path.join(target_path, plugin) + + try: + # 复制新文件到目标位置 + if os.path.exists(new_file): + os.replace(old_file, new_file) + print(f"{old_file} update succeeded...") + os.remove(new_file) + except Exception as e: + print(f"{old_file} update failed: {str(e)}") + + # 删除空的更新目录 + try: + os.rmdir(update_path) + except: + pass + + # 删除空的更新主目录 + try: + os.rmdir(update_folder) + except: + pass + +# 如果直接运行此脚本 +if __name__ == '__main__': + sg_update_plugin() \ No newline at end of file diff --git a/scripts/Reference/userSetup.py b/scripts/Reference/userSetup.py new file mode 100644 index 0000000..e51539c --- /dev/null +++ b/scripts/Reference/userSetup.py @@ -0,0 +1,9 @@ +from maya import cmds, mel, utils + +def SuperRiggingInstallations(): + if not cmds.about(batch=True): + mel.eval("SGUpdatePlugin;") + mel.eval("SGCreateMenuItem;") + mel.eval("SGEnableJointOrient;") + +utils.executeDeferred(SuperRiggingInstallations)