mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
122 lines
3.1 KiB
Python
122 lines
3.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Library of functions useful for 3dsmax pipeline."""
|
|
import json
|
|
import six
|
|
from pymxs import runtime as rt
|
|
from typing import Union
|
|
import contextlib
|
|
|
|
|
|
JSON_PREFIX = "JSON::"
|
|
|
|
|
|
def imprint(node_name: str, data: dict) -> bool:
|
|
node = rt.getNodeByName(node_name)
|
|
if not node:
|
|
return False
|
|
|
|
for k, v in data.items():
|
|
if isinstance(v, (dict, list)):
|
|
rt.setUserProp(node, k, f'{JSON_PREFIX}{json.dumps(v)}')
|
|
else:
|
|
rt.setUserProp(node, k, v)
|
|
|
|
return True
|
|
|
|
|
|
def lsattr(
|
|
attr: str,
|
|
value: Union[str, None] = None,
|
|
root: Union[str, None] = None) -> list:
|
|
"""List nodes having attribute with specified value.
|
|
|
|
Args:
|
|
attr (str): Attribute name to match.
|
|
value (str, Optional): Value to match, of omitted, all nodes
|
|
with specified attribute are returned no matter of value.
|
|
root (str, Optional): Root node name. If omitted, scene root is used.
|
|
|
|
Returns:
|
|
list of nodes.
|
|
"""
|
|
root = rt.rootnode if root is None else rt.getNodeByName(root)
|
|
|
|
def output_node(node, nodes):
|
|
nodes.append(node)
|
|
for child in node.Children:
|
|
output_node(child, nodes)
|
|
|
|
nodes = []
|
|
output_node(root, nodes)
|
|
return [
|
|
n for n in nodes
|
|
if rt.getUserProp(n, attr) == value
|
|
] if value else [
|
|
n for n in nodes
|
|
if rt.getUserProp(n, attr)
|
|
]
|
|
|
|
|
|
def read(container) -> dict:
|
|
data = {}
|
|
props = rt.getUserPropBuffer(container)
|
|
# this shouldn't happen but let's guard against it anyway
|
|
if not props:
|
|
return data
|
|
|
|
for line in props.split("\r\n"):
|
|
try:
|
|
key, value = line.split("=")
|
|
except ValueError:
|
|
# if the line cannot be split we can't really parse it
|
|
continue
|
|
|
|
value = value.strip()
|
|
if isinstance(value.strip(), six.string_types) and \
|
|
value.startswith(JSON_PREFIX):
|
|
try:
|
|
value = json.loads(value[len(JSON_PREFIX):])
|
|
except json.JSONDecodeError:
|
|
# not a json
|
|
pass
|
|
|
|
data[key.strip()] = value
|
|
|
|
data["instance_node"] = container.name
|
|
|
|
return data
|
|
|
|
|
|
@contextlib.contextmanager
|
|
def maintained_selection():
|
|
previous_selection = rt.getCurrentSelection()
|
|
try:
|
|
yield
|
|
finally:
|
|
if previous_selection:
|
|
rt.select(previous_selection)
|
|
else:
|
|
rt.select()
|
|
|
|
|
|
def get_all_children(parent, node_type=None):
|
|
"""Handy function to get all the children of a given node
|
|
|
|
Args:
|
|
parent (3dsmax Node1): Node to get all children of.
|
|
node_type (None, runtime.class): give class to check for
|
|
e.g. rt.FFDBox/rt.GeometryClass etc.
|
|
|
|
Returns:
|
|
list: list of all children of the parent node
|
|
"""
|
|
def list_children(node):
|
|
children = []
|
|
for c in node.Children:
|
|
children.append(c)
|
|
children = children + list_children(c)
|
|
return children
|
|
child_list = list_children(parent)
|
|
|
|
return ([x for x in child_list if rt.superClassOf(x) == node_type]
|
|
if node_type else child_list)
|