mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
allows users to set up the scene unit scale in Max with OP/AYON settings /refactor fbx extractors
This commit is contained in:
parent
8ea81950e2
commit
ebc4f1467d
9 changed files with 180 additions and 78 deletions
|
|
@ -294,6 +294,35 @@ def reset_frame_range(fps: bool = True):
|
|||
frame_range["frameStartHandle"], frame_range["frameEndHandle"])
|
||||
|
||||
|
||||
def reset_unit_scale():
|
||||
"""Apply the unit scale setting to 3dsMax
|
||||
"""
|
||||
project_name = get_current_project_name()
|
||||
settings = get_project_settings(project_name).get("max")
|
||||
unit_scale_setting = settings.get("unit_scale_settings")
|
||||
if unit_scale_setting:
|
||||
scene_scale = unit_scale_setting["scene_unit_scale"]
|
||||
rt.units.SystemType = rt.Name(scene_scale)
|
||||
|
||||
def convert_unit_scale():
|
||||
"""Convert system unit scale in 3dsMax
|
||||
for fbx export
|
||||
|
||||
Returns:
|
||||
str: unit scale
|
||||
"""
|
||||
unit_scale_dict = {
|
||||
"inches": "in",
|
||||
"feet": "ft",
|
||||
"miles": "mi",
|
||||
"millimeters": "mm",
|
||||
"centimeters": "cm",
|
||||
"meters": "m",
|
||||
"kilometers": "km"
|
||||
}
|
||||
current_unit_scale = rt.Execute("units.SystemType as string")
|
||||
return unit_scale_dict[current_unit_scale]
|
||||
|
||||
def set_context_setting():
|
||||
"""Apply the project settings from the project definition
|
||||
|
||||
|
|
@ -310,6 +339,7 @@ def set_context_setting():
|
|||
reset_scene_resolution()
|
||||
reset_frame_range()
|
||||
reset_colorspace()
|
||||
reset_unit_scale()
|
||||
|
||||
|
||||
def get_max_version():
|
||||
|
|
|
|||
|
|
@ -124,6 +124,10 @@ class OpenPypeMenu(object):
|
|||
colorspace_action.triggered.connect(self.colorspace_callback)
|
||||
openpype_menu.addAction(colorspace_action)
|
||||
|
||||
unit_scale_action = QtWidgets.QAction("Set Unit Scale", openpype_menu)
|
||||
unit_scale_action.triggered.connect(self.unit_scale_callback)
|
||||
openpype_menu.addAction(unit_scale_action)
|
||||
|
||||
return openpype_menu
|
||||
|
||||
def load_callback(self):
|
||||
|
|
@ -157,3 +161,7 @@ class OpenPypeMenu(object):
|
|||
def colorspace_callback(self):
|
||||
"""Callback to reset colorspace"""
|
||||
return lib.reset_colorspace()
|
||||
|
||||
def unit_scale_callback(self):
|
||||
"""Callback to reset unit scale"""
|
||||
return lib.reset_unit_scale()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
import os
|
||||
import logging
|
||||
from operator import attrgetter
|
||||
from functools import partial
|
||||
|
||||
import json
|
||||
|
||||
|
|
@ -13,6 +14,10 @@ from openpype.pipeline import (
|
|||
register_loader_plugin_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from openpype.lib import (
|
||||
register_event_callback,
|
||||
emit_event
|
||||
)
|
||||
from openpype.hosts.max.api.menu import OpenPypeMenu
|
||||
from openpype.hosts.max.api import lib
|
||||
from openpype.hosts.max.api.plugin import MS_CUSTOM_ATTRIB
|
||||
|
|
@ -46,19 +51,14 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
|||
register_loader_plugin_path(LOAD_PATH)
|
||||
register_creator_plugin_path(CREATE_PATH)
|
||||
|
||||
# self._register_callbacks()
|
||||
self._register_callbacks()
|
||||
self.menu = OpenPypeMenu()
|
||||
register_event_callback(
|
||||
"init", self._deferred_menu_creation)
|
||||
|
||||
self._has_been_setup = True
|
||||
|
||||
def context_setting():
|
||||
return lib.set_context_setting()
|
||||
|
||||
rt.callbacks.addScript(rt.Name('systemPostNew'),
|
||||
context_setting)
|
||||
|
||||
rt.callbacks.addScript(rt.Name('filePostOpen'),
|
||||
lib.check_colorspace)
|
||||
register_event_callback("open", on_open)
|
||||
register_event_callback("new", on_new)
|
||||
|
||||
def has_unsaved_changes(self):
|
||||
# TODO: how to get it from 3dsmax?
|
||||
|
|
@ -83,11 +83,28 @@ class MaxHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
|
|||
return ls()
|
||||
|
||||
def _register_callbacks(self):
|
||||
rt.callbacks.removeScripts(id=rt.name("OpenPypeCallbacks"))
|
||||
|
||||
rt.callbacks.addScript(
|
||||
unique_id = rt.Name("openpype_callbacks")
|
||||
for handler, event in self._op_events.copy().items():
|
||||
if event is None:
|
||||
continue
|
||||
try:
|
||||
rt.callbacks.removeScripts(id=unique_id)
|
||||
self._op_events[handler] = None
|
||||
except RuntimeError as exc:
|
||||
self.log.info(exc)
|
||||
#self._deferred_menu_creation
|
||||
self._op_events["init"] = rt.callbacks.addScript(
|
||||
rt.Name("postLoadingMenus"),
|
||||
self._deferred_menu_creation, id=rt.Name('OpenPypeCallbacks'))
|
||||
partial(_emit_event_notification_param, "init"),
|
||||
id=unique_id)
|
||||
self._op_events["new"] = rt.callbacks.addScript(
|
||||
rt.Name('systemPostNew'),
|
||||
partial(_emit_event_notification_param, "new"),
|
||||
id=unique_id)
|
||||
self._op_events["open"] = rt.callbacks.addScript(
|
||||
rt.Name('filePostOpen'),
|
||||
partial(_emit_event_notification_param, "open"),
|
||||
id=unique_id)
|
||||
|
||||
def _deferred_menu_creation(self):
|
||||
self.log.info("Building menu ...")
|
||||
|
|
@ -144,6 +161,19 @@ attributes "OpenPypeContext"
|
|||
rt.saveMaxFile(dst_path)
|
||||
|
||||
|
||||
def _emit_event_notification_param(event):
|
||||
notification = rt.callbacks.notificationParam()
|
||||
emit_event(event, {"notificationParam": notification})
|
||||
|
||||
|
||||
def on_open():
|
||||
return lib.check_colorspace()
|
||||
|
||||
|
||||
def on_new():
|
||||
return lib.set_context_setting()
|
||||
|
||||
|
||||
def ls() -> list:
|
||||
"""Get all OpenPype instances."""
|
||||
objs = rt.objects
|
||||
|
|
|
|||
|
|
@ -1,55 +0,0 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
from pymxs import runtime as rt
|
||||
|
||||
from openpype.hosts.max.api import maintained_selection
|
||||
from openpype.pipeline import OptionalPyblishPluginMixin, publish
|
||||
|
||||
|
||||
class ExtractCameraFbx(publish.Extractor, OptionalPyblishPluginMixin):
|
||||
"""Extract Camera with FbxExporter."""
|
||||
|
||||
order = pyblish.api.ExtractorOrder - 0.2
|
||||
label = "Extract Fbx Camera"
|
||||
hosts = ["max"]
|
||||
families = ["camera"]
|
||||
optional = True
|
||||
|
||||
def process(self, instance):
|
||||
if not self.is_active(instance.data):
|
||||
return
|
||||
|
||||
stagingdir = self.staging_dir(instance)
|
||||
filename = "{name}.fbx".format(**instance.data)
|
||||
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
rt.FBXExporterSetParam("Animation", True)
|
||||
rt.FBXExporterSetParam("Cameras", True)
|
||||
rt.FBXExporterSetParam("AxisConversionMethod", "Animation")
|
||||
rt.FBXExporterSetParam("UpAxis", "Y")
|
||||
rt.FBXExporterSetParam("Preserveinstances", True)
|
||||
|
||||
with maintained_selection():
|
||||
# select and export
|
||||
node_list = instance.data["members"]
|
||||
rt.Select(node_list)
|
||||
rt.ExportFile(
|
||||
filepath,
|
||||
rt.Name("noPrompt"),
|
||||
selectedOnly=True,
|
||||
using=rt.FBXEXP,
|
||||
)
|
||||
|
||||
self.log.info("Performing Extraction ...")
|
||||
if "representations" not in instance.data:
|
||||
instance.data["representations"] = []
|
||||
|
||||
representation = {
|
||||
"name": "fbx",
|
||||
"ext": "fbx",
|
||||
"files": filename,
|
||||
"stagingDir": stagingdir,
|
||||
}
|
||||
instance.data["representations"].append(representation)
|
||||
self.log.info(f"Extracted instance '{instance.name}' to: {filepath}")
|
||||
|
|
@ -3,6 +3,7 @@ import pyblish.api
|
|||
from openpype.pipeline import publish, OptionalPyblishPluginMixin
|
||||
from pymxs import runtime as rt
|
||||
from openpype.hosts.max.api import maintained_selection
|
||||
from openpype.hosts.max.api.lib import convert_unit_scale
|
||||
|
||||
|
||||
class ExtractModelFbx(publish.Extractor, OptionalPyblishPluginMixin):
|
||||
|
|
@ -23,14 +24,7 @@ class ExtractModelFbx(publish.Extractor, OptionalPyblishPluginMixin):
|
|||
stagingdir = self.staging_dir(instance)
|
||||
filename = "{name}.fbx".format(**instance.data)
|
||||
filepath = os.path.join(stagingdir, filename)
|
||||
|
||||
rt.FBXExporterSetParam("Animation", False)
|
||||
rt.FBXExporterSetParam("Cameras", False)
|
||||
rt.FBXExporterSetParam("Lights", False)
|
||||
rt.FBXExporterSetParam("PointCache", False)
|
||||
rt.FBXExporterSetParam("AxisConversionMethod", "Animation")
|
||||
rt.FBXExporterSetParam("UpAxis", "Y")
|
||||
rt.FBXExporterSetParam("Preserveinstances", True)
|
||||
self._set_fbx_attributes()
|
||||
|
||||
with maintained_selection():
|
||||
# select and export
|
||||
|
|
@ -56,3 +50,33 @@ class ExtractModelFbx(publish.Extractor, OptionalPyblishPluginMixin):
|
|||
self.log.info(
|
||||
"Extracted instance '%s' to: %s" % (instance.name, filepath)
|
||||
)
|
||||
|
||||
def _set_fbx_attributes(self):
|
||||
unit_scale = convert_unit_scale()
|
||||
rt.FBXExporterSetParam("Animation", False)
|
||||
rt.FBXExporterSetParam("Cameras", False)
|
||||
rt.FBXExporterSetParam("Lights", False)
|
||||
rt.FBXExporterSetParam("PointCache", False)
|
||||
rt.FBXExporterSetParam("AxisConversionMethod", "Animation")
|
||||
rt.FBXExporterSetParam("UpAxis", "Y")
|
||||
rt.FBXExporterSetParam("Preserveinstances", True)
|
||||
if unit_scale:
|
||||
rt.FBXExporterSetParam("ConvertUnit", unit_scale)
|
||||
|
||||
class ExtractCameraFbx(ExtractModelFbx):
|
||||
"""Extract Camera with FbxExporter."""
|
||||
|
||||
order = pyblish.api.ExtractorOrder - 0.2
|
||||
label = "Extract Fbx Camera"
|
||||
families = ["camera"]
|
||||
optional = True
|
||||
|
||||
def _set_fbx_attributes(self):
|
||||
unit_scale = convert_unit_scale()
|
||||
rt.FBXExporterSetParam("Animation", True)
|
||||
rt.FBXExporterSetParam("Cameras", True)
|
||||
rt.FBXExporterSetParam("AxisConversionMethod", "Animation")
|
||||
rt.FBXExporterSetParam("UpAxis", "Y")
|
||||
rt.FBXExporterSetParam("Preserveinstances", True)
|
||||
if unit_scale:
|
||||
rt.FBXExporterSetParam("ConvertUnit", unit_scale)
|
||||
|
|
@ -1,4 +1,8 @@
|
|||
{
|
||||
"unit_scale_settings": {
|
||||
"enabled": true,
|
||||
"scene_unit_scale": "Inches"
|
||||
},
|
||||
"imageio": {
|
||||
"activate_host_color_management": true,
|
||||
"ocio_config": {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,37 @@
|
|||
"label": "Max",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"key": "unit_scale_settings",
|
||||
"type": "dict",
|
||||
"label": "Set Unit Scale",
|
||||
"collapsible": true,
|
||||
"is_group": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"key": "scene_unit_scale",
|
||||
"label": "Scene Unit Scale",
|
||||
"type": "enum",
|
||||
"multiselection": false,
|
||||
"defaults": "exr",
|
||||
"enum_items": [
|
||||
{"Inches": "in"},
|
||||
{"Feet": "ft"},
|
||||
{"Miles": "mi"},
|
||||
{"Millimeters": "mm"},
|
||||
{"Centimeters": "cm"},
|
||||
{"Meters": "m"},
|
||||
{"Kilometers": "km"}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "imageio",
|
||||
"type": "dict",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,28 @@ from .publishers import (
|
|||
)
|
||||
|
||||
|
||||
def unit_scale_enum():
|
||||
"""Return enumerator for scene unit scale."""
|
||||
return [
|
||||
{"label": "in", "value": "Inches"},
|
||||
{"label": "ft", "value": "Feet"},
|
||||
{"label": "mi", "value": "Miles"},
|
||||
{"label": "mm", "value": "Millimeters"},
|
||||
{"label": "cm", "value": "Centimeters"},
|
||||
{"label": "m", "value": "Meters"},
|
||||
{"label": "km", "value": "Kilometers"}
|
||||
]
|
||||
|
||||
|
||||
class UnitScaleSettings(BaseSettingsModel):
|
||||
enabled: bool = Field(True, title="Enabled")
|
||||
scene_unit_scale: str = Field(
|
||||
"Centimeters",
|
||||
title="Scene Unit Scale",
|
||||
enum_resolver=unit_scale_enum
|
||||
)
|
||||
|
||||
|
||||
class PRTAttributesModel(BaseSettingsModel):
|
||||
_layout = "compact"
|
||||
name: str = Field(title="Name")
|
||||
|
|
@ -24,6 +46,10 @@ class PointCloudSettings(BaseSettingsModel):
|
|||
|
||||
|
||||
class MaxSettings(BaseSettingsModel):
|
||||
unit_scale_settings: UnitScaleSettings = Field(
|
||||
default_factory=UnitScaleSettings,
|
||||
title="Set Unit Scale"
|
||||
)
|
||||
imageio: ImageIOSettings = Field(
|
||||
default_factory=ImageIOSettings,
|
||||
title="Color Management (ImageIO)"
|
||||
|
|
@ -46,6 +72,10 @@ class MaxSettings(BaseSettingsModel):
|
|||
|
||||
|
||||
DEFAULT_VALUES = {
|
||||
"unit_scale_settings": {
|
||||
"enabled": True,
|
||||
"scene_unit_scale": "cm"
|
||||
},
|
||||
"RenderSettings": DEFAULT_RENDER_SETTINGS,
|
||||
"CreateReview": DEFAULT_CREATE_REVIEW_SETTINGS,
|
||||
"PointCloud": {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.3"
|
||||
__version__ = "0.1.4"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue