Updated
This commit is contained in:
parent
7f6860ab12
commit
f91643cc52
62
scripts/Reference/SGAddBlendShape.py
Normal file
62
scripts/Reference/SGAddBlendShape.py
Normal file
@ -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)
|
66
scripts/Reference/SGAutomaticGrouping.py
Normal file
66
scripts/Reference/SGAutomaticGrouping.py
Normal file
@ -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 # 忽略可能的父子关系错误
|
86
scripts/Reference/SGBatchAddBlendShape.py
Normal file
86
scripts/Reference/SGBatchAddBlendShape.py
Normal file
@ -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)
|
42
scripts/Reference/SGBatchDelBlendShape.py
Normal file
42
scripts/Reference/SGBatchDelBlendShape.py
Normal file
@ -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)
|
152
scripts/Reference/SGBindPoseReset.py
Normal file
152
scripts/Reference/SGBindPoseReset.py
Normal file
@ -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)
|
64
scripts/Reference/SGBindSkinCluster.py
Normal file
64
scripts/Reference/SGBindSkinCluster.py
Normal file
@ -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')
|
116
scripts/Reference/SGBlendShapeFindFlipTarget.py
Normal file
116
scripts/Reference/SGBlendShapeFindFlipTarget.py
Normal file
@ -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)
|
75
scripts/Reference/SGBlendShapeFlipTarget.py
Normal file
75
scripts/Reference/SGBlendShapeFlipTarget.py
Normal file
@ -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)
|
79
scripts/Reference/SGBlendShapeMirrorTarget.py
Normal file
79
scripts/Reference/SGBlendShapeMirrorTarget.py
Normal file
@ -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)
|
517
scripts/Reference/SGCloneBlendShape.py
Normal file
517
scripts/Reference/SGCloneBlendShape.py
Normal file
@ -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
|
47
scripts/Reference/SGClothingWeight.py
Normal file
47
scripts/Reference/SGClothingWeight.py
Normal file
@ -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"
|
||||||
|
)
|
44
scripts/Reference/SGCopySkin.py
Normal file
44
scripts/Reference/SGCopySkin.py
Normal file
@ -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"
|
||||||
|
)
|
187
scripts/Reference/SGCreateARKit.py
Normal file
187
scripts/Reference/SGCreateARKit.py
Normal file
@ -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()
|
73
scripts/Reference/SGCreateBlendShape.py
Normal file
73
scripts/Reference/SGCreateBlendShape.py
Normal file
@ -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)
|
27
scripts/Reference/SGCreateBodyCtrl.py
Normal file
27
scripts/Reference/SGCreateBodyCtrl.py
Normal file
@ -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)
|
204
scripts/Reference/SGCreateLOD.py
Normal file
204
scripts/Reference/SGCreateLOD.py
Normal file
@ -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)
|
86
scripts/Reference/SGCreateMenuItem.py
Normal file
86
scripts/Reference/SGCreateMenuItem.py
Normal file
@ -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}")')
|
70
scripts/Reference/SGCreateRL4Node.py
Normal file
70
scripts/Reference/SGCreateRL4Node.py
Normal file
@ -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:<objName>.<attrName>"
|
||||||
|
else:
|
||||||
|
joint_name = "<objName>.<attrName>"
|
||||||
|
|
||||||
|
# 设置各种命名模式
|
||||||
|
anim_multiplier_name = "FRM_WMmultipliers.<objName>_<attrName>"
|
||||||
|
blend_shape_name = "<objName>_blendShapes.<attrName>"
|
||||||
|
control_name = "<objName>.<attrName>"
|
||||||
|
|
||||||
|
# 创建嵌入式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}')
|
36
scripts/Reference/SGCurrentProjectDNA.py
Normal file
36
scripts/Reference/SGCurrentProjectDNA.py
Normal file
@ -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)
|
98
scripts/Reference/SGDefineJointForLOD.py
Normal file
98
scripts/Reference/SGDefineJointForLOD.py
Normal file
@ -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"
|
||||||
|
]
|
106
scripts/Reference/SGDelBlendShape.py
Normal file
106
scripts/Reference/SGDelBlendShape.py
Normal file
@ -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)
|
29
scripts/Reference/SGDeleteJointForLOD.py
Normal file
29
scripts/Reference/SGDeleteJointForLOD.py
Normal file
@ -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)
|
19
scripts/Reference/SGDeleteRL4Node.py
Normal file
19
scripts/Reference/SGDeleteRL4Node.py
Normal file
@ -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)
|
64
scripts/Reference/SGDemoHelp.py
Normal file
64
scripts/Reference/SGDemoHelp.py
Normal file
@ -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')
|
97
scripts/Reference/SGDuplicateTarget.py
Normal file
97
scripts/Reference/SGDuplicateTarget.py
Normal file
@ -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)
|
27
scripts/Reference/SGEditBlendShape.py
Normal file
27
scripts/Reference/SGEditBlendShape.py
Normal file
@ -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
|
679
scripts/Reference/SGEnableJointOrient.py
Normal file
679
scripts/Reference/SGEnableJointOrient.py
Normal file
@ -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]
|
329
scripts/Reference/SGExportFBXWindow.py
Normal file
329
scripts/Reference/SGExportFBXWindow.py
Normal file
@ -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}"')
|
37
scripts/Reference/SGExportSkinCluster.py
Normal file
37
scripts/Reference/SGExportSkinCluster.py
Normal file
@ -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)
|
57
scripts/Reference/SGFastBindSkinCluster.py
Normal file
57
scripts/Reference/SGFastBindSkinCluster.py
Normal file
@ -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 # 忽略导入错误
|
46
scripts/Reference/SGFastUnbindSkinCluster.py
Normal file
46
scripts/Reference/SGFastUnbindSkinCluster.py
Normal file
@ -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)
|
31
scripts/Reference/SGGetBlendShape.py
Normal file
31
scripts/Reference/SGGetBlendShape.py
Normal file
@ -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 ""
|
64
scripts/Reference/SGHelp.py
Normal file
64
scripts/Reference/SGHelp.py
Normal file
@ -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')
|
26
scripts/Reference/SGImportBodyAnim.py
Normal file
26
scripts/Reference/SGImportBodyAnim.py
Normal file
@ -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()
|
26
scripts/Reference/SGImportFaceAnim.py
Normal file
26
scripts/Reference/SGImportFaceAnim.py
Normal file
@ -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()
|
55
scripts/Reference/SGImportSkinCluster.py
Normal file
55
scripts/Reference/SGImportSkinCluster.py
Normal file
@ -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()
|
310
scripts/Reference/SGMesheDetach.py
Normal file
310
scripts/Reference/SGMesheDetach.py
Normal file
@ -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
|
26
scripts/Reference/SGMotionApply.py
Normal file
26
scripts/Reference/SGMotionApply.py
Normal file
@ -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()
|
112
scripts/Reference/SGPose.py
Normal file
112
scripts/Reference/SGPose.py
Normal file
@ -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
|
260
scripts/Reference/SGPresetsSettings.py
Normal file
260
scripts/Reference/SGPresetsSettings.py
Normal file
@ -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()
|
199
scripts/Reference/SGRBFDeformerWindow.py
Normal file
199
scripts/Reference/SGRBFDeformerWindow.py
Normal file
@ -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()
|
70
scripts/Reference/SGRangeBlendShapeAll.py
Normal file
70
scripts/Reference/SGRangeBlendShapeAll.py
Normal file
@ -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
|
60
scripts/Reference/SGRangeBlendShapeSelect.py
Normal file
60
scripts/Reference/SGRangeBlendShapeSelect.py
Normal file
@ -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
|
34
scripts/Reference/SGRebuildTarget.py
Normal file
34
scripts/Reference/SGRebuildTarget.py
Normal file
@ -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
|
95
scripts/Reference/SGRefreshGeoLineEdit.py
Normal file
95
scripts/Reference/SGRefreshGeoLineEdit.py
Normal file
@ -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
|
29
scripts/Reference/SGRefreshNeutralJointTranslation.py
Normal file
29
scripts/Reference/SGRefreshNeutralJointTranslation.py
Normal file
@ -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()
|
45
scripts/Reference/SGRenameBlendShapes.py
Normal file
45
scripts/Reference/SGRenameBlendShapes.py
Normal file
@ -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()
|
104
scripts/Reference/SGReorderBlendShapes.py
Normal file
104
scripts/Reference/SGReorderBlendShapes.py
Normal file
@ -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()
|
47
scripts/Reference/SGRepairJointForLOD.py
Normal file
47
scripts/Reference/SGRepairJointForLOD.py
Normal file
@ -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
|
98
scripts/Reference/SGRepairNormals.py
Normal file
98
scripts/Reference/SGRepairNormals.py
Normal file
@ -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
|
103
scripts/Reference/SGRepairSeams.py
Normal file
103
scripts/Reference/SGRepairSeams.py
Normal file
@ -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()
|
43
scripts/Reference/SGRepairVertexOrder.py
Normal file
43
scripts/Reference/SGRepairVertexOrder.py
Normal file
@ -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()
|
48
scripts/Reference/SGResetBlendShapes.py
Normal file
48
scripts/Reference/SGResetBlendShapes.py
Normal file
@ -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()
|
49
scripts/Reference/SGResetTarget.py
Normal file
49
scripts/Reference/SGResetTarget.py
Normal file
@ -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
|
63
scripts/Reference/SGSaveBlendShape.py
Normal file
63
scripts/Reference/SGSaveBlendShape.py
Normal file
@ -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()
|
68
scripts/Reference/SGSaveBlendShapeMappings.py
Normal file
68
scripts/Reference/SGSaveBlendShapeMappings.py
Normal file
@ -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
|
34
scripts/Reference/SGSetBodyNeutralJointTranslation.py
Normal file
34
scripts/Reference/SGSetBodyNeutralJointTranslation.py
Normal file
@ -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()
|
41
scripts/Reference/SGSetColor.py
Normal file
41
scripts/Reference/SGSetColor.py
Normal file
@ -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
|
88
scripts/Reference/SGSetHeadNeutralJointTranslation.py
Normal file
88
scripts/Reference/SGSetHeadNeutralJointTranslation.py
Normal file
@ -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()
|
33
scripts/Reference/SGStandardizedNaming.py
Normal file
33
scripts/Reference/SGStandardizedNaming.py
Normal file
@ -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()
|
106
scripts/Reference/SGSupplementMeshes.py
Normal file
106
scripts/Reference/SGSupplementMeshes.py
Normal file
@ -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()
|
58
scripts/Reference/SGTransferMaps.py
Normal file
58
scripts/Reference/SGTransferMaps.py
Normal file
@ -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()
|
30
scripts/Reference/SGUVTransferVertexOrder.py
Normal file
30
scripts/Reference/SGUVTransferVertexOrder.py
Normal file
@ -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()
|
33
scripts/Reference/SGUnbindSkinCluster.py
Normal file
33
scripts/Reference/SGUnbindSkinCluster.py
Normal file
@ -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()
|
73
scripts/Reference/SGUpdateCtrl.py
Normal file
73
scripts/Reference/SGUpdateCtrl.py
Normal file
@ -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
|
80
scripts/Reference/SGUpdatePlugin.py
Normal file
80
scripts/Reference/SGUpdatePlugin.py
Normal file
@ -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()
|
9
scripts/Reference/userSetup.py
Normal file
9
scripts/Reference/userSetup.py
Normal file
@ -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)
|
Loading…
Reference in New Issue
Block a user