implement ornatrix cache loader

This commit is contained in:
Kayla Man 2024-06-19 20:08:46 +08:00
parent 14f12b4e7a
commit 0d02b9f956
4 changed files with 137 additions and 8 deletions

View file

@ -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

View file

@ -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)

View file

@ -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,

View file

@ -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],