refactor collector

This commit is contained in:
Kayla Man 2023-06-20 19:50:08 +08:00
commit 7938389156
36 changed files with 632 additions and 569 deletions

View file

@ -1,30 +1,27 @@
# -*- 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
import json
from typing import Any, Dict, Union
import six
from openpype.pipeline.context_tools import (
get_current_project_asset,
get_current_project
)
get_current_project, get_current_project_asset,)
from pymxs import runtime as rt
JSON_PREFIX = "JSON::"
def imprint(node_name: str, data: dict) -> bool:
node = rt.getNodeByName(node_name)
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)}')
rt.SetUserProp(node, k, f"{JSON_PREFIX}{json.dumps(v)}")
else:
rt.setUserProp(node, k, v)
rt.SetUserProp(node, k, v)
return True
@ -44,7 +41,7 @@ def lsattr(
Returns:
list of nodes.
"""
root = rt.rootnode if root is None else rt.getNodeByName(root)
root = rt.RootNode if root is None else rt.GetNodeByName(root)
def output_node(node, nodes):
nodes.append(node)
@ -55,16 +52,16 @@ def lsattr(
output_node(root, nodes)
return [
n for n in nodes
if rt.getUserProp(n, attr) == value
if rt.GetUserProp(n, attr) == value
] if value else [
n for n in nodes
if rt.getUserProp(n, attr)
if rt.GetUserProp(n, attr)
]
def read(container) -> dict:
data = {}
props = rt.getUserPropBuffer(container)
props = rt.GetUserPropBuffer(container)
# this shouldn't happen but let's guard against it anyway
if not props:
return data
@ -79,29 +76,25 @@ def read(container) -> dict:
value = value.strip()
if isinstance(value.strip(), six.string_types) and \
value.startswith(JSON_PREFIX):
try:
with contextlib.suppress(json.JSONDecodeError):
value = json.loads(value[len(JSON_PREFIX):])
except json.JSONDecodeError:
# not a json
pass
data[key.strip()] = value
data["instance_node"] = container.name
data["instance_node"] = container.Name
return data
@contextlib.contextmanager
def maintained_selection():
previous_selection = rt.getCurrentSelection()
previous_selection = rt.GetCurrentSelection()
try:
yield
finally:
if previous_selection:
rt.select(previous_selection)
rt.Select(previous_selection)
else:
rt.select()
rt.Select()
def get_all_children(parent, node_type=None):
@ -123,7 +116,7 @@ def get_all_children(parent, node_type=None):
return children
child_list = list_children(parent)
return ([x for x in child_list if rt.superClassOf(x) == node_type]
return ([x for x in child_list if rt.SuperClassOf(x) == node_type]
if node_type else child_list)
@ -182,7 +175,7 @@ def set_scene_resolution(width: int, height: int):
"""
# make sure the render dialog is closed
# for the update of resolution
# Changing the Render Setup dialog settingsshould be done
# Changing the Render Setup dialog settings should be done
# with the actual Render Setup dialog in a closed state.
if rt.renderSceneDialog.isOpen():
rt.renderSceneDialog.close()
@ -190,6 +183,7 @@ def set_scene_resolution(width: int, height: int):
rt.renderWidth = width
rt.renderHeight = height
def reset_scene_resolution():
"""Apply the scene resolution from the project definition
@ -212,7 +206,7 @@ def reset_scene_resolution():
set_scene_resolution(width, height)
def get_frame_range() -> dict:
def get_frame_range() -> Union[Dict[str, Any], None]:
"""Get the current assets frame range and handles.
Returns:
@ -286,5 +280,5 @@ def get_max_version():
#(25000, 62, 0, 25, 0, 0, 997, 2023, "")
max_info[7] = max version date
"""
max_info = rt.maxversion()
max_info = rt.MaxVersion()
return max_info[7]

View file

@ -124,7 +124,7 @@ class RenderProducts(object):
"""Get all the Arnold AOVs name"""
aov_name = []
amw = rt.MaxtoAOps.AOVsManagerWindow()
amw = rt.MaxToAOps.AOVsManagerWindow()
aov_mgr = rt.renderers.current.AOVManager
# Check if there is any aov group set in AOV manager
aov_group_num = len(aov_mgr.drivers)

View file

@ -1,15 +1,105 @@
# -*- coding: utf-8 -*-
"""3dsmax specific Avalon/Pyblish plugin definitions."""
from pymxs import runtime as rt
import six
from abc import ABCMeta
from openpype.pipeline import (
CreatorError,
Creator,
CreatedInstance
)
import six
from pymxs import runtime as rt
from openpype.lib import BoolDef
from .lib import imprint, read, lsattr
from openpype.pipeline import CreatedInstance, Creator, CreatorError
from .lib import imprint, lsattr, read
MS_CUSTOM_ATTRIB = """attributes "openPypeData"
(
parameters main rollout:OPparams
(
all_handles type:#maxObjectTab tabSize:0 tabSizeVariable:on
)
rollout OPparams "OP Parameters"
(
listbox list_node "Node References" items:#()
button button_add "Add to Container"
button button_del "Delete from Container"
fn node_to_name the_node =
(
handle = the_node.handle
obj_name = the_node.name
handle_name = obj_name + "<" + handle as string + ">"
return handle_name
)
on button_add pressed do
(
current_selection = selectByName title:"Select Objects to add to
the Container" buttontext:"Add"
if current_selection == undefined then return False
temp_arr = #()
i_node_arr = #()
for c in current_selection do
(
handle_name = node_to_name c
node_ref = NodeTransformMonitor node:c
append temp_arr handle_name
append i_node_arr node_ref
)
all_handles = join i_node_arr all_handles
list_node.items = join temp_arr list_node.items
)
on button_del pressed do
(
current_selection = selectByName title:"Select Objects to remove
from the Container" buttontext:"Remove"
if current_selection == undefined then return False
temp_arr = #()
i_node_arr = #()
new_i_node_arr = #()
new_temp_arr = #()
for c in current_selection do
(
node_ref = NodeTransformMonitor node:c as string
handle_name = node_to_name c
tmp_all_handles = #()
for i in all_handles do
(
tmp = i as string
append tmp_all_handles tmp
)
idx = finditem tmp_all_handles node_ref
if idx do
(
new_i_node_arr = DeleteItem all_handles idx
)
idx = finditem list_node.items handle_name
if idx do
(
new_temp_arr = DeleteItem list_node.items idx
)
)
all_handles = join i_node_arr new_i_node_arr
list_node.items = join temp_arr new_temp_arr
)
on OPparams open do
(
if all_handles.count != 0 then
(
temp_arr = #()
for x in all_handles do
(
handle_name = node_to_name x.node
append temp_arr handle_name
)
list_node.items = temp_arr
)
)
)
)"""
class OpenPypeCreatorError(CreatorError):
@ -20,28 +110,40 @@ class MaxCreatorBase(object):
@staticmethod
def cache_subsets(shared_data):
if shared_data.get("max_cached_subsets") is None:
shared_data["max_cached_subsets"] = {}
cached_instances = lsattr("id", "pyblish.avalon.instance")
for i in cached_instances:
creator_id = rt.getUserProp(i, "creator_identifier")
if creator_id not in shared_data["max_cached_subsets"]:
shared_data["max_cached_subsets"][creator_id] = [i.name]
else:
shared_data[
"max_cached_subsets"][creator_id].append(i.name) # noqa
if shared_data.get("max_cached_subsets") is not None:
return shared_data
shared_data["max_cached_subsets"] = {}
cached_instances = lsattr("id", "pyblish.avalon.instance")
for i in cached_instances:
creator_id = rt.GetUserProp(i, "creator_identifier")
if creator_id not in shared_data["max_cached_subsets"]:
shared_data["max_cached_subsets"][creator_id] = [i.name]
else:
shared_data[
"max_cached_subsets"][creator_id].append(i.name)
return shared_data
@staticmethod
def create_instance_node(node_name: str, parent: str = ""):
parent_node = rt.getNodeByName(parent) if parent else rt.rootScene
if not parent_node:
raise OpenPypeCreatorError(f"Specified parent {parent} not found")
def create_instance_node(node):
"""Create instance node.
container = rt.container(name=node_name)
container.Parent = parent_node
If the supplied node is existing node, it will be used to hold the
instance, otherwise new node of type Dummy will be created.
return container
Args:
node (rt.MXSWrapperBase, str): Node or node name to use.
Returns:
instance
"""
if isinstance(node, str):
node = rt.Container(name=node)
attrs = rt.Execute(MS_CUSTOM_ATTRIB)
rt.custAttributes.add(node.baseObject, attrs)
return node
@six.add_metaclass(ABCMeta)
@ -50,7 +152,7 @@ class MaxCreator(Creator, MaxCreatorBase):
def create(self, subset_name, instance_data, pre_create_data):
if pre_create_data.get("use_selection"):
self.selected_nodes = rt.getCurrentSelection()
self.selected_nodes = rt.GetCurrentSelection()
instance_node = self.create_instance_node(subset_name)
instance_data["instance_node"] = instance_node.name
@ -60,8 +162,16 @@ class MaxCreator(Creator, MaxCreatorBase):
instance_data,
self
)
for node in self.selected_nodes:
node.Parent = instance_node
if pre_create_data.get("use_selection"):
node_list = []
for i in self.selected_nodes:
node_ref = rt.NodeTransformMonitor(node=i)
node_list.append(node_ref)
# Setting the property
rt.setProperty(
instance_node.openPypeData, "all_handles", node_list)
self._add_instance_to_context(instance)
imprint(instance_node.name, instance.data_to_store())
@ -70,10 +180,9 @@ class MaxCreator(Creator, MaxCreatorBase):
def collect_instances(self):
self.cache_subsets(self.collection_shared_data)
for instance in self.collection_shared_data[
"max_cached_subsets"].get(self.identifier, []):
for instance in self.collection_shared_data["max_cached_subsets"].get(self.identifier, []): # noqa
created_instance = CreatedInstance.from_existing(
read(rt.getNodeByName(instance)), self
read(rt.GetNodeByName(instance)), self
)
self._add_instance_to_context(created_instance)
@ -98,12 +207,10 @@ class MaxCreator(Creator, MaxCreatorBase):
"""
for instance in instances:
instance_node = rt.getNodeByName(
instance.data.get("instance_node"))
if instance_node:
rt.select(instance_node)
rt.execute(f'for o in selection do for c in o.children do c.parent = undefined') # noqa
rt.delete(instance_node)
if instance_node := rt.GetNodeByName(instance.data.get("instance_node")): # noqa
count = rt.custAttributes.count(instance_node)
rt.custAttributes.delete(instance_node, count)
rt.Delete(instance_node)
self._remove_instance_from_context(instance)