Skip to content

Commit

Permalink
Merge pull request #473 from ynput/feature/AY-976_arnold-scene-source…
Browse files Browse the repository at this point in the history
…-raw

Arnold Scene Source Raw - AY-976
  • Loading branch information
antirotor authored May 6, 2024
2 parents d592e56 + a356722 commit f3fec75
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 134 deletions.
2 changes: 1 addition & 1 deletion client/ayon_core/hosts/maya/api/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1299,7 +1299,7 @@ def is_visible(node,
override_enabled = cmds.getAttr('{}.overrideEnabled'.format(node))
override_visibility = cmds.getAttr('{}.overrideVisibility'.format(
node))
if override_enabled and override_visibility:
if override_enabled and not override_visibility:
return False

if parentHidden:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from maya import cmds

from ayon_core.hosts.maya.api import (
lib,
plugin
Expand Down Expand Up @@ -87,16 +89,24 @@ def get_instance_attr_defs(self):

return defs

def create(self, product_name, instance_data, pre_create_data):

from maya import cmds
class CreateArnoldSceneSourceProxy(CreateArnoldSceneSource):
"""Arnold Scene Source Proxy
This product type facilitates working with proxy geometry in the viewport.
"""

identifier = "io.openpype.creators.maya.assproxy"
label = "Arnold Scene Source Proxy"
product_type = "assProxy"
icon = "cube"

def create(self, product_name, instance_data, pre_create_data):
instance = super(CreateArnoldSceneSource, self).create(
product_name, instance_data, pre_create_data
)

instance_node = instance.get("instance_node")

content = cmds.sets(name=instance_node + "_content_SET", empty=True)
proxy = cmds.sets(name=instance_node + "_proxy_SET", empty=True)
cmds.sets([content, proxy], forceElement=instance_node)
cmds.sets([proxy], forceElement=instance_node)
51 changes: 27 additions & 24 deletions client/ayon_core/hosts/maya/plugins/load/load_arnold_standin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
unique_namespace,
get_attribute_input,
maintained_selection,
get_fps_for_current_context
)
from ayon_core.hosts.maya.api.pipeline import containerise
from ayon_core.hosts.maya.api.plugin import get_load_color_for_product_type
Expand All @@ -29,7 +30,13 @@ class ArnoldStandinLoader(load.LoaderPlugin):
"""Load as Arnold standin"""

product_types = {
"ass", "animation", "model", "proxyAbc", "pointcache", "usd"
"ass",
"assProxy",
"animation",
"model",
"proxyAbc",
"pointcache",
"usd"
}
representations = {"ass", "abc", "usda", "usdc", "usd"}

Expand Down Expand Up @@ -95,8 +102,10 @@ def load(self, context, name, namespace, options):
sequence = is_sequence(os.listdir(os.path.dirname(repre_path)))
cmds.setAttr(standin_shape + ".useFrameExtension", sequence)

fps = float(version_attributes.get("fps")) or 25
cmds.setAttr(standin_shape + ".abcFPS", fps)
fps = (
version_attributes.get("fps") or get_fps_for_current_context()
)
cmds.setAttr(standin_shape + ".abcFPS", float(fps))

nodes = [root, standin, standin_shape]
if operator is not None:
Expand Down Expand Up @@ -128,6 +137,18 @@ def _get_proxy_path(self, path):
proxy_path = "/".join([os.path.dirname(path), proxy_basename])
return proxy_basename, proxy_path

def _update_operators(self, string_replace_operator, proxy_basename, path):
cmds.setAttr(
string_replace_operator + ".match",
proxy_basename.split(".")[0],
type="string"
)
cmds.setAttr(
string_replace_operator + ".replace",
os.path.basename(path).split(".")[0],
type="string"
)

def _setup_proxy(self, shape, path, namespace):
proxy_basename, proxy_path = self._get_proxy_path(path)

Expand All @@ -150,16 +171,7 @@ def _setup_proxy(self, shape, path, namespace):
"*.(@node=='{}')".format(node_type),
type="string"
)
cmds.setAttr(
string_replace_operator + ".match",
proxy_basename,
type="string"
)
cmds.setAttr(
string_replace_operator + ".replace",
os.path.basename(path),
type="string"
)
self._update_operators(string_replace_operator, proxy_basename, path)

