MetaFusion/Reference/MSLiveLink/DHI/importer.py

561 lines
23 KiB
Python
Raw Normal View History

2025-02-03 22:58:41 +08:00
import os
import time
from shutil import copyfile
import DHI.core.consts as consts
import dna
import dnacalib as dnac
from DHI.characterConfig import CharacterConfig
from DHI.core.maya.skin_weights_maya_handler import SkinWeightsMayaHandler
from DHI.core.util import MayaUtil
from DHI.modules.dna_viewer import DNA, RigConfig, build_rig
from maya import cmds, mel
from .core.progress_bar import ProgressBar
class CharacterImporter:
Instance = None
def __init__(self):
self.character_config = None
CharacterImporter.Instance = self
def process(self, message):
print("Received message for MetaHuman assembly...")
confirm_dialog_response = cmds.confirmDialog(
title="Confirm dialog",
message="Are you sure you want to import MetaHuman character? All unsaved progress will be lost.",
button=["Yes", "No"],
defaultButton="Yes",
cancelButton="No",
dismissString="No",
)
if confirm_dialog_response == "Yes":
print("Importing MetaHuman character...")
self.character_config = CharacterConfig(message["character"])
print(self.character_config)
self.execute()
def execute(self):
try:
start_time = time.time()
ProgressBar.start_progress_bar("Loading character...", 12)
# start new scene
cmds.file(new=True, force=True)
cmds.upAxis(ax=self.character_config.scene_orientation_string_value, rv=True)
# start import process
self.pre_import_character()
self.import_head()
self.namespace_head_joints()
self.import_body_and_parent_constraint()
self.move_body_meshes_to_lod_groups(self.character_config.body_mesh_name)
self.move_flipflops(self.character_config.body_mesh_name)
self.import_flipflops_shader(self.character_config.body_mesh_name)
self.import_body_shader(self.character_config.body_shader_scene_path, self.character_config.body_mesh_name)
self.import_head_shaders()
self.import_head_lights()
self.adjust_head_gui_position()
self.post_import_character()
ProgressBar.end_progress_bar()
print("--- Import finished in %s seconds ---" % (time.time() - start_time))
except Exception as ex:
print("Error building scene", ex)
ProgressBar.end_progress_bar()
def transform_dna(self, reader):
"""
Apply dna rotation and translation to given DNA reader.
:param reader:
:return:
"""
calibrated = dnac.DNACalibDNAReader(reader)
rotate = dnac.RotateCommand(self.character_config.scene_orientation, [0.0, 0.0, 0.0])
rotate.run(calibrated)
translate = dnac.TranslateCommand(self.character_config.scene_flipflop_offset)
translate.run(calibrated)
path = os.path.join(self.character_config.dna_path_dir, "character.rotate.dna")
self.save_dna(calibrated, path)
return DNA(path)
def save_dna(self, reader, path):
stream = dna.FileStream(
path,
dna.FileStream.AccessMode_Write,
dna.FileStream.OpenMode_Binary,
)
writer = dna.BinaryStreamWriter(stream)
writer.setFrom(reader)
writer.write()
if not dna.Status.isOk():
status = dna.Status.get()
raise RuntimeError(f"Error saving DNA: {status.message}")
def pre_import_character(self):
ProgressBar.start_progress_bar("Creating DNA backup...", 1)
os.environ["PROJECT_ROOT"] = self.character_config.workspace_dir
self.backup_dna_with_geometry()
def backup_dna_with_geometry(self):
print("Backup DNA with geometry started...")
rl_dna_path = self.resolve_rldna_path(self.character_config.dna_path)
copyfile(self.character_config.dna_path, rl_dna_path)
print("Backup DNA with geometry finished...")
def resolve_rldna_path(self, dna_path):
"""
returns a string with _rl as a sufix
"""
return dna_path.replace(".dna", "") + "_rl.dna"
def scene_cleanup(self):
"""Remove sufficient elements from assembled scene"""
# remove sufficient meshes
resolved_body_mesh_name = self.character_config.body_mesh_name.replace("_body", "")
print("Removing sufficient objects")
if cmds.objExists("ROM_root"):
cmds.delete("ROM_root")
if cmds.objExists(resolved_body_mesh_name + "_lod0"):
cmds.delete(resolved_body_mesh_name + "_lod0")
if cmds.objExists(resolved_body_mesh_name + "_lod1"):
cmds.delete(resolved_body_mesh_name + "_lod1")
if cmds.objExists(resolved_body_mesh_name + "_lod2"):
cmds.delete(resolved_body_mesh_name + "_lod2")
if cmds.objExists(resolved_body_mesh_name + "_lod3"):
cmds.delete(resolved_body_mesh_name + "_lod3")
if cmds.objExists(resolved_body_mesh_name + "_body_uExport"):
cmds.delete(resolved_body_mesh_name + "_body_uExport")
# remove animation joint chain0
if cmds.objExists("anim:root"):
cmds.delete("anim:root")
# remove unused layers
print("Removing unused layers...")
if cmds.objExists(resolved_body_mesh_name + "_LOD0_layer"):
cmds.delete(resolved_body_mesh_name + "_LOD0_layer")
if cmds.objExists(resolved_body_mesh_name + "_LOD1_layer"):
cmds.delete(resolved_body_mesh_name + "_LOD1_layer")
if cmds.objExists(resolved_body_mesh_name + "_LOD2_layer"):
cmds.delete(resolved_body_mesh_name + "_LOD2_layer")
if cmds.objExists(resolved_body_mesh_name + "_LOD3_layer"):
cmds.delete(resolved_body_mesh_name + "_LOD3_layer")
if cmds.objExists(resolved_body_mesh_name + "_lod0_layer"):
cmds.delete(resolved_body_mesh_name + "_lod0_layer")
if cmds.objExists(resolved_body_mesh_name + "_lod1_layer"):
cmds.delete(resolved_body_mesh_name + "_lod1_layer")
if cmds.objExists(resolved_body_mesh_name + "_lod2_layer"):
cmds.delete(resolved_body_mesh_name + "_lod2_layer")
if cmds.objExists(resolved_body_mesh_name + "_lod3_layer"):
cmds.delete(resolved_body_mesh_name + "_lod3_layer")
# remove unused body group and meshes
if cmds.objExists("export_geo_GRP"):
cmds.delete("export_geo_GRP")
def create_joints_group_and_move_joints(self):
cmds.group(parent="rig", empty=True, name="joints_grp")
cmds.parent("DHIhead:spine_04", "joints_grp")
cmds.parent("DHIbody:root", "joints_grp")
if cmds.objExists("root_drv"):
cmds.parent("root_drv", "joints_grp")
def post_import_character(self):
ProgressBar.step_progress_bar("Assembly done... Cleaning up the scene")
print("Post import reached...")
MayaUtil.create_workspace(self.character_config.workspace_dir)
MayaUtil.remove_geometry_from_dna(self.resolve_rldna_path(self.character_config.dna_path))
self.remove_unknown_plugins()
self.scene_cleanup()
print("Moving body to world space...")
cmds.parent("root_drv", world=True)
if cmds.objExists("Skeletons"):
cmds.delete("Skeletons")
cmds.select("root")
body_joint_names = cmds.listRelatives(allDescendents=True, type="joint")
body_joint_names.append("root")
body_joints = []
for maya_joint in body_joint_names:
# get joint node
joint_nodes = cmds.ls(maya_joint, type="joint")
body_joints.append(joint_nodes[0])
print("Creating control sets...")
self.create_facial_controls_set()
self.create_body_joints_set(body_joint_names)
self.add_joints_to_namespace(consts.BODY_NAMESPACE, body_joints)
cmds.group(world=True, empty=True, name="rig")
cmds.parent("head_grp", "rig")
self.create_joints_group_and_move_joints()
if cmds.objExists("rig_setup_GRP"):
cmds.rename("rig_setup_GRP", "rig_setup_grp")
cmds.parent("rig_setup_grp", "rig")
cmds.group(parent="rig", empty=True, name="body_grp")
cmds.group(parent="body_grp", empty=True, name="geometry_grp")
cmds.parent("body_lod0_grp", "body_grp|geometry_grp")
cmds.parent("body_lod1_grp", "body_grp|geometry_grp")
cmds.parent("body_lod2_grp", "body_grp|geometry_grp")
cmds.parent("body_lod3_grp", "body_grp|geometry_grp")
MayaUtil.delete_history(consts.MESH_NAMES)
MayaUtil.save_maya_scene(
self.character_config.dna_path_dir + "/" + self.character_config.character_name + "_full_rig",
file_type="mayaBinary",
)
def get_gui_translation(self, gender, height):
val = "_".join([gender, height])
if val in consts.GUI_TRANSLATIONS:
if consts.GUI_TRANSLATIONS[val] is not None:
return [
consts.GUI_TRANSLATIONS[val][0],
consts.GUI_TRANSLATIONS[val][1],
consts.GUI_TRANSLATIONS[val][2],
]
else:
return None
else:
return None
def get_gui_scale(self, gender, height):
val = "_".join([gender, height])
if val in consts.GUI_SCALES:
return consts.GUI_SCALES[val]
else:
return None
def adjust_head_gui_position(self):
ProgressBar.step_progress_bar("Adjusting head gui position...")
print("Adjusting head gui position...")
resolved_translation = self.get_gui_translation(self.character_config.gender, self.character_config.height)
resolved_scale = self.get_gui_scale(self.character_config.gender, self.character_config.height)
if cmds.objExists("GRP_faceGUI"):
if resolved_translation:
cmds.select(clear=True)
cmds.xform("CTRL_faceGUI", translation=resolved_translation, relative=True)
if resolved_scale:
cmds.select(clear=True)
cmds.select("GRP_faceGUI", add=True)
cmds.scale(resolved_scale, resolved_scale, resolved_scale)
cmds.xform("CTRL_faceGUI", ws=True, rp=[0, 0, 0])
cmds.xform("CTRL_faceGUI", ro=self.character_config.scene_orientation, relative=True)
cmds.xform("GRP_convergenceGUI", translation=[0.0, 0.0, 0.0], relative=True)
def create_body_joints_set(self, body_joints):
cmds.select(clear=True)
for bj in body_joints:
if cmds.objExists(bj):
cmds.select(bj, add=True)
cmds.sets(n="Body joints")
def create_facial_controls_set(self):
file_stream = dna.FileStream(
str(self.character_config.dna_path),
dna.FileStream.AccessMode_Read,
dna.FileStream.OpenMode_Binary,
)
reader = dna.BinaryStreamReader(file_stream, dna.DataLayer_All)
reader.read()
cmds.select(clear=True)
for i in range(reader.getGUIControlCount()):
control_name = reader.getGUIControlName(i).split(".")[0]
if cmds.objExists(control_name):
cmds.select(control_name, add=True)
if cmds.objExists("CTRL_C_eye"):
cmds.select("CTRL_C_eye", add=True)
if cmds.objExists("CTRL_rigLogicSwitch"):
cmds.select("CTRL_rigLogicSwitch", add=True)
if cmds.objExists("CTRL_lookAtSwitch"):
cmds.select("CTRL_lookAtSwitch", add=True)
if cmds.objExists("CTRL_C_eyesAim"):
cmds.select("CTRL_C_eyesAim", add=True)
if cmds.objExists("CTRL_C_neck_swallow"):
cmds.select("CTRL_C_neck_swallow", add=True)
if cmds.objExists("CTRL_L_eyeAim"):
cmds.select("CTRL_L_eyeAim", add=True)
if cmds.objExists("CTRL_R_eyeAim"):
cmds.select("CTRL_R_eyeAim", add=True)
if cmds.objExists("CTRL_convergenceSwitch"):
cmds.select("CTRL_convergenceSwitch", add=True)
if cmds.objExists("CTRL_neckCorrectivesMultiplyerU"):
cmds.select("CTRL_neckCorrectivesMultiplyerU", add=True)
if cmds.objExists("CTRL_neckCorrectivesMultiplyerM"):
cmds.select("CTRL_neckCorrectivesMultiplyerM", add=True)
if cmds.objExists("CTRL_neckCorrectivesMultiplyerD"):
cmds.select("CTRL_neckCorrectivesMultiplyerD", add=True)
if cmds.objExists("CTRL_faceGUIfollowHead"):
cmds.select("CTRL_faceGUIfollowHead", add=True)
if cmds.objExists("CTRL_eyesAimFollowHead"):
cmds.select("CTRL_eyesAimFollowHead", add=True)
cmds.sets(n="FacialControls")
def move_body_meshes_to_lod_groups(self, body_mesh_name):
ProgressBar.step_progress_bar("Organizing body meshes to LOD groups...")
try:
# create layer groups
print("Creating layer groups...")
cmds.group(parent="geometry_grp", empty=True, name="body_lod0_grp")
cmds.group(parent="geometry_grp", empty=True, name="body_lod1_grp")
cmds.group(parent="geometry_grp", empty=True, name="body_lod2_grp")
cmds.group(parent="geometry_grp", empty=True, name="body_lod3_grp")
cmds.select(clear=True)
cmds.select("body_lod3_grp", replace=True)
cmds.createDisplayLayer(name="body_lod3_layer", noRecurse=True)
cmds.setAttr("body_lod3_layer.visibility", 0)
cmds.select("body_lod2_grp", replace=True)
cmds.createDisplayLayer(name="body_lod2_layer", noRecurse=True)
cmds.setAttr("body_lod2_layer.visibility", 0)
cmds.select("body_lod1_grp", replace=True)
cmds.createDisplayLayer(name="body_lod1_layer", noRecurse=True)
cmds.setAttr("body_lod1_layer.visibility", 0)
cmds.select("body_lod0_grp", replace=True)
cmds.createDisplayLayer(name="body_lod0_layer", noRecurse=True)
cmds.setAttr("body_lod0_layer.visibility", 1)
cmds.select(clear=True)
# move body meshes to world, so we can group them in LOD (no parents allowed)
print("Moving body meshes to world...")
scene_body_mesh_name = self.character_config.body_mesh_name.replace("_body", "")
for i in range(0, 4):
mesh_name = "{}_lod{}_mesh".format(body_mesh_name, str(i))
if cmds.objExists(mesh_name):
cmds.parent(mesh_name, "body_lod" + str(i) + "_grp")
# move combined mesh to LOD_0
resolved_body_mesh_name = (scene_body_mesh_name + "_combined_lod0_mesh")
print("Moving combined mesh to LOD_0 %s" % resolved_body_mesh_name)
if cmds.objExists(resolved_body_mesh_name):
# set default shader
cmds.select(clear=True)
cmds.select(resolved_body_mesh_name, r=True)
mel.eval("sets -e -forceElement initialShadingGroup")
cmds.hide(resolved_body_mesh_name)
cmds.parent(resolved_body_mesh_name, "body_lod0_grp")
except Exception as err:
print("Error moving body meshes to their corresponding LOD groups", err)
pass
def move_flipflops(self, body_mesh_name):
ProgressBar.step_progress_bar("Moving flip flips...")
print("Moving flip flops...")
# move flip flops
flip_flops_meshes = []
for i in range(0, 4):
flip_flops_mesh_name = (body_mesh_name.replace("_body", "") + "_flipflops")
flip_flops_mesh = "{}_lod{}_mesh".format(flip_flops_mesh_name, str(i))
flip_flops_meshes.append((str(i), flip_flops_mesh))
# quick fix for bodies with non-aligned flipflop names
flip_flops_mesh = "f_med_shs_flipflops_lod%s_mesh" % (str(i))
flip_flops_meshes.append((str(i), flip_flops_mesh))
flip_flops_mesh = "OBJ_m_srt_shs_flipflops_lod%s_mesh" % (str(i))
flip_flops_meshes.append((str(i), flip_flops_mesh))
for lod, mesh_name in flip_flops_meshes:
if cmds.objExists(mesh_name):
cmds.parent(mesh_name, "body_lod" + lod + "_grp")
def import_flipflops_shader(self, mesh_name):
ProgressBar.step_progress_bar("Importing flip flops shader...")
print("Importing flip flops shader...")
MESH_SHADER_MAPPING = {}
MESH_SHADER_MAPPING[mesh_name.replace("_body", "") + "_flipflops_lod"] = "flipflop_shader"
MESH_SHADER_MAPPING["f_med_shs_flipflops_lod"] = "flipflop_shader"
MESH_SHADER_MAPPING["OBJ_m_srt_shs_flipflops_lod"] = "flipflop_shader"
MayaUtil.import_shader(self.character_config.flipflops_scene_path, MESH_SHADER_MAPPING)
MayaUtil.set_map_textures(consts.FLIP_FLOP_SHADERS, self.character_config.flipflops_dir_path)
def remove_unknown_plugins(self):
print("Removing unknown plugins...")
unknown_plugins = cmds.unknownPlugin(query=True, list=True)
if unknown_plugins:
for plugin in unknown_plugins:
try:
cmds.unknownPlugin(plugin, remove=True)
except Exception as err:
print(err)
def namespace_head_joints(self):
ProgressBar.step_progress_bar("Namespacing head joints...")
head_joints = [joint for joint in cmds.ls(type="joint")]
self.add_joints_to_namespace(consts.HEAD_NAMESPACE, head_joints)
def import_body_and_parent_constraint(self):
print(f"Importing body scene from path {self.character_config.body_scene_path}")
ProgressBar.step_progress_bar(f"Importing body scene from path {self.character_config.body_scene_path}")
head_joints = [joint for joint in cmds.ls(type="joint")]
# import body to scene
cmds.file(self.character_config.body_scene_path, op="v=0", typ="mayaAscii", i=True)
cmds.select("root")
body_joints = cmds.listRelatives(allDescendents=True, type="joint")
# fetch all mesh names and skin weights and remove mesh clusters
body_mesh_name = self.character_config.body_mesh_name
mesh_names = []
for i in range(0, 4):
# body mesh
mesh_name = "{}_lod{}_mesh".format(body_mesh_name, str(i))
mesh_names.append(mesh_name)
# flip flops mesh
flip_flops_mesh_name = body_mesh_name.replace("_body", "") + ("_flipflops")
flip_flops_mesh = "{}_lod{}_mesh".format(flip_flops_mesh_name, str(i))
mesh_names.append(flip_flops_mesh)
meshes_to_adapt = []
skin_weights = []
swmh = SkinWeightsMayaHandler()
print("Removing skin clusters...")
for mesh_name in mesh_names:
if cmds.objExists(mesh_name):
meshes_to_adapt.append(mesh_name)
skin_weights.append(swmh.get_skin_weights_from_scene(mesh_name))
cmds.delete(str(swmh.get_skin_weights_data(mesh_name)[1]))
print("Moving pelvis...")
cmds.move(0, 0, 2, 'pelvis_drv', r=True)
print("Adapting meshes...")
MayaUtil._adapt_meshes(consts.SCALE, consts.SCALE_PIVOT, [0.0, 0.0, 0.0], mesh_names, consts.TRANSLATE_FACTOR)
print("Moving pivots...")
for mesh_name in mesh_names:
if cmds.objExists(mesh_name):
cmds.move(0, 0, 0, mesh_name + ".scalePivot", mesh_name + ".rotatePivot", absolute=True)
print("Applying skin weights...")
# apply skin weights
for mesh_name, skin_weight in zip(meshes_to_adapt, skin_weights):
swmh.create_skin_cluster(
skin_weight.joints,
mesh_name,
mesh_name + "_skinCluster",
skin_weight.noOfInfluences,
)
swmh.setSkinWeightsToScene(mesh_name, skin_weight)
# add parent constraint for all matching head joints to body joints
body_joints.append("root")
for joint in body_joints:
head_joint = consts.HEAD_NAMESPACE + ":" + joint
if head_joint in head_joints:
cmds.parentConstraint(joint, head_joint, maintainOffset=True)
cmds.scaleConstraint(joint, head_joint, maintainOffset=True)
def add_joints_to_namespace(self, namespace, joints):
print(f"Adding joints to namespace {namespace}...")
for joint in joints:
if cmds.objExists(joint):
cmds.rename(joint, ":" + namespace + ":" + joint.replace("", ""))
def import_head_shaders(self):
ProgressBar.step_progress_bar(f"Importing head shader: {self.character_config.shader_scene_path}")
ProgressBar.step_progress_bar("Importing head shader: %s" % self.character_config.shader_scene_path)
MayaUtil.import_shader(self.character_config.shader_scene_path, consts.MESH_SHADER_MAPPING)
if self.character_config.platform == "Windows":
MayaUtil.resolve_scene_shader_paths(consts.SHADERS, self.character_config.shaders_dir_path)
MayaUtil.set_mask_textures(consts.MASKS, self.character_config.masks_dir_path)
MayaUtil.set_map_textures(consts.COMMON_MAP_INFOS, self.character_config.head_maps_path)
MayaUtil.set_map_textures(consts.MAP_INFOS, self.character_config.maps_dir_path)
MayaUtil.connect_attributes_to_shader(consts.SHADER_ATTRIBUTES_MAPPING)
def import_body_shader(self, shader_scene_path, body_mesh_name):
ProgressBar.step_progress_bar("Importing body shader...")
print("Importing body shader...")
cmds.file(shader_scene_path, op="v=0", typ="mayaAscii", i=True)
for lod_lvl in range(0, 4):
try:
# Apply shader to all meshes based on LOD level
resolved_mesh_name = body_mesh_name + "_lod" + str(lod_lvl) + "_mesh"
cmds.select(resolved_mesh_name, replace=True)
mel.eval("sets -e -forceElement shader_body_shaderSG")
except Exception as ex:
print(f"Error: {ex}")
print("Skipped adding shader for body mesh %s." % lod_lvl)
MayaUtil.set_map_textures(consts.BODY_MAP_INFOS, self.character_config.maps_dir_path)
# does not apply to linux
if self.character_config.platform == "Windows":
MayaUtil.resolve_scene_shader_paths(consts.BODY_SHADERS, self.character_config.shaders_dir_path)
MayaUtil.set_map_textures(consts.COMMON_MAP_INFOS_BODY, self.character_config.head_maps_path)
def import_head_lights(self):
ProgressBar.step_progress_bar("Importing head lights...")
MayaUtil.create_lights(self.character_config.light_scene_path, self.character_config.scene_orientation)
def import_head(self):
ProgressBar.step_progress_bar("Importing head...")
dna = DNA(self.character_config.dna_path)
dna = self.transform_dna(dna.reader)
config = RigConfig(
gui_path=self.character_config.gui_path,
analog_gui_path=self.character_config.ac_path,
aas_path=os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets",
self.character_config.dna_version, "additional_assemble_script.py"),
aas_parameter={"dna": dna, "gender": self.character_config.gender, "height": self.character_config.height},
)
build_rig(dna=dna, config=config)