Update
This commit is contained in:
parent
1983e3260f
commit
7f6860ab12
396
scripts/BuildBody.py
Normal file
396
scripts/BuildBody.py
Normal file
@ -0,0 +1,396 @@
|
|||||||
|
#!/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")
|
Loading…
Reference in New Issue
Block a user