diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 8b70b3ced7..edbd14bb8b 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -234,27 +234,40 @@ def reset_scene_resolution(): set_scene_resolution(width, height) -def get_frame_range() -> Union[Dict[str, Any], None]: +def get_frame_range(asset_doc=None) -> Union[Dict[str, Any], None]: """Get the current assets frame range and handles. + Args: + asset_doc (dict): Asset Entity Data + Returns: dict: with frame start, frame end, handle start, handle end. """ # Set frame start/end - asset = get_current_project_asset() - frame_start = asset["data"].get("frameStart") - frame_end = asset["data"].get("frameEnd") + if asset_doc is None: + asset_doc = get_current_project_asset() + + data = asset_doc["data"] + frame_start = data.get("frameStart") + frame_end = data.get("frameEnd") if frame_start is None or frame_end is None: - return + return {} + + frame_start = int(frame_start) + frame_end = int(frame_end) + handle_start = int(data.get("handleStart", 0)) + handle_end = int(data.get("handleEnd", 0)) + frame_start_handle = frame_start - handle_start + frame_end_handle = frame_end + handle_end - handle_start = asset["data"].get("handleStart", 0) - handle_end = asset["data"].get("handleEnd", 0) return { "frameStart": frame_start, "frameEnd": frame_end, "handleStart": handle_start, - "handleEnd": handle_end + "handleEnd": handle_end, + "frameStartHandle": frame_start_handle, + "frameEndHandle": frame_end_handle, } @@ -274,12 +287,11 @@ def reset_frame_range(fps: bool = True): fps_number = float(data_fps["data"]["fps"]) rt.frameRate = fps_number frame_range = get_frame_range() - frame_start_handle = frame_range["frameStart"] - int( - frame_range["handleStart"] - ) - frame_end_handle = frame_range["frameEnd"] + int(frame_range["handleEnd"]) - set_timeline(frame_start_handle, frame_end_handle) - set_render_frame_range(frame_start_handle, frame_end_handle) + + set_timeline( + frame_range["frameStartHandle"], frame_range["frameEndHandle"]) + set_render_frame_range( + frame_range["frameStartHandle"], frame_range["frameEndHandle"]) def set_context_setting(): diff --git a/openpype/hosts/max/plugins/publish/collect_frame_range.py b/openpype/hosts/max/plugins/publish/collect_frame_range.py new file mode 100644 index 0000000000..86fb6e856c --- /dev/null +++ b/openpype/hosts/max/plugins/publish/collect_frame_range.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +import pyblish.api +from pymxs import runtime as rt + + +class CollectFrameRange(pyblish.api.InstancePlugin): + """Collect Frame Range.""" + + order = pyblish.api.CollectorOrder + 0.01 + label = "Collect Frame Range" + hosts = ['max'] + families = ["camera", "maxrender", + "pointcache", "pointcloud", + "review", "redshiftproxy"] + + def process(self, instance): + if instance.data["family"] == "maxrender": + instance.data["frameStartHandle"] = int(rt.rendStart) + instance.data["frameEndHandle"] = int(rt.rendEnd) + else: + instance.data["frameStartHandle"] = int(rt.animationRange.start) + instance.data["frameEndHandle"] = int(rt.animationRange.end) diff --git a/openpype/hosts/max/plugins/publish/collect_render.py b/openpype/hosts/max/plugins/publish/collect_render.py index a359e61921..7765b3b924 100644 --- a/openpype/hosts/max/plugins/publish/collect_render.py +++ b/openpype/hosts/max/plugins/publish/collect_render.py @@ -14,7 +14,7 @@ from openpype.client import get_last_version_by_subset_name class CollectRender(pyblish.api.InstancePlugin): """Collect Render for Deadline""" - order = pyblish.api.CollectorOrder + 0.01 + order = pyblish.api.CollectorOrder + 0.02 label = "Collect 3dsmax Render Layers" hosts = ['max'] families = ["maxrender"] @@ -97,8 +97,8 @@ class CollectRender(pyblish.api.InstancePlugin): "renderer": renderer, "source": filepath, "plugin": "3dsmax", - "frameStart": int(rt.rendStart), - "frameEnd": int(rt.rendEnd), + "frameStart": instance.data["frameStartHandle"], + "frameEnd": instance.data["frameEndHandle"], "version": version_int, "farm": True } diff --git a/openpype/hosts/max/plugins/publish/collect_review.py b/openpype/hosts/max/plugins/publish/collect_review.py index 8e27a857d7..531521fa38 100644 --- a/openpype/hosts/max/plugins/publish/collect_review.py +++ b/openpype/hosts/max/plugins/publish/collect_review.py @@ -29,8 +29,8 @@ class CollectReview(pyblish.api.InstancePlugin, attr_values = self.get_attr_values_from_data(instance.data) data = { "review_camera": camera_name, - "frameStart": instance.context.data["frameStart"], - "frameEnd": instance.context.data["frameEnd"], + "frameStart": instance.data["frameStartHandle"], + "frameEnd": instance.data["frameEndHandle"], "fps": instance.context.data["fps"], "dspGeometry": attr_values.get("dspGeometry"), "dspShapes": attr_values.get("dspShapes"), diff --git a/openpype/hosts/max/plugins/publish/extract_camera_abc.py b/openpype/hosts/max/plugins/publish/extract_camera_abc.py index b1918c53e0..a42f27be6e 100644 --- a/openpype/hosts/max/plugins/publish/extract_camera_abc.py +++ b/openpype/hosts/max/plugins/publish/extract_camera_abc.py @@ -19,8 +19,8 @@ class ExtractCameraAlembic(publish.Extractor, OptionalPyblishPluginMixin): def process(self, instance): if not self.is_active(instance.data): return - start = float(instance.data.get("frameStartHandle", 1)) - end = float(instance.data.get("frameEndHandle", 1)) + start = instance.data["frameStartHandle"] + end = instance.data["frameEndHandle"] self.log.info("Extracting Camera ...") diff --git a/openpype/hosts/max/plugins/publish/extract_pointcache.py b/openpype/hosts/max/plugins/publish/extract_pointcache.py index c3de623bc0..f6a8500c08 100644 --- a/openpype/hosts/max/plugins/publish/extract_pointcache.py +++ b/openpype/hosts/max/plugins/publish/extract_pointcache.py @@ -51,8 +51,8 @@ class ExtractAlembic(publish.Extractor): families = ["pointcache"] def process(self, instance): - start = float(instance.data.get("frameStartHandle", 1)) - end = float(instance.data.get("frameEndHandle", 1)) + start = instance.data["frameStartHandle"] + end = instance.data["frameEndHandle"] self.log.debug("Extracting pointcache ...") diff --git a/openpype/hosts/max/plugins/publish/extract_pointcloud.py b/openpype/hosts/max/plugins/publish/extract_pointcloud.py index 190f049d23..d9fbe5e9dd 100644 --- a/openpype/hosts/max/plugins/publish/extract_pointcloud.py +++ b/openpype/hosts/max/plugins/publish/extract_pointcloud.py @@ -40,8 +40,8 @@ class ExtractPointCloud(publish.Extractor): def process(self, instance): self.settings = self.get_setting(instance) - start = int(instance.context.data.get("frameStart")) - end = int(instance.context.data.get("frameEnd")) + start = instance.data["frameStartHandle"] + end = instance.data["frameEndHandle"] self.log.info("Extracting PRT...") stagingdir = self.staging_dir(instance) diff --git a/openpype/hosts/max/plugins/publish/extract_redshift_proxy.py b/openpype/hosts/max/plugins/publish/extract_redshift_proxy.py index f67ed30c6b..47ed85977b 100644 --- a/openpype/hosts/max/plugins/publish/extract_redshift_proxy.py +++ b/openpype/hosts/max/plugins/publish/extract_redshift_proxy.py @@ -16,8 +16,8 @@ class ExtractRedshiftProxy(publish.Extractor): families = ["redshiftproxy"] def process(self, instance): - start = int(instance.context.data.get("frameStart")) - end = int(instance.context.data.get("frameEnd")) + start = instance.data["frameStartHandle"] + end = instance.data["frameEndHandle"] self.log.debug("Extracting Redshift Proxy...") stagingdir = self.staging_dir(instance) diff --git a/openpype/hosts/max/plugins/publish/extract_review_animation.py b/openpype/hosts/max/plugins/publish/extract_review_animation.py index 8e06e52b5c..af86ed7694 100644 --- a/openpype/hosts/max/plugins/publish/extract_review_animation.py +++ b/openpype/hosts/max/plugins/publish/extract_review_animation.py @@ -48,8 +48,8 @@ class ExtractReviewAnimation(publish.Extractor): "ext": instance.data["imageFormat"], "files": filenames, "stagingDir": staging_dir, - "frameStart": instance.data["frameStart"], - "frameEnd": instance.data["frameEnd"], + "frameStart": instance.data["frameStartHandle"], + "frameEnd": instance.data["frameEndHandle"], "tags": tags, "preview": True, "camera_name": review_camera diff --git a/openpype/hosts/max/plugins/publish/extract_thumbnail.py b/openpype/hosts/max/plugins/publish/extract_thumbnail.py index 82f4fc7a8b..0e7da89fa2 100644 --- a/openpype/hosts/max/plugins/publish/extract_thumbnail.py +++ b/openpype/hosts/max/plugins/publish/extract_thumbnail.py @@ -24,7 +24,7 @@ class ExtractThumbnail(publish.Extractor): f"Create temp directory {tmp_staging} for thumbnail" ) fps = int(instance.data["fps"]) - frame = int(instance.data["frameStart"]) + frame = int(instance.data["frameStartHandle"]) instance.context.data["cleanupFullPaths"].append(tmp_staging) filename = "{name}_thumbnail..png".format(**instance.data) filepath = os.path.join(tmp_staging, filename) diff --git a/openpype/hosts/max/plugins/publish/validate_animation_timeline.py b/openpype/hosts/max/plugins/publish/validate_animation_timeline.py deleted file mode 100644 index 2a9483c763..0000000000 --- a/openpype/hosts/max/plugins/publish/validate_animation_timeline.py +++ /dev/null @@ -1,48 +0,0 @@ -import pyblish.api - -from pymxs import runtime as rt -from openpype.pipeline.publish import ( - RepairAction, - ValidateContentsOrder, - PublishValidationError -) -from openpype.hosts.max.api.lib import get_frame_range, set_timeline - - -class ValidateAnimationTimeline(pyblish.api.InstancePlugin): - """ - Validates Animation Timeline for Preview Animation in Max - """ - - label = "Animation Timeline for Review" - order = ValidateContentsOrder - families = ["review"] - hosts = ["max"] - actions = [RepairAction] - - def process(self, instance): - frame_range = get_frame_range() - frame_start_handle = frame_range["frameStart"] - int( - frame_range["handleStart"] - ) - frame_end_handle = frame_range["frameEnd"] + int( - frame_range["handleEnd"] - ) - if rt.animationRange.start != frame_start_handle or ( - rt.animationRange.end != frame_end_handle - ): - raise PublishValidationError("Incorrect animation timeline " - "set for preview animation.. " - "\nYou can use repair action to " - "the correct animation timeline") - - @classmethod - def repair(cls, instance): - frame_range = get_frame_range() - frame_start_handle = frame_range["frameStart"] - int( - frame_range["handleStart"] - ) - frame_end_handle = frame_range["frameEnd"] + int( - frame_range["handleEnd"] - ) - set_timeline(frame_start_handle, frame_end_handle) diff --git a/openpype/hosts/max/plugins/publish/validate_frame_range.py b/openpype/hosts/max/plugins/publish/validate_frame_range.py index 21e847405e..0e8316e844 100644 --- a/openpype/hosts/max/plugins/publish/validate_frame_range.py +++ b/openpype/hosts/max/plugins/publish/validate_frame_range.py @@ -7,8 +7,10 @@ from openpype.pipeline import ( from openpype.pipeline.publish import ( RepairAction, ValidateContentsOrder, - PublishValidationError + PublishValidationError, + KnownPublishError ) +from openpype.hosts.max.api.lib import get_frame_range, set_timeline class ValidateFrameRange(pyblish.api.InstancePlugin, @@ -27,38 +29,60 @@ class ValidateFrameRange(pyblish.api.InstancePlugin, label = "Validate Frame Range" order = ValidateContentsOrder - families = ["maxrender"] + families = ["camera", "maxrender", + "pointcache", "pointcloud", + "review", "redshiftproxy"] hosts = ["max"] optional = True actions = [RepairAction] def process(self, instance): if not self.is_active(instance.data): - self.log.info("Skipping validation...") + self.log.debug("Skipping Validate Frame Range...") return - context = instance.context - frame_start = int(context.data.get("frameStart")) - frame_end = int(context.data.get("frameEnd")) - - inst_frame_start = int(instance.data.get("frameStart")) - inst_frame_end = int(instance.data.get("frameEnd")) + frame_range = get_frame_range( + asset_doc=instance.data["assetEntity"]) + inst_frame_start = instance.data.get("frameStartHandle") + inst_frame_end = instance.data.get("frameEndHandle") + if inst_frame_start is None or inst_frame_end is None: + raise KnownPublishError( + "Missing frame start and frame end on " + "instance to to validate." + ) + frame_start_handle = frame_range["frameStartHandle"] + frame_end_handle = frame_range["frameEndHandle"] errors = [] - if frame_start != inst_frame_start: + if frame_start_handle != inst_frame_start: errors.append( f"Start frame ({inst_frame_start}) on instance does not match " # noqa - f"with the start frame ({frame_start}) set on the asset data. ") # noqa - if frame_end != inst_frame_end: + f"with the start frame ({frame_start_handle}) set on the asset data. ") # noqa + if frame_end_handle != inst_frame_end: errors.append( f"End frame ({inst_frame_end}) on instance does not match " - f"with the end frame ({frame_start}) from the asset data. ") + f"with the end frame ({frame_end_handle}) " + "from the asset data. ") if errors: - errors.append("You can use repair action to fix it.") - raise PublishValidationError("\n".join(errors)) + bullet_point_errors = "\n".join( + "- {}".format(error) for error in errors + ) + report = ( + "Frame range settings are incorrect.\n\n" + f"{bullet_point_errors}\n\n" + "You can use repair action to fix it." + ) + raise PublishValidationError(report, title="Frame Range incorrect") @classmethod def repair(cls, instance): - rt.rendStart = instance.context.data.get("frameStart") - rt.rendEnd = instance.context.data.get("frameEnd") + frame_range = get_frame_range() + frame_start_handle = frame_range["frameStartHandle"] + frame_end_handle = frame_range["frameEndHandle"] + + if instance.data["family"] == "maxrender": + rt.rendStart = frame_start_handle + rt.rendEnd = frame_end_handle + else: + set_timeline(frame_start_handle, frame_end_handle)