diff --git a/openpype/hosts/max/api/lib.py b/openpype/hosts/max/api/lib.py index 88f1b35a14..995c35792a 100644 --- a/openpype/hosts/max/api/lib.py +++ b/openpype/hosts/max/api/lib.py @@ -250,7 +250,7 @@ def reset_frame_range(fps: bool = True): frame_range["handleStart"] ) frame_end_handle = frame_range["frameEnd"] + int(frame_range["handleEnd"]) - rt.interval(frame_start_handle, frame_end_handle) + set_timeline(frame_start_handle, frame_end_handle) set_render_frame_range(frame_start_handle, frame_end_handle) @@ -287,9 +287,20 @@ def get_max_version(): @contextlib.contextmanager def viewport_camera(camera): original = rt.viewport.getCamera() + if not original: + # if there is no original camera + # use the current camera as original + original = rt.getNodeByName(camera) review_camera = rt.getNodeByName(camera) try: rt.viewport.setCamera(review_camera) yield finally: rt.viewport.setCamera(original) + + +def set_timeline(frameStart, frameEnd): + """Set frame range for timeline editor in Max + """ + rt.animationRange = rt.interval(frameStart, frameEnd) + return rt.animationRange diff --git a/openpype/hosts/max/plugins/publish/extract_thumbnail.py b/openpype/hosts/max/plugins/publish/extract_thumbnail.py index 33f705fefd..cc943f388f 100644 --- a/openpype/hosts/max/plugins/publish/extract_thumbnail.py +++ b/openpype/hosts/max/plugins/publish/extract_thumbnail.py @@ -29,7 +29,7 @@ class ExtractThumbnail(publish.Extractor): filename = "{name}_thumbnail..jpg".format(**instance.data) filepath = os.path.join(tmp_staging, filename) filepath = filepath.replace("\\", "/") - thumbnail = self.get_filename(instance.name) + thumbnail = self.get_filename(instance.name, frame) self.log.debug( "Writing Thumbnail to" @@ -42,7 +42,7 @@ class ExtractThumbnail(publish.Extractor): representation = { "name": "thumbnail", - "ext": "jpg", + "ext": "png", "files": thumbnail, "stagingDir": tmp_staging, "thumbnail": True @@ -54,8 +54,11 @@ class ExtractThumbnail(publish.Extractor): instance.data["representations"] = [] instance.data["representations"].append(representation) - def get_filename(self, filename): - return f"{filename}_thumbnail.0001.jpg" + def get_filename(self, filename, target_frame): + thumbnail_name = "{}_thumbnail.{:04}.png".format( + filename, target_frame + ) + return thumbnail_name def set_preview_arg(self, instance, filepath, fps, frame): job_args = list() diff --git a/openpype/hosts/max/plugins/publish/validate_animation_timeline.py b/openpype/hosts/max/plugins/publish/validate_animation_timeline.py new file mode 100644 index 0000000000..249451680c --- /dev/null +++ b/openpype/hosts/max/plugins/publish/validate_animation_timeline.py @@ -0,0 +1,47 @@ +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 != rt.interval( + frame_start_handle, 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)