mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
🚧 3dsmax addon basics
This commit is contained in:
parent
d2de7c56fc
commit
decc8df4ae
14 changed files with 299 additions and 3 deletions
|
|
@ -1,2 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
print("inside python startup")
|
||||
10
openpype/hosts/max/__init__.py
Normal file
10
openpype/hosts/max/__init__.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from .addon import (
|
||||
MaxAddon,
|
||||
MAX_HOST_DIR,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"MaxAddon",
|
||||
"MAX_HOST_DIR",
|
||||
)
|
||||
16
openpype/hosts/max/addon.py
Normal file
16
openpype/hosts/max/addon.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from openpype.modules import OpenPypeModule, IHostAddon
|
||||
|
||||
MAX_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class MaxAddon(OpenPypeModule, IHostAddon):
|
||||
name = "max"
|
||||
host_name = "max"
|
||||
|
||||
def initialize(self, module_settings):
|
||||
self.enabled = True
|
||||
|
||||
def get_workfile_extensions(self):
|
||||
return [".max"]
|
||||
13
openpype/hosts/max/api/__init__.py
Normal file
13
openpype/hosts/max/api/__init__.py
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Public API for 3dsmax"""
|
||||
|
||||
from .pipeline import (
|
||||
MaxHost
|
||||
)
|
||||
from .menu import OpenPypeMenu
|
||||
|
||||
|
||||
__all__ = [
|
||||
"MaxHost",
|
||||
"OpenPypeMenu"
|
||||
]
|
||||
2
openpype/hosts/max/api/lib.py
Normal file
2
openpype/hosts/max/api/lib.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
def imprint(attr, data):
|
||||
...
|
||||
80
openpype/hosts/max/api/menu.py
Normal file
80
openpype/hosts/max/api/menu.py
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""3dsmax menu definition of OpenPype."""
|
||||
from abc import ABCMeta, abstractmethod
|
||||
import six
|
||||
from Qt import QtWidgets, QtCore
|
||||
from pymxs import runtime as rt
|
||||
|
||||
from openpype.tools.utils import host_tools
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class OpenPypeMenu(object):
|
||||
|
||||
def __init__(self):
|
||||
self.main_widget = self.get_main_widget()
|
||||
|
||||
@staticmethod
|
||||
def get_main_widget():
|
||||
"""Get 3dsmax main window."""
|
||||
return QtWidgets.QWidget.find(rt.windows.getMAXHWND())
|
||||
|
||||
def get_main_menubar(self):
|
||||
"""Get main Menubar by 3dsmax main window."""
|
||||
return list(self.main_widget.findChildren(QtWidgets.QMenuBar))[0]
|
||||
|
||||
def get_or_create_openpype_menu(self, name="&OpenPype", before="&Help"):
|
||||
menu_bar = self.get_main_menubar()
|
||||
menu_items = menu_bar.findChildren(
|
||||
QtWidgets.QMenu, options=QtCore.Qt.FindDirectChildrenOnly)
|
||||
help_action = None
|
||||
for item in menu_items:
|
||||
if name in item.title():
|
||||
# we already have OpenPype menu
|
||||
return item
|
||||
|
||||
if before in item.title():
|
||||
help_action = item.menuAction()
|
||||
|
||||
op_menu = QtWidgets.QMenu("&OpenPype")
|
||||
menu_bar.insertMenu(before, op_menu)
|
||||
return op_menu
|
||||
|
||||
def build_openpype_menu(self):
|
||||
openpype_menu = self.get_or_create_openpype_menu()
|
||||
load_action = QtWidgets.QAction("Load...", openpype_menu)
|
||||
load_action.triggered.connect(self.load_callback)
|
||||
openpype_menu.addAction(load_action)
|
||||
|
||||
publish_action = QtWidgets.QAction("Publish...", openpype_menu)
|
||||
publish_action.triggered.connect(self.publish_callback)
|
||||
openpype_menu.addAction(publish_action)
|
||||
|
||||
manage_action = QtWidgets.QAction("Manage...", openpype_menu)
|
||||
manage_action.triggered.connect(self.manage_callback)
|
||||
openpype_menu.addAction(manage_action)
|
||||
|
||||
library_action = QtWidgets.QAction("Library...", openpype_menu)
|
||||
library_action.triggered.connect(self.library_callback)
|
||||
openpype_menu.addAction(library_action)
|
||||
|
||||
openpype_menu.addSeparator()
|
||||
|
||||
workfiles_action = QtWidgets.QAction("Work Files...", openpype_menu)
|
||||
workfiles_action.triggered.connect(self.workfiles_callback)
|
||||
openpype_menu.addAction(workfiles_action)
|
||||
|
||||
def load_callback(self):
|
||||
host_tools.show_loader(parent=self.main_widget)
|
||||
|
||||
def publish_callback(self):
|
||||
host_tools.show_publisher(parent=self.main_widget)
|
||||
|
||||
def manage_callback(self):
|
||||
host_tools.show_subset_manager(parent=self.main_widget)
|
||||
|
||||
def library_callback(self):
|
||||
host_tools.show_library_loader(parent=self.main_widget)
|
||||
|
||||
def workfiles_callback(self):
|
||||
host_tools.show_workfiles(parent=self.main_widget)
|
||||
153
openpype/hosts/max/api/pipeline.py
Normal file
153
openpype/hosts/max/api/pipeline.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Pipeline tools for OpenPype Houdini integration."""
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
import contextlib
|
||||
|
||||
from openpype.host import HostBase, IWorkfileHost, ILoadHost, INewPublisher
|
||||
import pyblish.api
|
||||
from openpype.pipeline import (
|
||||
register_creator_plugin_path,
|
||||
register_loader_plugin_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from openpype.hosts.max.api import OpenPypeMenu
|
||||
from openpype.hosts.max.api import lib
|
||||
from openpype.hosts.max import MAX_HOST_DIR
|
||||
from openpype.pipeline.load import any_outdated_containers
|
||||
from openpype.lib import (
|
||||
register_event_callback,
|
||||
emit_event,
|
||||
)
|
||||
from pymxs import runtime as rt # noqa
|
||||
|
||||
log = logging.getLogger("openpype.hosts.max")
|
||||
|
||||
PLUGINS_DIR = os.path.join(MAX_HOST_DIR, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
||||
INVENTORY_PATH = os.path.join(PLUGINS_DIR, "inventory")
|
||||
|
||||
|
||||
class MaxHost(HostBase, IWorkfileHost, ILoadHost, INewPublisher):
|
||||
name = "max"
|
||||
menu = None
|
||||
|
||||
def __init__(self):
|
||||
super(MaxHost, self).__init__()
|
||||
self._op_events = {}
|
||||
self._has_been_setup = False
|
||||
|
||||
def install(self):
|
||||
pyblish.api.register_host("max")
|
||||
|
||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
register_loader_plugin_path(LOAD_PATH)
|
||||
register_creator_plugin_path(CREATE_PATH)
|
||||
log.info("Building menu ...")
|
||||
|
||||
self.menu = OpenPypeMenu()
|
||||
|
||||
log.info("Installing callbacks ... ")
|
||||
# register_event_callback("init", on_init)
|
||||
self._register_callbacks()
|
||||
|
||||
# register_event_callback("before.save", before_save)
|
||||
# register_event_callback("save", on_save)
|
||||
# register_event_callback("open", on_open)
|
||||
# register_event_callback("new", on_new)
|
||||
|
||||
# pyblish.api.register_callback(
|
||||
# "instanceToggled", on_pyblish_instance_toggled
|
||||
# )
|
||||
|
||||
self._has_been_setup = True
|
||||
|
||||
def has_unsaved_changes(self):
|
||||
# TODO: how to get it from 3dsmax?
|
||||
return True
|
||||
|
||||
def get_workfile_extensions(self):
|
||||
return [".hip", ".hiplc", ".hipnc"]
|
||||
|
||||
def save_workfile(self, dst_path=None):
|
||||
rt.saveMaxFile(dst_path)
|
||||
return dst_path
|
||||
|
||||
def open_workfile(self, filepath):
|
||||
rt.checkForSave()
|
||||
rt.loadMaxFile(filepath)
|
||||
return filepath
|
||||
|
||||
def get_current_workfile(self):
|
||||
return os.path.join(rt.maxFilePath, rt.maxFileName)
|
||||
|
||||
def get_containers(self):
|
||||
return ls()
|
||||
|
||||
def _register_callbacks(self):
|
||||
for event in self._op_events.copy().values():
|
||||
if event is None:
|
||||
continue
|
||||
|
||||
try:
|
||||
rt.callbacks.removeScript(id=rt.name(event.name))
|
||||
except RuntimeError as e:
|
||||
log.info(e)
|
||||
|
||||
rt.callbacks.addScript(
|
||||
event.name, event.callback, id=rt.Name('OpenPype'))
|
||||
|
||||
@staticmethod
|
||||
def create_context_node():
|
||||
"""Helper for creating context holding node."""
|
||||
|
||||
root_scene = rt.rootScene
|
||||
|
||||
create_attr_script = ("""
|
||||
attributes "OpenPypeContext"
|
||||
(
|
||||
parameters main rollout:params
|
||||
(
|
||||
context type: #string
|
||||
)
|
||||
|
||||
rollout params "OpenPype Parameters"
|
||||
(
|
||||
editText editTextContext "Context" type: #string
|
||||
)
|
||||
)
|
||||
""")
|
||||
|
||||
attr = rt.execute(create_attr_script)
|
||||
rt.custAttributes.add(root_scene, attr)
|
||||
|
||||
return root_scene.OpenPypeContext.context
|
||||
|
||||
def update_context_data(self, data, changes):
|
||||
try:
|
||||
context = rt.rootScene.OpenPypeContext.context
|
||||
except AttributeError:
|
||||
# context node doesn't exists
|
||||
context = self.create_context_node()
|
||||
|
||||
lib.imprint(context, data)
|
||||
|
||||
def get_context_data(self):
|
||||
try:
|
||||
context = rt.rootScene.OpenPypeContext.context
|
||||
except AttributeError:
|
||||
# context node doesn't exists
|
||||
context = self.create_context_node()
|
||||
return lib.read(context)
|
||||
|
||||
def save_file(self, dst_path=None):
|
||||
# Force forwards slashes to avoid segfault
|
||||
dst_path = dst_path.replace("\\", "/")
|
||||
rt.saveMaxFile(dst_path)
|
||||
|
||||
|
||||
def ls():
|
||||
...
|
||||
17
openpype/hosts/max/hooks/set_paths.py
Normal file
17
openpype/hosts/max/hooks/set_paths.py
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
from openpype.lib import PreLaunchHook
|
||||
|
||||
|
||||
class SetPath(PreLaunchHook):
|
||||
"""Set current dir to workdir.
|
||||
|
||||
Hook `GlobalHostDataHook` must be executed before this hook.
|
||||
"""
|
||||
app_groups = ["max"]
|
||||
|
||||
def execute(self):
|
||||
workdir = self.launch_context.env.get("AVALON_WORKDIR", "")
|
||||
if not workdir:
|
||||
self.log.warning("BUG: Workdir is not filled.")
|
||||
return
|
||||
|
||||
self.launch_context.kwargs["cwd"] = workdir
|
||||
7
openpype/hosts/max/startup/startup.py
Normal file
7
openpype/hosts/max/startup/startup.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from openpype.hosts.max.api import MaxHost
|
||||
from openpype.pipeline import install_host
|
||||
|
||||
host = MaxHost()
|
||||
install_host(host)
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ class HostsEnumEntity(BaseEnumEntity):
|
|||
|
||||
schema_types = ["hosts-enum"]
|
||||
all_host_names = [
|
||||
"3dsmax",
|
||||
"max",
|
||||
"aftereffects",
|
||||
"blender",
|
||||
"celaction",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue