Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Max: Implementation of Camera Attributes Validator #6110

Merged
merged 13 commits into from
Feb 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions openpype/hosts/max/api/action.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from pymxs import runtime as rt

import pyblish.api

from openpype.pipeline.publish import get_errored_instances_from_context


class SelectInvalidAction(pyblish.api.Action):
"""Select invalid objects in Blender when a publish plug-in failed."""
label = "Select Invalid"
on = "failed"
icon = "search"

def process(self, context, plugin):
errored_instances = get_errored_instances_from_context(context,
plugin=plugin)

# Get the invalid nodes for the plug-ins
self.log.info("Finding invalid nodes...")
invalid = list()
for instance in errored_instances:
invalid_nodes = plugin.get_invalid(instance)
if invalid_nodes:
if isinstance(invalid_nodes, (list, tuple)):
invalid.extend(invalid_nodes)
else:
self.log.warning(
"Failed plug-in doesn't have any selectable objects."
)

if not invalid:
self.log.info("No invalid nodes found.")
return
invalid_names = [obj.name for obj in invalid if isinstance(obj, str)]
if not invalid_names:
invalid_names = [obj.name for obj, _ in invalid]
invalid = [obj for obj, _ in invalid]
self.log.info(
"Selecting invalid objects: %s", ", ".join(invalid_names)
)

rt.Select(invalid)
88 changes: 88 additions & 0 deletions openpype/hosts/max/plugins/publish/validate_camera_attributes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import pyblish.api
from pymxs import runtime as rt

from openpype.pipeline.publish import (
RepairAction,
OptionalPyblishPluginMixin,
PublishValidationError
)
from openpype.hosts.max.api.action import SelectInvalidAction


class ValidateCameraAttributes(OptionalPyblishPluginMixin,
pyblish.api.InstancePlugin):
"""Validates Camera has no invalid attribute properties
or values.(For 3dsMax Cameras only)

"""

order = pyblish.api.ValidatorOrder
families = ['camera']
hosts = ['max']
label = 'Validate Camera Attributes'
actions = [SelectInvalidAction, RepairAction]
optional = True

DEFAULTS = ["fov", "nearrange", "farrange",
"nearclip", "farclip"]
CAM_TYPE = ["Freecamera", "Targetcamera",
"Physical"]

@classmethod
def get_invalid(cls, instance):
invalid = []
if rt.units.DisplayType != rt.Name("Generic"):
cls.log.warning(
"Generic Type is not used as a scene unit\n\n"
"sure you tweak the settings with your own values\n\n"
"before validation.")
cameras = instance.data["members"]
project_settings = instance.context.data["project_settings"].get("max")
cam_attr_settings = (
project_settings["publish"]["ValidateCameraAttributes"]
)
for camera in cameras:
if str(rt.ClassOf(camera)) not in cls.CAM_TYPE:
cls.log.debug(
"Skipping camera created from external plugin..")
continue
for attr in cls.DEFAULTS:
default_value = cam_attr_settings.get(attr)
if default_value == float(0):
cls.log.debug(
f"the value of {attr} in setting set to"
" zero. Skipping the check.")
continue
if round(rt.getProperty(camera, attr), 1) != default_value:
cls.log.error(
f"Invalid attribute value for {camera.name}:{attr} "
f"(should be: {default_value}))")
invalid.append(camera)

return invalid

def process(self, instance):
if not self.is_active(instance.data):
self.log.debug("Skipping Validate Camera Attributes.")
return
invalid = self.get_invalid(instance)

if invalid:
raise PublishValidationError(
"Invalid camera attributes found. See log.")