cmds.connectAttr(
string_replace_operator + ".out",
Expand Down Expand Up @@ -194,18 +206,9 @@ def update(self, container, context):
path = get_representation_path(repre_entity)
proxy_basename, proxy_path = self._get_proxy_path(path)

# Whether there is proxy or so, we still update the string operator.
# Whether there is proxy or not, we still update the string operator.
# If no proxy exists, the string operator won't replace anything.
cmds.setAttr(
string_replace_operator + ".match",
proxy_basename,
type="string"
)
cmds.setAttr(
string_replace_operator + ".replace",
os.path.basename(path),
type="string"
)
self._update_operators(string_replace_operator, proxy_basename, path)

dso_path = path
if os.path.exists(proxy_path):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,23 @@ class CollectArnoldSceneSource(pyblish.api.InstancePlugin):
# Offset to be after renderable camera collection.
order = pyblish.api.CollectorOrder + 0.2
label = "Collect Arnold Scene Source"
families = ["ass"]
families = ["ass", "assProxy"]

def process(self, instance):
objsets = instance.data["setMembers"]
instance.data["members"] = []
for set_member in instance.data["setMembers"]:
if cmds.nodeType(set_member) != "objectSet":
instance.data["members"].extend(self.get_hierarchy(set_member))
continue

for objset in objsets:
objset = str(objset)
members = cmds.sets(objset, query=True)
members = cmds.sets(set_member, query=True)
members = cmds.ls(members, long=True)
if members is None:
self.log.warning("Skipped empty instance: \"%s\" " % objset)
self.log.warning(
"Skipped empty instance: \"%s\" " % set_member
)
continue
if objset.endswith("content_SET"):
instance.data["contentMembers"] = self.get_hierarchy(members)
if objset.endswith("proxy_SET"):
if set_member.endswith("proxy_SET"):
instance.data["proxy"] = self.get_hierarchy(members)

# Use camera in object set if present else default to render globals
Expand All @@ -33,7 +35,7 @@ def process(self, instance):
renderable = [c for c in cameras if cmds.getAttr("%s.renderable" % c)]
if renderable:
camera = renderable[0]
for node in instance.data["contentMembers"]:
for node in instance.data["members"]:
camera_shapes = cmds.listRelatives(
node, shapes=True, type="camera"
)
Expand All @@ -46,18 +48,11 @@ def process(self, instance):
self.log.debug("data: {}".format(instance.data))

def get_hierarchy(self, nodes):
"""Return nodes with all their children.
Arguments:
nodes (List[str]): List of nodes to collect children hierarchy for
Returns:
list: Input nodes with their children hierarchy
"""
"""Return nodes with all their children"""
nodes = cmds.ls(nodes, long=True)
if not nodes:
return []

children = get_all_children(nodes, ignore_intermediate_objects=True)
return list(children.union(nodes))
children = get_all_children(nodes)
# Make sure nodes merged with children only
# contains unique entries
return list(set(nodes + list(children)))
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ class ExtractArnoldSceneSource(publish.Extractor):
families = ["ass"]
asciiAss = False

def process(self, instance):
staging_dir = self.staging_dir(instance)
def _pre_process(self, instance, staging_dir):
file_path = os.path.join(staging_dir, "{}.ass".format(instance.name))

# Mask
Expand Down Expand Up @@ -70,24 +69,38 @@ def process(self, instance):
"mask": mask
}

filenames, nodes_by_id = self._extract(
instance.data["contentMembers"], attribute_data, kwargs
)

if "representations" not in instance.data:
instance.data["representations"] = []

return attribute_data, kwargs

def process(self, instance):
staging_dir = self.staging_dir(instance)
attribute_data, kwargs = self._pre_process(instance, staging_dir)

filenames = self._extract(
instance.data["members"], attribute_data, kwargs
)

self._post_process(
instance, filenames, staging_dir, kwargs["startFrame"]
)

def _post_process(self, instance, filenames, staging_dir, frame_start):
nodes_by_id = self._nodes_by_id(instance[:])
representation = {
"name": "ass",
"ext": "ass",
"files": filenames if len(filenames) > 1 else filenames[0],
"stagingDir": staging_dir,
"frameStart": kwargs["startFrame"]
"frameStart": frame_start
}

instance.data["representations"].append(representation)

json_path = os.path.join(staging_dir, "{}.json".format(instance.name))
json_path = os.path.join(
staging_dir, "{}.json".format(instance.name)
)
with open(json_path, "w") as f:
json.dump(nodes_by_id, f)

Expand All @@ -104,13 +117,68 @@ def process(self, instance):
"Extracted instance {} to: {}".format(instance.name, staging_dir)
)

# Extract proxy.
if not instance.data.get("proxy", []):
return
def _nodes_by_id(self, nodes):
nodes_by_id = defaultdict(list)

for node in nodes:
id = lib.get_id(node)

if id is None:
continue

# Converting Maya hierarchy separator "|" to Arnold separator "/".
nodes_by_id[id].append(node.replace("|", "/"))

return nodes_by_id

def _extract(self, nodes, attribute_data, kwargs):
filenames = []
with lib.attribute_values(attribute_data):
with lib.maintained_selection():
self.log.debug(
"Writing: {}".format(nodes)
)
cmds.select(nodes, noExpand=True)

self.log.debug(
"Extracting ass sequence with: {}".format(kwargs)
)

exported_files = cmds.arnoldExportAss(**kwargs)

for file in exported_files:
filenames.append(os.path.split(file)[1])

self.log.debug("Exported: {}".format(filenames))

return filenames


class ExtractArnoldSceneSourceProxy(ExtractArnoldSceneSource):
"""Extract the content of the instance to an Arnold Scene Source file."""

label = "Extract Arnold Scene Source Proxy"
hosts = ["maya"]
families = ["assProxy"]
asciiAss = True

def process(self, instance):
staging_dir = self.staging_dir(instance)
attribute_data, kwargs = self._pre_process(instance, staging_dir)

filenames, _ = self._duplicate_extract(
instance.data["members"], attribute_data, kwargs
)

self._post_process(
instance, filenames, staging_dir, kwargs["startFrame"]
)

kwargs["filename"] = file_path.replace(".ass", "_proxy.ass")
kwargs["filename"] = os.path.join(
staging_dir, "{}_proxy.ass".format(instance.name)
)

filenames, _ = self._extract(
filenames, _ = self._duplicate_extract(
instance.data["proxy"], attribute_data, kwargs
)

Expand All @@ -125,12 +193,11 @@ def process(self, instance):

instance.data["representations"].append(representation)

def _extract(self, nodes, attribute_data, kwargs):
def _duplicate_extract(self, nodes, attribute_data, kwargs):
self.log.debug(
"Writing {} with:\n{}".format(kwargs["filename"], kwargs)
)
filenames = []
nodes_by_id = defaultdict(list)
# Duplicating nodes so they are direct children of the world. This
# makes the hierarchy of any exported ass file the same.
with lib.delete_after() as delete_bin:
Expand All @@ -147,7 +214,9 @@ def _extract(self, nodes, attribute_data, kwargs):
if not shapes:
continue

duplicate_transform = cmds.duplicate(node)[0]
basename = cmds.duplicate(node)[0]
parents = cmds.ls(node, long=True)[0].split("|")[:-1]
duplicate_transform = "|".join(parents + [basename])

if cmds.listRelatives(duplicate_transform, parent=True):
duplicate_transform = cmds.parent(
Expand All @@ -172,28 +241,7 @@ def _extract(self, nodes, attribute_data, kwargs):
duplicate_nodes.extend(shapes)
delete_bin.append(duplicate_transform)

# Copy cbId to mtoa_constant.
for node in duplicate_nodes:
# Converting Maya hierarchy separator "|" to Arnold
# separator "/".
nodes_by_id[lib.get_id(node)].append(node.replace("|", "/"))

with lib.attribute_values(attribute_data):
with lib.maintained_selection():
self.log.debug(
"Writing: {}".format(duplicate_nodes)
)
cmds.select(duplicate_nodes, noExpand=True)

self.log.debug(
"Extracting ass sequence with: {}".format(kwargs)
)

exported_files = cmds.arnoldExportAss(**kwargs)

for file in exported_files:
filenames.append(os.path.split(file)[1])

self.log.debug("Exported: {}".format(filenames))
nodes_by_id = self._nodes_by_id(duplicate_nodes)
filenames = self._extract(duplicate_nodes, attribute_data, kwargs)

return filenames, nodes_by_id
Loading

0 comments on commit f3fec75

Please sign in to comment.