MetaFusion/scripts/BuildBody.py
2025-01-16 00:28:37 +08:00

396 lines
17 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import maya.cmds as cmds
import sys
def body_transformation(gender_mesh):
def add_chain_depth_attribute(joints):
for joint in joints:
if not cmds.attributeQuery('chainDepth', node=joint, exists=True):
cmds.addAttr(joint, longName='chainDepth', attributeType='long', defaultValue=0)
cmds.setAttr(joint + '.chainDepth', keyable=True)
def set_chain_depth_value(joints, value):
for joint in joints:
cmds.setAttr(joint + '.chainDepth', value)
# 获取场景中的所有关节
all_joints = cmds.ls(type="joint")
# 移除"DHIhead:spine_04"层次中的关节(避免头部)
exclude_joints = cmds.ls("DHIhead:spine_04", dag=True, type="joint")
all_joints = list(set(all_joints) - set(exclude_joints))
add_chain_depth_attribute(all_joints)
# 过滤末端关节(没有子关节的关节)
end_joints = [joint for joint in all_joints if not cmds.listRelatives(joint, children=True, type='joint')]
# 将chainDepth属性设置为0所有末端关节
set_chain_depth_value(all_joints, 100)
set_chain_depth_value(end_joints, 0)
parents1 = []
for joint_name in end_joints:
p_joint = cmds.listRelatives(joint_name, parent=True, type="joint")
if p_joint:
children = cmds.listRelatives(p_joint, children=True, type="joint") or []
if all(cmds.getAttr(child + ".chainDepth") == 0 for child in children):
parents1.append(p_joint[0])
set_chain_depth_value(parents1, 1)
#链深度添加 Attr 循环
chainDepth = 1
while parents1:
chainDepth += 1
new_parents = []
for joint_name in parents1:
p_joint = cmds.listRelatives(joint_name, parent=True, type="joint")
if p_joint:
children = cmds.listRelatives(p_joint, children=True, type="joint") or []
if all(cmds.getAttr(child + ".chainDepth") < chainDepth for child in children):
new_parents.append(p_joint[0])
if new_parents:
set_chain_depth_value(new_parents, chainDepth)
parents1 = new_parents
else:
break
j_name = []
j_delta_x = []
j_delta_y = []
j_delta_z = []
# DELTA X 计算
for joint_name in end_joints:
mesh_name = gender_mesh
# 1. 获取关节的世界空间位置
joint_pos = cmds.xform(joint_name, query=True, worldSpace=True, translation=True)
# 2. 找到最近的顶点"NVertex",位于关节位置
closest_point_node = cmds.createNode("closestPointOnMesh")
cmds.connectAttr(mesh_name + ".worldMesh", closest_point_node + ".inMesh")
cmds.setAttr(closest_point_node + ".inPosition", joint_pos[0], joint_pos[1], joint_pos[2])
nearest_vertex_index = cmds.getAttr(closest_point_node + ".closestVertexIndex")
cmds.delete(closest_point_node)
vertex = mesh_name + ".vtx[{}]".format(nearest_vertex_index)
cmds.select(vertex, replace=True)
# 增长选择
for i in range(1):
# 获取当前选择顶点
vertices = cmds.ls(selection=True, flatten=True)
# 将顶点转换为边
edges = []
for vertex in vertices:
edges.extend(cmds.polyListComponentConversion(vertex, fromVertex=True, toEdge=True))
# 选择边
cmds.select(edges)
# 获取当前选择边
edges = cmds.ls(selection=True, flatten=True)
# 将边转换为顶点
vertices = []
for edge in edges:
vertices.extend(cmds.polyListComponentConversion(edge, fromEdge=True, toVertex=True))
# 选择顶点
cmds.select(vertices)
vertices = cmds.ls(selection=True, flatten=True)
# 将顶点添加到列表中
vertex_position_before = []
for vertex in vertices:
position = cmds.pointPosition(vertex, world=True)
vertex_position_before.append(position)
# 3 将目标混合形状节点设置为1
cmds.setAttr("BodyShape.B2", 1)
# 4. 获取NVertex的新位置并找到新位置
vertex_position_after = []
for vertex in vertices:
position = cmds.pointPosition(vertex, world=True)
vertex_position_after.append(position)
delta_vertex = []
for i in range(len(vertex_position_after)):
sub_delta = []
for j in range(3):
sub_delta.append(vertex_position_after[i][j] - vertex_position_before[i][j])
delta_vertex.append(sub_delta)
average_vertex = [sum(x) / len(x) for x in zip(*delta_vertex)]
# 5. 将关节移动到NVertex移动的距离
delta_x = average_vertex[0]
delta_y = average_vertex[1]
delta_z = average_vertex[2]
if not cmds.attributeQuery("delta_x", node=joint_name, exists=True):
cmds.addAttr(joint_name, ln="delta_x", at="double")
if not cmds.attributeQuery("delta_y", node=joint_name, exists=True):
cmds.addAttr(joint_name, ln="delta_y", at="double")
if not cmds.attributeQuery("delta_z", node=joint_name, exists=True):
cmds.addAttr(joint_name, ln="delta_z", at="double")
cmds.setAttr(joint_name + ".delta_x", delta_x)
cmds.setAttr(joint_name + ".delta_y", delta_y)
cmds.setAttr(joint_name + ".delta_z", delta_z)
# 6 重置
cmds.setAttr("BodyShape.B2", 0)
j_name.append(joint_name)
j_delta_x.append(delta_x)
j_delta_y.append(delta_y)
j_delta_z.append(delta_z)
#cmds.move(delta_x, delta_y, delta_z, joint_name, relative=True)
for i in range(len(j_name)):
cmds.move(j_delta_x[i], j_delta_y[i], j_delta_z[i], j_name[i], relative=True)
#修复身体部分
moving_joint = []
target_joint = []
def fix_body(moving_joint, target_joint):
m_pos = cmds.xform(moving_joint, query=True, worldSpace=True, rotatePivot=True)
ta_pos = cmds.xform(target_joint, query=True, worldSpace=True, rotatePivot=True)
child_joints = cmds.listRelatives(moving_joint, c=True, type="joint")
child_positions = []
if child_joints:
for child_joint in child_joints:
child_positions.append(cmds.xform(child_joint, q=True, ws=True, t=True))
# 将"FACIAL_C_LowerLipRotation"关节的平移和旋转枢轴属性设置为与"FACIAL_C_MouthUpper"相同
cmds.xform(moving_joint, translation=ta_pos, rotatePivot=m_pos, worldSpace=True)
if child_joints:
for i, child_joint in enumerate(child_joints):
cmds.xform(child_joint, ws=True, t=child_positions[i])
m_pos = cmds.xform(moving_joint, query=True, worldSpace=True, rotatePivot=True)
ta_pos = cmds.xform(target_joint, query=True, worldSpace=True, rotatePivot=True)
cmds.xform(target_joint, translation=m_pos, rotatePivot=ta_pos, worldSpace=True)
# 过滤chainDepth值为0的关节
CD = 1
lower_CD = 0
maxCD = 15
while CD <= maxCD:
joint_CD = [joint for joint in all_joints if cmds.getAttr(joint + ".chainDepth") == CD]
lower_joints = [joint for joint in all_joints if cmds.getAttr(joint + ".chainDepth") == lower_CD]
for joint_name in joint_CD:
first_place = cmds.xform(joint_name, q=True, ws=True, translation=True)
child_joints = cmds.listRelatives(joint_name, children=True, type="joint")
average_point = []
if child_joints:
target_points = []
for child_joint in child_joints:
if child_joint in lower_joints:
delta_x = cmds.getAttr(child_joint + '.delta_x')
delta_y = cmds.getAttr(child_joint + '.delta_y')
delta_z = cmds.getAttr(child_joint + '.delta_z')
target_points.append([delta_x, delta_y, delta_z])
if child_joint in end_joints:
delta_x = cmds.getAttr(child_joint + '.delta_x')
delta_y = cmds.getAttr(child_joint + '.delta_y')
delta_z = cmds.getAttr(child_joint + '.delta_z')
#print(cmds.getAttr(child_joint + ".chainDepth"))
target_points.append([delta_x, delta_y, delta_z])
target_points.append([delta_x, delta_y, delta_z])
target_points.append([delta_x, delta_y, delta_z])
target_points.append([delta_x, delta_y, delta_z])
target_points.append([delta_x, delta_y, delta_z])
average_point = [sum(x) / len(x) for x in zip(*target_points)]
if not average_point:
average_point = [0, 0, 0]
avg_x = average_point[0]
avg_y = average_point[1]
avg_z = average_point[2]
cmds.move(avg_x, avg_y, avg_z, joint_name, relative=True)
for child_joint in child_joints:
cmds.move(-avg_x, -avg_y, -avg_z, child_joint, relative=True)
pureJoint = joint_name[:-2]
last_letter = joint_name[-2:]
joint_name_corrective = pureJoint+"_correctiveRoot"+last_letter
joint_name_half = pureJoint+"_half"+last_letter
if joint_name_corrective in all_joints:
fix_body(joint_name, joint_name_corrective)
#print(joint_name + " fixed to the " + joint_name_corrective)
if joint_name_half in all_joints:
fix_body(joint_name, joint_name_half)
#print(joint_name + " fixed to the " + joint_name_half)
end_place = cmds.xform(joint_name, q=True, ws=True, translation=True)
delta_x = end_place[0] - first_place[0]
delta_y = end_place[1] - first_place[1]
delta_z = end_place[2] - first_place[2]
if not cmds.attributeQuery("delta_x", node=joint_name, exists=True):
cmds.addAttr(joint_name, ln="delta_x", at="double")
if not cmds.attributeQuery("delta_y", node=joint_name, exists=True):
cmds.addAttr(joint_name, ln="delta_y", at="double")
if not cmds.attributeQuery("delta_z", node=joint_name, exists=True):
cmds.addAttr(joint_name, ln="delta_z", at="double")
cmds.setAttr(joint_name + ".delta_x", delta_x)
cmds.setAttr(joint_name + ".delta_y", delta_y)
cmds.setAttr(joint_name + ".delta_z", delta_z)
lower_CD += 1
CD += 1
#
#
# 将身体原始骨骼转换为新头部骨骼
CD = 15
lower_CD = -1
minCD = 0
# 定义关节名称和命名空间
head_joint = "DHIhead:spine_04"
body_joint = "DHIbody:spine_04"
# 将关节名称和命名空间分开
head_namespace, head_joint_name = head_joint.split(":")
body_namespace, body_joint_name = body_joint.split(":")
# 获取头部关节的子关节列表
head_child_joints = cmds.listRelatives(head_joint, children=True, allDescendents=True,type="joint")
body_child_joints = cmds.listRelatives(body_joint, children=True, allDescendents=True, type="joint")
while CD >= minCD:
joint_CD = [joint for joint in body_child_joints if cmds.getAttr(joint + ".chainDepth") == CD]
for joint_name in joint_CD:
body_namespace, body_joint_name = joint_name.split(":")
# 构建当前关节的关节名称
head_current_joint = "{}:{}".format(head_namespace, body_joint_name)
body_current_joint = "{}:{}".format(body_namespace, body_joint_name)
if cmds.objExists(head_current_joint):
moving_joint = body_current_joint
target_joint = head_current_joint
def move2target_joint():
lower_lip_rotation_pos = cmds.xform(moving_joint, query=True, worldSpace=True, rotatePivot=True)
jaw_pos = cmds.xform(target_joint, query=True, worldSpace=True, rotatePivot=True)
# 获取子关节
child_joints = cmds.listRelatives(moving_joint, c=True, type="joint")
# 存储子关节的初始位置
child_positions = []
if child_joints:
for child_joint in child_joints:
child_positions.append(cmds.xform(child_joint, q=True, ws=True, t=True))
# 将"FACIAL_C_LowerLipRotation"关节的平移和旋转枢轴属性设置为与"FACIAL_C_MouthUpper"相同
cmds.xform(moving_joint, translation=jaw_pos, rotatePivot=lower_lip_rotation_pos, worldSpace=True)
if child_joints:
# 将每个子关节移动回其原始位置
for i, child_joint in enumerate(child_joints):
cmds.xform(child_joint, ws=True, t=child_positions[i])
move2target_joint()
lower_CD -= 1
CD -= 1
fix_body("DHIbody:spine_04", "DHIhead:spine_04")
def bind_skin(gender_mesh):
# 身体
mesh_obj = cmds.ls(gender_mesh)[0]
# 复制网格
duplicated_mesh_obj = cmds.duplicate(mesh_obj)[0]
new_name = gender_mesh + "_duplicate"
cmds.rename(duplicated_mesh_obj, new_name)
bind_obj = cmds.ls(new_name)[0]
cmds.select([bind_obj, "DHIbody:root"])
# 绑定皮肤到网格
skin_cluster = cmds.skinCluster("DHIbody:root", bind_obj)[0]
# 将皮肤方法设置为"经典线性"
cmds.setAttr(skin_cluster + ".skinningMethod", 1)
cmds.select([mesh_obj, bind_obj])
cmds.copySkinWeights(noMirror=True, surfaceAssociation="closestPoint", influenceAssociation=["name", "oneToOne"])
cmds.delete(mesh_obj)
cmds.rename(bind_obj, gender_mesh)
ROOT_DIR = "c:/dna_calibration"
gender=0
gender_mesh = "f_tal_nrw_body_lod0_mesh"
skeleton_file_path = f"{ROOT_DIR}/data/Fem_Body_skeleton.fbx"
def build_body(ROOT_DIR):
#切换性别
if gender == 0:
skeleton_file_path = f"{ROOT_DIR}/data/Fem_Body_skeleton.fbx"
gender_mesh = "f_tal_nrw_body_lod0_mesh"
elif gender == 1:
skeleton_file_path = f"{ROOT_DIR}/data/Ma_Body_skeleton.fbx"
gender_mesh = "m_med_nrw_body_lod0_mesh"
# 将选定的对象重命名为B2
selected_object = cmds.ls(selection=True)
cmds.rename(selected_object, "B2")
# 导入FBX文件
cmds.file(skeleton_file_path, i=True, type="FBX", ignoreVersion=True, ra=True, mergeNamespacesOnClash=False, options="fbx")
cmds.select("B2", replace=True)
# 定义混合形状节点和目标形状名称
blendshape_node = "BodyShape"
target_shape = "|body_rig|body_grp|body_geometry_grp|body_lod0_grp|"+gender_mesh+"|"+gender_mesh+"Shape"
cmds.blendShape(gender_mesh, automatic=True)
cmds.rename("blendShape1", "BodyShape")
cmds.blendShape(blendshape_node, edit=True, target=[(target_shape, 0.0, "B2", 1.0)])
cmds.setAttr("BodyShape.B2", 0)
#cmds.setAttr("B2" + ".visibility", 0)
cmds.delete('B2')
body_transformation(gender_mesh)
# 复制头部网格
body_mesh = gender_mesh
new_mesh = cmds.duplicate(body_mesh)[0]
# 解锁平移属性
cmds.setAttr(new_mesh+'.translateX', lock=False)
cmds.setAttr(new_mesh+'.translateY', lock=False)
cmds.setAttr(new_mesh+'.translateZ', lock=False)
# 将复制网格沿X轴移动75个单位
cmds.move(75, 0, 0, new_mesh, relative=True)
# 将复制网格添加为目标混合形状节点
blend_shape_node = "BodyShape"
cmds.blendShape(blend_shape_node, edit=True, target=(body_mesh, 1, new_mesh, 1.0))
# 将复制网格的目标混合形状权重设置为-1
cmds.setAttr("BodyShape."+new_mesh, -1)
cmds.setAttr("BodyShape.B2", 1)
cmds.delete(gender_mesh+"1")
bind_skin(gender_mesh)
#build_body("c:/dna_calibration")