diff --git a/client/ayon_max/api/pipeline.py b/client/ayon_max/api/pipeline.py index a87cd657ce..0809b736cd 100644 --- a/client/ayon_max/api/pipeline.py +++ b/client/ayon_max/api/pipeline.py @@ -7,14 +7,18 @@ import json from ayon_core.host import HostBase, IWorkfileHost, ILoadHost, IPublishHost + +from ayon_core.lib import register_event_callback import pyblish.api from ayon_core.pipeline import ( register_creator_plugin_path, register_loader_plugin_path, AVALON_CONTAINER_ID, AYON_CONTAINER_ID, + get_current_project_name ) from ayon_max.api.menu import AYONMenu +from ayon_core.settings import get_project_settings from ayon_max.api import lib from ayon_max.api.plugin import MS_CUSTOM_ATTRIB from ayon_max import MAX_HOST_DIR @@ -47,9 +51,11 @@ def install(self): register_loader_plugin_path(LOAD_PATH) register_creator_plugin_path(CREATE_PATH) - # self._register_callbacks() + _set_project() + self.menu = AYONMenu() + register_event_callback("workfile.open.before", on_before_open) self._has_been_setup = True rt.callbacks.addScript(rt.Name('systemPostNew'), on_new) @@ -205,6 +211,32 @@ def containerise(name: str, nodes: list, context, return container +def _set_project(): + project_name = get_current_project_name() + project_settings = get_project_settings(project_name) + enable_project_creation = project_settings["max"].get("enabled_project_creation") + if not enable_project_creation: + log.debug("Project creation disabled. Skipping project creation.") + workdir = os.getenv("AYON_WORKDIR") + + os.makedirs(workdir, exist_ok=True) + mxp_filepath = os.path.join(workdir, "workspace.mxp") + if os.path.exists(mxp_filepath): + rt.pathConfig.load(mxp_filepath) + directory_count = rt.pathConfig.getProjectSubDirectoryCount() + for count in range(directory_count): + proj_dir = rt.pathConfig.getProjectSubDirectory(count) + if proj_dir: + os.makedirs(proj_dir, exist_ok=True) + rt.pathConfig.setCurrentProjectFolder(workdir) + + +def on_before_open(): + """Check and set up project before opening workfile + """ + _set_project() + + def load_custom_attribute_data(): """Re-loading the AYON custom parameter built by the creator diff --git a/client/ayon_max/hooks/pre_copy_mxp.py b/client/ayon_max/hooks/pre_copy_mxp.py new file mode 100644 index 0000000000..4173be2caf --- /dev/null +++ b/client/ayon_max/hooks/pre_copy_mxp.py @@ -0,0 +1,32 @@ +from ayon_applications import PreLaunchHook, LaunchTypes +from ayon_max.mxp import create_workspace_mxp + + +class PreCopyMxp(PreLaunchHook): + """Copy workspace.mxp to workdir. + + Hook `GlobalHostDataHook` must be executed before this hook. + """ + app_groups = {"3dsmax", "adsk_3dsmax"} + launch_types = {LaunchTypes.local} + + def execute(self): + max_setting = self.data["project_settings"]["max"] + mxp_workspace = max_setting.get("mxp_workspace") + # Ensure the hook would not cause possible error + # when using the old addon. + if mxp_workspace is None: + self.log.warning("No mxp workspace setting found in the " + "latest Max Addon.") + return + enabled_project_creation = mxp_workspace.get("enabled_project_creation") + if not enabled_project_creation: + self.log.debug("3dsmax project creation is not enabled. " + "Skipping creating workspace.mxp to workdir.") + return + workdir = self.launch_context.env.get("AYON_WORKDIR") + if not workdir: + self.log.warning("BUG: Workdir is not filled.") + return + + create_workspace_mxp(workdir, mxp_workspace=mxp_workspace) diff --git a/client/ayon_max/mxp.py b/client/ayon_max/mxp.py new file mode 100644 index 0000000000..78630d3b9b --- /dev/null +++ b/client/ayon_max/mxp.py @@ -0,0 +1,52 @@ +import os +from ayon_core.lib import Logger + + +def create_workspace_mxp(workdir, mxp_workspace=None): + dst_filepath = os.path.join(workdir, "workspace.mxp") + if os.path.exists(dst_filepath): + return + + log = Logger.get_logger("create_workspace_mxp") + max_script = default_mxp_template() + if mxp_workspace: + if not mxp_workspace.get("enabled_project_creation"): + log.debug("3dsmax project creation is disabled.") + return + + max_script = mxp_workspace.get("mxp_workspace_script") + # Skip if mxp script in settings is empty + if not max_script: + log.debug("File 'workspace.mxp' not created. Settings value is empty.") + return + + os.makedirs(workdir, exist_ok=True) + with open(dst_filepath, "w") as mxp_file: + mxp_file.write(max_script) + + return dst_filepath + + +def default_mxp_template(): + """Return text script for the path configuration if + users do not enable project creation in AYON project + setting + """ + mxp_template = "\n".join(( + '[Directories]', + 'Animations= ./', + 'Archives=./', + 'AutoBackup=./', + 'BitmapProxies=./', + 'Fluid Simulations=./', + 'Images=./', + 'MaxStart=./', + 'Previews=./', + 'RenderAssets=./', + 'RenderOutput= ./renders/3dsmax', + 'Scenes=./', + 'Sounds=./', + '[XReferenceDirs]', + 'Dir1=./' + )) + return mxp_template diff --git a/server/settings/main.py b/server/settings/main.py index 7b0bfc6421..a0d0ea99a2 100644 --- a/server/settings/main.py +++ b/server/settings/main.py @@ -21,6 +21,14 @@ def unit_scale_enum(): ] +class MxpWorkspaceSettings(BaseSettingsModel): + enabled_project_creation: bool = SettingsField( + False, title="Enable Project Creation") + mxp_workspace_script: str = SettingsField( + title="Max mxp Workspace", widget="textarea" + ) + + class UnitScaleSettings(BaseSettingsModel): enabled: bool = SettingsField(True, title="Enabled") scene_unit_scale: str = SettingsField( @@ -46,6 +54,10 @@ class MaxSettings(BaseSettingsModel): default_factory=UnitScaleSettings, title="Set Unit Scale" ) + mxp_workspace: MxpWorkspaceSettings = SettingsField( + default_factory=MxpWorkspaceSettings, + title="Max Workspace" + ) imageio: ImageIOSettings = SettingsField( default_factory=ImageIOSettings, title="Color Management (ImageIO)" @@ -67,11 +79,34 @@ class MaxSettings(BaseSettingsModel): title="Publish Plugins") +DEFAULT_MXP_WORKSPACE_SETTINGS = "\n".join(( + '[Directories]', + 'Animations= ./sceneassets/animations', + 'Archives=./archives', + 'AutoBackup=./autoback', + 'BitmapProxies=./proxies', + 'Fluid Simulations=./SimCache', + 'Images=./sceneassets/images', + 'MaxStart=./', + 'Previews=./previews', + 'RenderAssets=./sceneassets/renderassets', + 'RenderOutput= ./renders/3dsmax', + 'Scenes=./', + 'Sounds=./sceneassets/sounds', + '[XReferenceDirs]', + 'Dir1=./' +)) + + DEFAULT_VALUES = { "unit_scale_settings": { "enabled": True, "scene_unit_scale": "Centimeters" }, + "mxp_workspace": { + "enabled_project_creation": False, + "mxp_workspace_script": DEFAULT_MXP_WORKSPACE_SETTINGS + }, "RenderSettings": DEFAULT_RENDER_SETTINGS, "CreateReview": DEFAULT_CREATE_REVIEW_SETTINGS, "PointCloud": {