@classmethod
def repair(cls, instance):
invalid_cameras = cls.get_invalid(instance)
project_settings = instance.context.data["project_settings"].get("max")
cam_attr_settings = (
project_settings["publish"]["ValidateCameraAttributes"]
)
for camera in invalid_cameras:
for attr in cls.DEFAULTS:
expected_value = cam_attr_settings.get(attr)
if expected_value == float(0):
cls.log.debug(
f"the value of {attr} in setting set to zero.")
continue
rt.setProperty(camera, attr, expected_value)
10 changes: 10 additions & 0 deletions openpype/settings/defaults/project_settings/max.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@
"enabled": false,
"attributes": {}
},
"ValidateCameraAttributes": {
"enabled": true,
"optional": true,
"active": false,
"fov": 45.0,
"nearrange": 0.0,
"farrange": 1000.0,
"nearclip": 1.0,
"farclip": 1000.0
},
"ValidateLoadedPlugin": {
"enabled": false,
"optional": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,76 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"checkbox_key": "enabled",
"key": "ValidateCameraAttributes",
"label": "Validate Camera Attributes",
"is_group": true,
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"type": "boolean",
"key": "optional",
"label": "Optional"
},
{
"type": "boolean",
"key": "active",
"label": "Active"
},
{
"type": "number",
"key": "fov",
"label": "Focal Length",
"decimal": 1,
"minimum": 0,
"maximum": 100.0
},
{
"type": "label",
"label": "If the value of the camera attributes set to 0, the system automatically skips checking it"
},
{
"type": "number",
"key": "nearrange",
"label": "Near Range",
"decimal": 1,
"minimum": 0,
"maximum": 100.0
},
{
"type": "number",
"key": "farrange",
"label": "Far Range",
"decimal": 1,
"minimum": 0,
"maximum": 2000.0
},
{
"type": "number",
"key": "nearclip",
"label": "Near Clip",
"decimal": 1,
"minimum": 0,
"maximum": 100.0
},
{
"type": "number",
"key": "farclip",
"label": "Far Clip",
"decimal": 1,
"minimum": 0,
"maximum": 2000.0
}
]
},

{
"type": "dict",
"collapsible": true,
Expand Down
30 changes: 29 additions & 1 deletion server_addon/max/server/settings/publishers.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,17 @@ def validate_json(cls, value):
return value


class ValidateCameraAttributesModel(BaseSettingsModel):
enabled: bool = SettingsField(title="Enabled")
optional: bool = SettingsField(title="Optional")
active: bool = SettingsField(title="Active")
fov: float = SettingsField(0.0, title="Focal Length")
nearrange: float = SettingsField(0.0, title="Near Range")
farrange: float = SettingsField(0.0, title="Far Range")
nearclip: float = SettingsField(0.0, title="Near Clip")
farclip: float = SettingsField(0.0, title="Far Clip")


class FamilyMappingItemModel(BaseSettingsModel):
product_types: list[str] = SettingsField(
default_factory=list,
Expand Down Expand Up @@ -63,7 +74,14 @@ class PublishersModel(BaseSettingsModel):
default_factory=ValidateAttributesModel,
title="Validate Attributes"
)

ValidateCameraAttributes: ValidateCameraAttributesModel = SettingsField(
default_factory=ValidateCameraAttributesModel,
title="Validate Camera Attributes",
description=(
"If the value of the camera attributes set to 0, "
"the system automatically skips checking it"
)
)
ValidateLoadedPlugin: ValidateLoadedPluginModel = SettingsField(
default_factory=ValidateLoadedPluginModel,
title="Validate Loaded Plugin"
Expand Down Expand Up @@ -101,6 +119,16 @@ class PublishersModel(BaseSettingsModel):
"enabled": False,
"attributes": "{}"
},
"ValidateCameraAttributes": {
"enabled": True,
"optional": True,
"active": False,
"fov": 45.0,
"nearrange": 0.0,
"farrange": 1000.0,
"nearclip": 1.0,
"farclip": 1000.0
},
"ValidateLoadedPlugin": {
"enabled": False,
"optional": True,
Expand Down
2 changes: 1 addition & 1 deletion server_addon/max/server/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.1.4"
__version__ = "0.1.5"
Loading