From 0d02b9f9562649a2df4ef316e7715dd788c876ac Mon Sep 17 00:00:00 2001 From: Kayla Man Date: Wed, 19 Jun 2024 20:08:46 +0800 Subject: [PATCH] implement ornatrix cache loader --- .../plugins/load/load_ornatrix_cache.py | 130 ++++++++++++++++++ .../plugins/load/load_ornatrix_rig.py | 2 +- .../plugins/publish/collect_ornatrix_cache.py | 6 +- server_addon/maya/server/settings/loaders.py | 7 +- 4 files changed, 137 insertions(+), 8 deletions(-) create mode 100644 server_addon/maya/client/ayon_maya/plugins/load/load_ornatrix_cache.py diff --git a/server_addon/maya/client/ayon_maya/plugins/load/load_ornatrix_cache.py b/server_addon/maya/client/ayon_maya/plugins/load/load_ornatrix_cache.py new file mode 100644 index 0000000000..4281c972a8 --- /dev/null +++ b/server_addon/maya/client/ayon_maya/plugins/load/load_ornatrix_cache.py @@ -0,0 +1,130 @@ +import json +import os +from ayon_core.pipeline import get_representation_path +from ayon_maya.api import lib +from ayon_maya.api.pipeline import containerise +from ayon_maya.api import plugin +from maya import cmds, mel + + +class OxCacheLoader(plugin.Loader): + """Load Ornatrix Cache with one or more Yeti nodes""" + + product_types = {"oxcache", "oxrig"} + representations = {"abc"} + + label = "Load Ornatrix Cache" + order = -9 + icon = "code-fork" + color = "orange" + + def load(self, context, name=None, namespace=None, data=None): + """Loads a .fursettings file defining how to load .abc into + HairGuideFromMesh nodes + + The .fursettings file defines what the node names should be and also + what "cbId" attribute they should receive to match the original source + and allow published looks to also work for Ornatrix rigs and its caches. + + """ + # Build namespace + folder_name = context["folder"]["name"] + if namespace is None: + namespace = self.create_namespace(folder_name) + + # Ensure Onratrix is loaded + if not cmds.pluginInfo("Ornatrix.mll", query=True, loaded=True): + cmds.loadPlugin("Ornatrix.mll", quiet=True) + + path = self.filepath_from_context(context) + settings = self.read_settings(path) + # read the fursettings + nodes = [] + for setting in settings["nodes"]: + nodes.extend(self.create_node(namespace, path, setting)) + + self[:] = nodes + + return containerise( + name=name, + namespace=namespace, + nodes=nodes, + context=context, + loader=self.__class__.__name__ + ) + + def remove(self, container): + + namespace = container["namespace"] + nodes = container["nodes"] + + self.log.info("Removing '%s' from Maya.." % container["name"]) + + nodes = cmds.ls(nodes, long=True) + + try: + cmds.delete(nodes) + except ValueError: + # Already implicitly deleted by Maya upon removing reference + pass + + cmds.namespace(removeNamespace=namespace, deleteNamespaceContent=True) + + def update(self, container, context): + repre_entity = context["representation"] + nodes = container["nodes"] + + path = get_representation_path(repre_entity) + for node in nodes: + if cmds.ls(node, type="HairFromGuidesNode"): + cmds.setAttr(f"{node}.cacheFilePath", path) + + + def switch(self, container, context): + self.update(container, context) + + # helper functions + def create_namespace(self, folder_name): + """Create a unique namespace + Args: + asset (dict): asset information + + """ + + asset_name = "{}_".format(folder_name) + prefix = "_" if asset_name[0].isdigit() else "" + namespace = lib.unique_namespace( + asset_name, + prefix=prefix, + suffix="_" + ) + + return namespace + + def create_node(self, namespace, filepath, node_settings): + nodes = [] + orig_guide_name = node_settings["name"] + orig_shape_name = node_settings["shape"]["name"] + mesh_shape_name = "{}:{}".format(namespace, orig_shape_name) + guide_name = "{}:{}".format(namespace, orig_guide_name) + mesh_shape_node = cmds.ls(guide_name, type="mesh") + if not mesh_shape_node: + mesh_shape_node = cmds.createNode("mesh", name=guide_name) + hair_guide_node = cmds.ls(guide_name, type="HairFromGuidesNode") + if not hair_guide_node: + hair_guide_node = cmds.createNode( + "HairFromGuidesNode", name=guide_name) + + lib.set_id(hair_guide_node, node_settings["cbId"]) + mel.eval(f"OxAddStrandOperator {mesh_shape_name} {guide_name};") + cmds.setAttr(f"{guide_name}.cacheFilePath", filepath) + nodes.append(hair_guide_node) + return nodes + + def read_setting(self, path): + path_no_ext, _ = os.path.splitext(path) + settings_path = f"{path_no_ext}.cachesettings" + with open(settings_path, "r") as fp: + setting_attributes = json.load(fp) + + return setting_attributes diff --git a/server_addon/maya/client/ayon_maya/plugins/load/load_ornatrix_rig.py b/server_addon/maya/client/ayon_maya/plugins/load/load_ornatrix_rig.py index 1046dc5bf1..6797bde6bf 100644 --- a/server_addon/maya/client/ayon_maya/plugins/load/load_ornatrix_rig.py +++ b/server_addon/maya/client/ayon_maya/plugins/load/load_ornatrix_rig.py @@ -54,7 +54,7 @@ class OxRigLoader(plugin.ReferenceLoader): groupName=group_name ) - color = plugin.get_load_color_for_product_type("OxRig") + color = plugin.get_load_color_for_product_type("oxrig") if color is not None: red, green, blue = color cmds.setAttr(group_name + ".useOutlinerColor", 1) diff --git a/server_addon/maya/client/ayon_maya/plugins/publish/collect_ornatrix_cache.py b/server_addon/maya/client/ayon_maya/plugins/publish/collect_ornatrix_cache.py index f11c49da95..9356c05244 100644 --- a/server_addon/maya/client/ayon_maya/plugins/publish/collect_ornatrix_cache.py +++ b/server_addon/maya/client/ayon_maya/plugins/publish/collect_ornatrix_cache.py @@ -18,14 +18,16 @@ class CollectOxCache(plugin.MayaInstancePlugin): for ox_shape in ox_shapes: # Get transform data parent = cmds.listRelatives(ox_shape, parent=True)[0] - transform_data = {"name": parent, "cbId": lib.get_id(parent)} + mesh_shape_data = {"name": parent, "cbId": lib.get_id(parent)} ox_cache_nodes = [ ox_node for ox_node in cmds.listConnections(ox_shape, destination=True) if cmds.nodeType(ox_node) == "HairFromGuidesNode" ] # transfer cache file + if not lib.get_id(ox_shape): + return shape_data = { - "transform": transform_data, + "shape": mesh_shape_data, "name": ox_shapes, "cbId": lib.get_id(ox_shape), "ox_nodes": ox_cache_nodes, diff --git a/server_addon/maya/server/settings/loaders.py b/server_addon/maya/server/settings/loaders.py index 6ae1d7cabf..95d23f0f54 100644 --- a/server_addon/maya/server/settings/loaders.py +++ b/server_addon/maya/server/settings/loaders.py @@ -39,9 +39,7 @@ class ColorsSetting(BaseSettingsModel): (99, 206, 220, 1.0), title="Yeti Cache:") yetiRig: ColorRGBA_uint8 = SettingsField( (0, 205, 125, 1.0), title="Yeti Rig:") - oxCache: ColorRGBA_uint8 = SettingsField( - (156, 234, 195, 1.0), title="Ornatrix Cache:") - oxRig: ColorRGBA_uint8 = SettingsField( + oxrig: ColorRGBA_uint8 = SettingsField( (206, 234, 195, 1.0), title="Ornatrix Rig:") # model: ColorRGB_float = SettingsField( # (0.82, 0.52, 0.12), title="Model:" @@ -253,8 +251,7 @@ DEFAULT_LOADERS_SETTING = { "vrayscene_layer": [255, 150, 12, 1.0], "yeticache": [99, 206, 220, 1.0], "yetiRig": [0, 205, 125, 1.0], - "oxCache": [156, 234, 195, 1.0], - "oxRig": [206, 234, 195, 1.0] + "oxrig": [206, 234, 195, 1.0] # "model": [0.82, 0.52, 0.12], # "rig": [0.23, 0.89, 0.92], # "pointcache": [0.37, 0.82, 0.12],