mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
abstract namespaced functions for extract fbx animation and add fbx loaders in animatin family
This commit is contained in:
parent
ea4ce1b8be
commit
37cefd892c
10 changed files with 102 additions and 64 deletions
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ class CreateRig(plugin.MayaCreator):
|
|||
instance_node = instance.get("instance_node")
|
||||
|
||||
self.log.info("Creating Rig instance set up ...")
|
||||
# TODO:change name (_controls_set -> _rigs_SET)
|
||||
# TODO:change name (_controls_SET -> _rigs_SET)
|
||||
controls = cmds.sets(name=subset_name + "_controls_SET", empty=True)
|
||||
# TODO:change name (_out_SET -> _geo_SET)
|
||||
pointcache = cmds.sets(name=subset_name + "_out_SET", empty=True)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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", [])
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue