Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

[QUAD] Enhancement: Add action in the Workfile Template Builder #6324

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions openpype/hosts/maya/api/pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
register_loader_plugin_path,
register_inventory_action_path,
register_creator_plugin_path,
register_builder_action_path,
deregister_loader_plugin_path,
deregister_inventory_action_path,
deregister_creator_plugin_path,
deregister_builder_action_path,
AVALON_CONTAINER_ID,
)
from openpype.pipeline.load import any_outdated_containers
Expand Down Expand Up @@ -64,6 +66,7 @@
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
BUILDER_PATH = os.path.join(PLUGINS_DIR, "builder")

AVALON_CONTAINERS = ":AVALON_CONTAINERS"

Expand All @@ -90,6 +93,7 @@ def install(self):
register_loader_plugin_path(LOAD_PATH)
register_creator_plugin_path(CREATE_PATH)
register_inventory_action_path(INVENTORY_PATH)
register_builder_action_path(BUILDER_PATH)
self.log.info(PUBLISH_PATH)

self.log.info("Installing callbacks ... ")
Expand Down Expand Up @@ -335,6 +339,7 @@ def uninstall():
deregister_loader_plugin_path(LOAD_PATH)
deregister_creator_plugin_path(CREATE_PATH)
deregister_inventory_action_path(INVENTORY_PATH)
deregister_builder_action_path(BUILDER_PATH)

menu.uninstall()

Expand Down
148 changes: 148 additions & 0 deletions openpype/hosts/maya/api/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
LoaderPlugin,
get_representation_path,
)
from openpype.pipeline.action import BuilderAction
from openpype.pipeline.load import LoadError
from openpype.client import get_asset_by_name
from openpype.pipeline.create import get_subset_name
Expand Down Expand Up @@ -965,3 +966,150 @@ def _organize_containers(nodes, container):
continue
if cmds.getAttr(id_attr) == AVALON_CONTAINER_ID:
cmds.sets(node, forceElement=container)


class ActionBase(BuilderAction):
"""Base class for all actions."""
label = "Action Base"
options = [
qargparse.Integer(
"count",
label="Count",
default=1,
min=1,
help="How many times to load?"
),
qargparse.Double3(
"offset",
label="Position Offset",
help="Offset loaded models for easier selection."
),
qargparse.Boolean(
"attach_to_root",
label="Group imported asset",
default=True,
help="Should a group be created to encapsulate"
" imported representation ?"
)
]

def load(
self,
context,
name=None,
namespace=None,
options=None
):
assert os.path.exists(self.fname), "%s does not exist." % self.fname

asset = context['asset']
subset = context['subset']
settings = get_project_settings(context['project']['name'])
custom_naming = settings['maya']['load']['reference_loader']
loaded_containers = []

if not custom_naming['namespace']:
raise LoadError("No namespace specified in "
"Maya ReferenceLoader settings")
elif not custom_naming['group_name']:
raise LoadError("No group name specified in "
"Maya ReferenceLoader settings")

formatting_data = {
"asset_name": asset['name'],
"asset_type": asset['type'],
"subset": subset['name'],
"family": (
subset['data'].get('family') or
subset['data']['families'][0]
)
}

custom_namespace = custom_naming['namespace'].format(
**formatting_data
)

custom_group_name = custom_naming['group_name'].format(
**formatting_data
)

count = options.get("count") or 1

for c in range(0, count):
namespace = lib.get_custom_namespace(custom_namespace)
group_name = "{}:{}".format(
namespace,
custom_group_name
)

options['group_name'] = group_name

# Offset loaded subset
if "offset" in options:
offset = [i * c for i in options["offset"]]
options["translate"] = offset

self.log.info(options)

self.process(
context=context,
name=name,
namespace=namespace,
options=options
)

# Only containerize if any nodes were loaded by the Loader
nodes = self[:]
if not nodes:
return

ref_node = get_reference_node(nodes, self.log)
container = containerise(
name=name,
namespace=namespace,
nodes=[ref_node],
context=context,
loader=self.__class__.__name__
)
loaded_containers.append(container)
self._organize_containers(nodes, container)
c += 1
namespace = None

return loaded_containers

def process(self, context, name, namespace, options):
"""To be implemented by subclass"""
raise NotImplementedError("Must be implemented by subclass")

def prepare_root_value(self, file_url, project_name):
"""Replace root value with env var placeholder.
Use ${OPENPYPE_ROOT_WORK} (or any other root) instead of proper root
value when storing referenced url into a workfile.
Useful for remote workflows with SiteSync.
Args:
file_url (str)
project_name (dict)
Returns:
(str)
"""
settings = get_project_settings(project_name)
use_env_var_as_root = (settings["maya"]
["maya-dirmap"]
["use_env_var_as_root"])
if use_env_var_as_root:
anatomy = Anatomy(project_name)
file_url = anatomy.replace_root_with_env_key(file_url, '${{{}}}')

return file_url

@staticmethod
def _organize_containers(nodes, container):
# type: (list, str) -> None
"""Put containers in loaded data to correct hierarchy."""
for node in nodes:
id_attr = "{}.id".format(node)
if not cmds.attributeQuery("id", node=node, exists=True):
continue
if cmds.getAttr(id_attr) == AVALON_CONTAINER_ID:
cmds.sets(node, forceElement=container)
3 changes: 3 additions & 0 deletions openpype/hosts/maya/api/workfile_template_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ def create_placeholder(self, placeholder_data):
if parent:
placeholder = cmds.parent(placeholder, selection[0])[0]

if placeholder_data['action'] is None:
placeholder_data.pop('action')

imprint(placeholder, placeholder_data)

# Add helper attributes to keep placeholder info
Expand Down
20 changes: 20 additions & 0 deletions openpype/hosts/maya/plugins/builder/builder_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from maya import cmds

from openpype.pipeline.action import BuilderAction


class ConnectShape(BuilderAction):
"""Connect Shape within containers.
Source container will connect to the target containers, by searching for
matching geometry IDs (cbid).
Source containers are of family; "animation" and "pointcache".
The connection with be done with a live world space blendshape.
"""

label = "Connect Shape"
icon = "link"
color = "white"

def process(self):
self.log.info("Connect Shape")
print(cmds.ls())
18 changes: 16 additions & 2 deletions openpype/modules/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1037,15 +1037,16 @@ def collect_plugin_paths(self):

Returns:
dict: Output is dictionary with keys "publish", "create", "load",
"actions" and "inventory" each containing list of paths.
"actions", "inventory", "builder" each containing a list of paths.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line too long (82 > 79 characters)

"""
# Output structure
output = {
"publish": [],
"create": [],
"load": [],
"actions": [],
"inventory": []
"inventory": [],
"builder": []
}
unknown_keys_by_module = {}
for module in self.get_enabled_modules():
Expand Down Expand Up @@ -1176,6 +1177,19 @@ def collect_inventory_action_paths(self, host_name):
host_name
)

def collect_builder_action_paths(self, host_name):
"""Helper to collect builder action paths from modules.
Args:
host_name (str): For which host are load action meant.
Returns:
list: List of builder action paths.
"""

return self._collect_plugin_paths(
"get_builder_action_paths",
host_name
)

def get_host_module(self, host_name):
"""Find host module by host name.

Expand Down
16 changes: 14 additions & 2 deletions openpype/modules/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class IPluginPaths(OpenPypeInterface):
"""Module has plugin paths to return.

Expected result is dictionary with keys "publish", "create", "load",
"actions" or "inventory" and values as list or string.
"actions", "inventory", "builder" and values as a list or string.
{
"publish": ["path/to/publish_plugins"]
}
Expand Down Expand Up @@ -116,14 +116,26 @@ def get_inventory_action_paths(self, host_name):

Notes:
Default implementation uses 'get_plugin_paths' and always return
all publish plugin paths.
all inventory plugin paths.

Args:
host_name (str): For which host are the plugins meant.
"""

return self._get_plugin_paths_by_type("inventory")

def get_builder_action_paths(self, host_name):
"""Receive builder action paths.
Give addons ability to add builder action plugin paths.
Notes:
Default implementation uses 'get_plugin_paths' and always return
all builder plugin paths.
Args:
host_name (str): For which host are the plugins meant.
"""

return self._get_plugin_paths_by_type("builder")


class ILaunchHookPaths(OpenPypeInterface):
"""Module has launch hook paths to return.
Expand Down
40 changes: 33 additions & 7 deletions openpype/pipeline/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from .constants import (
AVALON_CONTAINER_ID,
AYON_CONTAINER_ID,
HOST_WORKFILE_EXTENSIONS,
HOST_WORKFILE_EXTENSIONS
)

from .mongodb import (
AvalonMongoDB,
AvalonMongoDB
)
from .anatomy import Anatomy

Expand All @@ -25,7 +25,7 @@
register_creator_plugin,
deregister_creator_plugin,
register_creator_plugin_path,
deregister_creator_plugin_path,
deregister_creator_plugin_path
)

from .load import (
Expand All @@ -48,15 +48,15 @@
loaders_from_representation,
get_representation_path,
get_representation_context,
get_repres_contexts,
get_repres_contexts
)

from .publish import (
PublishValidationError,
PublishXmlValidationError,
KnownPublishError,
OpenPypePyblishPluginMixin,
OptionalPyblishPluginMixin,
OptionalPyblishPluginMixin
)

from .actions import (
Expand All @@ -72,7 +72,7 @@
register_inventory_action,
register_inventory_action_path,
deregister_inventory_action,
deregister_inventory_action_path,
deregister_inventory_action_path
)

from .context_tools import (
Expand All @@ -96,6 +96,20 @@
get_current_asset_name,
get_current_task_name
)

from .action import (
BuilderAction,

discover_builder_plugins,
register_builder_action,
register_builder_action_path,
deregister_builder_action,
deregister_builder_action_path,

get_actions_by_name,
action_with_repre_context
)

install = install_host
uninstall = uninstall_host

Expand Down Expand Up @@ -196,7 +210,19 @@
"get_current_asset_name",
"get_current_task_name",

# --- Action ---
"BuilderAction",

"discover_builder_plugins",
"register_builder_action",
"register_builder_action_path",
"deregister_builder_action",
"deregister_builder_action_path",

"get_actions_by_name",
"action_with_repre_context",

# Backwards compatible function names
"install",
"uninstall",
"uninstall"
)
Loading
Loading