diff --git a/client/ayon_core/hosts/houdini/api/lib.py b/client/ayon_core/hosts/houdini/api/lib.py index 972b10ad1b..da1b21ad95 100644 --- a/client/ayon_core/hosts/houdini/api/lib.py +++ b/client/ayon_core/hosts/houdini/api/lib.py @@ -1001,6 +1001,82 @@ def add_self_publish_button(node): node.setParmTemplateGroup(template) +def get_scene_viewer(): + """ + Return an instance of a visible viewport. + + There may be many, some could be closed, any visible are current + + Returns: + Optional[hou.SceneViewer]: A scene viewer, if any. + """ + panes = hou.ui.paneTabs() + panes = [x for x in panes if x.type() == hou.paneTabType.SceneViewer] + panes = sorted(panes, key=lambda x: x.isCurrentTab()) + if panes: + return panes[-1] + + return None + + +def sceneview_snapshot( + sceneview, + filepath="$HIP/thumbnails/$HIPNAME.$F4.jpg", + frame_start=None, + frame_end=None): + """Take a snapshot of your scene view. + + It takes snapshot of your scene view for the given frame range. + So, it's capable of generating snapshots image sequence. + It works in different Houdini context e.g. Objects, Solaris + + Example: + This is how the function can be used:: + + from ayon_core.hosts.houdini.api import lib + sceneview = hou.ui.paneTabOfType(hou.paneTabType.SceneViewer) + lib.sceneview_snapshot(sceneview) + + Notes: + .png output will render poorly, so use .jpg. + + How it works: + Get the current sceneviewer (may be more than one or hidden) + and screengrab the perspective viewport to a file in the + publish location to be picked up with the publish. + + Credits: + https://www.sidefx.com/forum/topic/42808/?page=1#post-354796 + + Args: + sceneview (hou.SceneViewer): The scene view pane from which you want + to take a snapshot. + filepath (str): thumbnail filepath. it expects `$F4` token + when frame_end is bigger than frame_star other wise + each frame will override its predecessor. + frame_start (int): the frame at which snapshot starts + frame_end (int): the frame at which snapshot ends + """ + + if frame_start is None: + frame_start = hou.frame() + if frame_end is None: + frame_end = frame_start + + if not isinstance(sceneview, hou.SceneViewer): + log.debug("Wrong Input. {} is not of type hou.SceneViewer." + .format(sceneview)) + return + viewport = sceneview.curViewport() + + flip_settings = sceneview.flipbookSettings().stash() + flip_settings.frameRange((frame_start, frame_end)) + flip_settings.output(filepath) + flip_settings.outputToMPlay(False) + sceneview.flipbook(viewport, flip_settings) + log.debug("A snapshot of sceneview has been saved to: {}".format(filepath)) + + def update_content_on_context_change(): """Update all Creator instances to current asset""" host = registered_host() diff --git a/client/ayon_core/hosts/houdini/plugins/publish/extract_active_view_thumbnail.py b/client/ayon_core/hosts/houdini/plugins/publish/extract_active_view_thumbnail.py new file mode 100644 index 0000000000..aedcb1da02 --- /dev/null +++ b/client/ayon_core/hosts/houdini/plugins/publish/extract_active_view_thumbnail.py @@ -0,0 +1,55 @@ +import pyblish.api +import tempfile +from ayon_core.pipeline import publish +from ayon_core.hosts.houdini.api import lib +from ayon_core.hosts.houdini.api.pipeline import IS_HEADLESS + + +class ExtractActiveViewThumbnail(publish.Extractor): + """Set instance thumbnail to a screengrab of current active viewport. + + This makes it so that if an instance does not have a thumbnail set yet that + it will get a thumbnail of the currently active view at the time of + publishing as a fallback. + + """ + order = pyblish.api.ExtractorOrder + 0.49 + label = "Extract Active View Thumbnail" + families = ["workfile"] + hosts = ["houdini"] + + def process(self, instance): + if IS_HEADLESS: + self.log.debug( + "Skip extraction of active view thumbnail, due to being in" + "headless mode." + ) + return + + thumbnail = instance.data.get("thumbnailPath") + if thumbnail: + # A thumbnail was already set for this instance + return + + view_thumbnail = self.get_view_thumbnail(instance) + if not view_thumbnail: + return + self.log.debug("Setting instance thumbnail path to: {}" + .format(view_thumbnail) + ) + instance.data["thumbnailPath"] = view_thumbnail + + def get_view_thumbnail(self, instance): + + sceneview = lib.get_scene_viewer() + if sceneview is None: + self.log.debug("Skipping Extract Active View Thumbnail" + " because no scene view was detected.") + return + + with tempfile.NamedTemporaryFile("w", suffix=".jpg", delete=False) as tmp: + lib.sceneview_snapshot(sceneview, tmp.name) + thumbnail_path = tmp.name + + instance.context.data["cleanupFullPaths"].append(thumbnail_path) + return thumbnail_path