abstract namespaced functions for extract fbx animation and add fbx loaders in animatin family

This commit is contained in:
Kayla 2023-09-29 18:26:58 +08:00
parent ea4ce1b8be
commit 37cefd892c
10 changed files with 102 additions and 64 deletions

View file

@ -203,6 +203,9 @@ class FBXExtractor:
path (str): Path to use for export.
"""
# The export requires forward slashes because we need
# to format it into a string in a mel expression
path = path.replace("\\", "/")
with maintained_selection():
cmds.select(members, r=True, noExpand=True)
mel.eval('FBXExport -f "{}" -s'.format(path))

View file

@ -922,7 +922,7 @@ def no_display_layers(nodes):
@contextlib.contextmanager
def namespaced(namespace, new=True):
def namespaced(namespace, new=True, relative_names=None):
"""Work inside namespace during context
Args:
@ -934,6 +934,7 @@ def namespaced(namespace, new=True):
"""
original = cmds.namespaceInfo(cur=True, absoluteName=True)
original_relative_names = cmds.namespace(query=True, relativeNames=True)
if new:
namespace = unique_namespace(namespace)
cmds.namespace(add=namespace)
@ -943,6 +944,8 @@ def namespaced(namespace, new=True):
yield namespace
finally:
cmds.namespace(set=original)
if relative_names is not None:
cmds.namespace(relativeNames=original_relative_names)
@contextlib.contextmanager

View file

@ -20,7 +20,7 @@ class CreateRig(plugin.MayaCreator):
instance_node = instance.get("instance_node")
self.log.info("Creating Rig instance set up ...")
# TODOchange name (_controls_set -> _rigs_SET)
# TODOchange name (_controls_SET -> _rigs_SET)
controls = cmds.sets(name=subset_name + "_controls_SET", empty=True)
# TODOchange name (_out_SET -> _geo_SET)
pointcache = cmds.sets(name=subset_name + "_out_SET", empty=True)

View file

@ -1,4 +1,46 @@
import openpype.hosts.maya.api.plugin
import maya.cmds as cmds
def _process_reference(file_url, name, namespace, options):
"""_summary_
Args:
file_url (str): fileapth of the objects to be loaded
name (str): subset name
namespace (str): namespace
options (dict): dict of storing the param
Returns:
list: list of object nodes
"""
from openpype.hosts.maya.api.lib import unique_namespace
# Get name from asset being loaded
# Assuming name is subset name from the animation, we split the number
# suffix from the name to ensure the namespace is unique
name = name.split("_")[0]
ext = file_url.split(".")[-1]
namespace = unique_namespace(
"{}_".format(name),
format="%03d",
suffix="_{}".format(ext)
)
attach_to_root = options.get("attach_to_root", True)
group_name = options["group_name"]
# no group shall be created
if not attach_to_root:
group_name = namespace
nodes = cmds.file(file_url,
namespace=namespace,
sharedReferenceFile=False,
groupReference=attach_to_root,
groupName=group_name,
reference=True,
returnNewNodes=True)
return nodes
class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
@ -7,7 +49,7 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
families = ["animation",
"camera",
"pointcache"]
representations = ["abc", "fbx"]
representations = ["abc"]
label = "Reference animation"
order = -10
@ -16,44 +58,42 @@ class AbcLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
def process_reference(self, context, name, namespace, options):
import maya.cmds as cmds
from openpype.hosts.maya.api.lib import unique_namespace
cmds.loadPlugin("AbcImport.mll", quiet=True)
# Prevent identical alembic nodes from being shared
# Create unique namespace for the cameras
# Get name from asset being loaded
# Assuming name is subset name from the animation, we split the number
# suffix from the name to ensure the namespace is unique
name = name.split("_")[0]
namespace = unique_namespace(
"{}_".format(name),
format="%03d",
suffix="_abc"
)
attach_to_root = options.get("attach_to_root", True)
group_name = options["group_name"]
# no group shall be created
if not attach_to_root:
group_name = namespace
# hero_001 (abc)
# asset_counter{optional}
path = self.filepath_from_context(context)
file_url = self.prepare_root_value(path,
context["project"]["name"])
nodes = cmds.file(file_url,
namespace=namespace,
sharedReferenceFile=False,
groupReference=attach_to_root,
groupName=group_name,
reference=True,
returnNewNodes=True)
nodes = _process_reference(file_url, name, namespace, options)
# load colorbleed ID attribute
self[:] = nodes
return nodes
class FbxLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
"""Loader to reference an Fbx files"""
families = ["animation",
"camera"]
representations = ["fbx"]
label = "Reference animation"
order = -10
icon = "code-fork"
color = "orange"
def process_reference(self, context, name, namespace, options):
cmds.loadPlugin("fbx4maya.mll", quiet=True)
path = self.filepath_from_context(context)
file_url = self.prepare_root_value(path,
context["project"]["name"])
nodes = _process_reference(file_url, name, namespace, options)
self[:] = nodes
return nodes

View file

@ -29,7 +29,8 @@ class CollectFbxAnimation(pyblish.api.InstancePlugin,
for skeleton_set in skeleton_sets:
skeleton_content = cmds.sets(skeleton_set, query=True)
self.log.debug(
"Collected animated "
f"skeleton data: {skeleton_content}")
"Collected animated skeleton data: {}".format(
skeleton_content
))
if skeleton_content:
instance.data["animated_skeleton"] += skeleton_content

View file

@ -35,5 +35,6 @@ class CollectSkeletonMesh(pyblish.api.InstancePlugin):
if skeleton_mesh_content:
instance.data["skeleton_mesh"] += skeleton_mesh_content
self.log.debug(
"Collected skeleton "
f"mesh Set: {skeleton_mesh_content}")
"Collected skeletonmesh Set: {}".format(
skeleton_mesh_content
))

View file

@ -40,18 +40,15 @@ class ExtractFBXAnimation(publish.Extractor):
fbx_exporter.set_options_from_instance(instance)
out_set_name = next(out for out in out_set)
# temporarily disable namespace
namespace = out_set_name.split(":")[0]
new_out_set = out_set_name.replace(
f"{namespace}:", "")
# Export from the rig's namespace so that the exported
# FBX does not include the namespace but preserves the node
# names as existing in the rig workfile
namespace, relative_out_set = out_set_name.split(":", 1)
cmds.namespace(relativeNames=True)
with namespaced(":" + namespace, new=False) as namespace:
path = path.replace("\\", "/")
fbx_exporter.export(new_out_set, path)
original_relative_names = cmds.namespace(
query=True, relativeNames=True)
if original_relative_names:
cmds.namespace(relativeNames=original_relative_names)
with namespaced(
":" + namespace,
new=False, relative_names=True) as namespace:
fbx_exporter.export(relative_out_set, path)
representations = instance.data.setdefault("representations", [])
representations.append({
@ -62,4 +59,4 @@ class ExtractFBXAnimation(publish.Extractor):
})
self.log.debug(
"Extracted Fbx animation successful to: {0}".format(path))
"Extracted Fbx animation to: {0}".format(path))

View file

@ -32,8 +32,6 @@ class ExtractSkeletonMesh(publish.Extractor,
filename = "{0}.fbx".format(instance.name)
path = os.path.join(staging_dir, filename)
# The export requires forward slashes because we need
# to format it into a string in a mel expression
fbx_exporter = fbx.FBXExtractor(log=self.log)
out_set = instance.data.get("skeleton_mesh", [])
@ -43,7 +41,6 @@ class ExtractSkeletonMesh(publish.Extractor,
fbx_exporter.set_options_from_instance(instance)
# Export
path = path.replace("\\", "/")
fbx_exporter.export(out_set, path)
representations = instance.data.setdefault("representations", [])

View file

@ -68,9 +68,7 @@ class ValidateSkeletonRigOutputIds(pyblish.api.InstancePlugin):
if shapes:
instance_nodes.extend(shapes)
scene_nodes = cmds.ls(type="transform", long=True)
scene_nodes += cmds.ls(type="mesh", long=True)
scene_nodes = set(scene_nodes) - set(instance_nodes)
scene_nodes = cmds.ls(type=("transform", "mesh"), long=True)
scene_nodes_by_basename = defaultdict(list)
for node in scene_nodes:
@ -109,7 +107,7 @@ class ValidateSkeletonRigOutputIds(pyblish.api.InstancePlugin):
for instance_node, matches in invalid_matches.items():
ids = set(get_id(node) for node in matches)
# If there are multiple scene ids matched, and error needs to be
# If there are multiple scene ids matched, an error needs to be
# raised for manual correction.
if len(ids) > 1:
multiple_ids_match.append({"node": instance_node,

View file

@ -24,19 +24,17 @@ class ValidateSkeletonTopGroupHierarchy(pyblish.api.InstancePlugin,
def process(self, instance):
invalid = []
skeleton_mesh_data = instance.data(("skeleton_mesh"), [])
skeleton_mesh_data = instance.data("skeleton_mesh", [])
if skeleton_mesh_data:
invalid = self.get_top_hierarchy(skeleton_mesh_data)
if invalid:
raise PublishValidationError(
"The skeletonMesh_SET includes the object which "
f"is not at the top hierarchy: {invalid}")
"is not at the top hierarchy: {}".format(invalid))
def get_top_hierarchy(self, targets):
non_top_hierarchy_list = []
for target in targets:
long_names = cmds.ls(target, long=True)
for name in long_names:
if len(name.split["|"]) > 2:
non_top_hierarchy_list.append(name)
targets = cmds.ls(targets, long=True) # ensure long names
non_top_hierarchy_list = [
target for target in targets if target.count("|") > 2
]
return non_top_hierarchy_list