diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
index 73505368dd..5d4db81a77 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -35,6 +35,10 @@ body:
label: Version
description: What version are you running? Look to OpenPype Tray
options:
+ - 3.17.6-nightly.1
+ - 3.17.5
+ - 3.17.5-nightly.3
+ - 3.17.5-nightly.2
- 3.17.5-nightly.1
- 3.17.4
- 3.17.4-nightly.2
@@ -131,10 +135,6 @@ body:
- 3.15.1
- 3.15.1-nightly.6
- 3.15.1-nightly.5
- - 3.15.1-nightly.4
- - 3.15.1-nightly.3
- - 3.15.1-nightly.2
- - 3.15.1-nightly.1
validations:
required: true
- type: dropdown
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7432b33e24..b3daf581ac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,463 @@
# Changelog
+## [3.17.5](https://github.com/ynput/OpenPype/tree/3.17.5)
+
+
+[Full Changelog](https://github.com/ynput/OpenPype/compare/3.17.4...3.17.5)
+
+### **🆕 New features**
+
+
+
+Fusion: Add USD loader #4896
+
+Add an OpenPype managed USD loader (`uLoader`) for Fusion.
+
+
+___
+
+
+
+
+
+Fusion: Resolution validator #5325
+
+Added a resolution validator.The code is from my old PR (https://github.com/ynput/OpenPype/pull/4921) that I closed because the PR also contained a frame range validator that no longer is needed.
+
+
+___
+
+
+
+
+
+Context Selection tool: Refactor Context tool (for AYON) #5766
+
+Context selection tool has AYON variant.
+
+
+___
+
+
+
+
+
+AYON: Use AYON username for user in template data #5842
+
+Use ayon username for template data in AYON mode.
+
+
+___
+
+
+
+
+
+Testing: app_group flag #5869
+
+`app_group` command flag. This is for changing which flavour of the host to launch. In the case of Maya, you can launch Maya and MayaPy, but it can be used for the Nuke family as well.Split from #5644
+
+
+___
+
+
+
+### **🚀 Enhancements**
+
+
+
+Enhancement: Fusion fix saver creation + minor Blender/Fusion logging tweaks #5558
+
+- Blender change logs to `debug` level in preparation for new publisher artist facing reports (note that it currently still uses the old publisher)
+- Fusion: Create Saver fix redeclaration of default_variants
+- Fusion: Fix saver being created in incorrect state without saving directly after create
+- Fusion: Allow reset frame range on render family
+- Fusion: Tweak logging level for artist-facing report
+
+
+___
+
+
+
+
+
+Resolve: load clip to timeline at set time #5665
+
+It is possible to load clip to correct place on timeline.
+
+
+___
+
+
+
+
+
+Nuke: Optional Deadline workfile dependency. #5732
+
+Adds option to add the workfile as dependency for the Deadline job.Think it used to have something like this, but it disappeared. Usecase is for remote workflow where the Nuke script needs to be synced before the job can start.
+
+
+___
+
+
+
+
+
+Enhancement/houdini rearrange ayon houdini settings files #5748
+
+Rearranging Houdini Settings to be more readable, easier to edit, update settings (include all families/product types)This PR is mainly for Ayon Settings to have more organized files. For Openpype, I'll make sure that each Houdini setting in Ayon has an equivalent in Openpype.
+- [x] update Ayon settings, fix typos and remove deprecated settings.
+- [x] Sync with Openpype
+- [x] Test in Openpype
+- [x] Test in Ayon
+
+
+___
+
+
+
+
+
+Chore: updating create ayon addon script #5822
+
+Adding developers environment options.
+
+
+___
+
+
+
+
+
+Max: Implement Validator for Properties/Attributes Value Check #5824
+
+Add optional validator which can check if the property attributes are valid in Max
+
+
+___
+
+
+
+
+
+Nuke: Remove unused 'get_render_path' function #5826
+
+Remove unused function `get_render_path` from nuke integration.
+
+
+___
+
+
+
+
+
+Chore: Limit current context template data function #5845
+
+Current implementation of `get_current_context_template_data` does return the same values as base template data function `get_template_data`.
+
+
+___
+
+
+
+
+
+Max: Make sure Collect Render not ignoring instance asset #5847
+
+- Make sure Collect Render is not always using asset from context.
+- Make sure Scene version being collected
+- Clean up unnecessary uses of code in the collector.
+
+
+___
+
+
+
+
+
+Ftrack: Events are not processed if project is not available in OpenPype #5853
+
+Events that happened on project which is not in OpenPype is not processed.
+
+
+___
+
+
+
+
+
+Nuke: Add Nuke 11.0 as default setting #5855
+
+Found I needed Nuke 11.0 in the default settings to help with unit testing.
+
+
+___
+
+
+
+
+
+TVPaint: Code cleanup #5857
+
+Removed unused import. Use `AYON` label in ayon mode. Removed unused data in publish context `"previous_context"`.
+
+
+___
+
+
+
+
+
+AYON settings: Use correct label for follow workfile version #5874
+
+Follow workfile version label was marked as Collect Anatomy Instance Data label.
+
+
+___
+
+
+
+### **🐛 Bug fixes**
+
+
+
+Nuke: Fix workfile template builder so representations get loaded next to each other #5061
+
+Refactor when the cleanup of the placeholder happens for the cases where multiple representations are loaded by a single placeholder.The existing code didn't take into account the case where a template placeholder can load multiple representations so it was trying to do the cleanup of the placeholder node and the re-arrangement of the imported nodes too early. I assume this was designed only for the cases where a single representation can load multiple nodes.
+
+
+___
+
+
+
+
+
+Nuke: Dont update node name on update #5704
+
+When updating `Image` containers the code is trying to set the name of the node. This results in a warning message from Nuke shown below;Suggesting to not change the node name when updating.
+
+
+___
+
+
+
+
+
+UIDefLabel can be unique #5827
+
+`UILabelDef` have implemented comparison and uniqueness.
+
+
+___
+
+
+
+
+
+AYON: Skip kitsu module when creating ayon addons #5828
+
+Create AYON packages is skipping kitsu module in creation of modules/addons and kitsu module is not loaded from modules on start. The addon already has it's repository https://github.com/ynput/ayon-kitsu.
+
+
+___
+
+
+
+
+
+Bugfix: Collect Rendered Files only collecting first instance #5832
+
+Collect all instances from the metadata file - don't return on first instance iteration.
+
+
+___
+
+
+
+
+
+Houdini: set frame range for the created composite ROP #5833
+
+Quick bug fix for created composite ROP, set its frame range to the frame range of the playbar.
+
+
+___
+
+
+
+
+
+Fix registering launcher actions from OpenPypeModules #5843
+
+Fix typo `actions_dir` -> `path` to fix register launcher actions fromm OpenPypeModule
+
+
+___
+
+
+
+
+
+Bugfix in houdini shelves manager and beautify settings #5844
+
+This PR fixes the problem in this PR https://github.com/ynput/OpenPype/issues/5457 by using the right function to load a pre-made houdini `.shelf` fileAlso, it beautifies houdini shelves settings to provide better guidance for users which helps with other issue https://github.com/ynput/OpenPype/issues/5458 , Rather adding default shelf and set names, I'll educate users how to use the tool correctly.Users now are able to select between the two options.| OpenPype | Ayon || -- | -- || | |
+
+
+___
+
+
+
+
+
+Blender: Fix missing Grease Pencils in review #5848
+
+Fix Grease Pencil missing in review when isolating objects.
+
+
+___
+
+
+
+
+
+Blender: Fix Render Settings in Ayon #5849
+
+Fix Render Settings in Ayon for Blender.
+
+
+___
+
+
+
+
+
+Bugfix: houdini tab menu working as expected #5850
+
+This PR:Tab menu name changes to Ayon when using ayon get_network_categories is checked in all creator plugins. | Product | Network Category | | -- | -- | | Alembic camera | rop, obj | | Arnold Ass | rop | | Arnold ROP | rop | | Bgeo | rop, sop | | composite sequence | cop2, rop | | hda | obj | | Karma ROP | rop | | Mantra ROP | rop | | ABC | rop, sop | | RS proxy | rop, sop| | RS ROP | rop | | Review | rop | | Static mesh | rop, obj, sop | | USD | lop, rop | | USD Render | rop | | VDB | rop, obj, sop | | V Ray | rop |
+
+
+___
+
+
+
+
+
+Bigfix: Houdini skip frame_range_validator if node has no 'trange' parameter #5851
+
+I faced a bug when publishing HDA instance as it has no `trange` parameter. As this PR title says : skip frame_range_validator if node has no 'trange' parameter
+
+
+___
+
+
+
+
+
+Bugfix: houdini image sequence loading and missing frames #5852
+
+I made this PR in to fix issues mentioned here https://github.com/ynput/OpenPype/pull/5833#issuecomment-1789207727in short:
+- image load doesn't work
+- publisher only publish one frame
+
+
+___
+
+
+
+
+
+Nuke: loaders' containers updating as nodes #5854
+
+Nuke loaded containers are updating correctly even they have been duplicating of originally loaded nodes. This had previously been removed duplicated nodes.
+
+
+___
+
+
+
+
+
+deadline: settings are not blocking extension input #5864
+
+Settings are not blocking user input.
+
+
+___
+
+
+
+
+
+Blender: Fix loading of blend layouts #5866
+
+Fix a problem with loading blend layouts.
+
+
+___
+
+
+
+
+
+AYON: Launcher refresh issues #5867
+
+Fixed refresh of projects issue in launcher tool. And renamed Qt models to contain `Qt` in their name (it was really hard to find out where were used). It is not possible to click on disabled item in launcher's projects view.
+
+
+___
+
+
+
+
+
+Fix the Wrong key words for tycache workfile template settings in AYON #5870
+
+Fix the wrong key words for the tycache workfile template settings in AYON(i.e. Instead of families, product_types should be used)
+
+
+___
+
+
+
+
+
+AYON tools: Handle empty icon definition #5876
+
+Ignore if passed icon definition is `None`.
+
+
+___
+
+
+
+### **🔀 Refactored code**
+
+
+
+Houdini: Remove on instance toggled callback #5860
+
+Remove on instance toggled callback which isn't relevant to the new publisher
+
+
+___
+
+
+
+
+
+Chore: Remove unused `instanceToggled` callbacks #5862
+
+The `instanceToggled` callbacks should be irrelevant for new publisher.
+
+
+___
+
+
+
+
+
+
## [3.17.4](https://github.com/ynput/OpenPype/tree/3.17.4)
diff --git a/openpype/cli.py b/openpype/cli.py
index 7422f32f13..f0fe550a1f 100644
--- a/openpype/cli.py
+++ b/openpype/cli.py
@@ -282,6 +282,9 @@ def run(script):
"--app_variant",
help="Provide specific app variant for test, empty for latest",
default=None)
+@click.option("--app_group",
+ help="Provide specific app group for test, empty for default",
+ default=None)
@click.option("-t",
"--timeout",
help="Provide specific timeout value for test case",
@@ -294,11 +297,11 @@ def run(script):
help="MongoDB for testing.",
default=None)
def runtests(folder, mark, pyargs, test_data_folder, persist, app_variant,
- timeout, setup_only, mongo_url):
+ timeout, setup_only, mongo_url, app_group):
"""Run all automatic tests after proper initialization via start.py"""
PypeCommands().run_tests(folder, mark, pyargs, test_data_folder,
persist, app_variant, timeout, setup_only,
- mongo_url)
+ mongo_url, app_group)
@main.command(help="DEPRECATED - run sync server")
diff --git a/openpype/hosts/aftereffects/api/pipeline.py b/openpype/hosts/aftereffects/api/pipeline.py
index 8fc7a70dd8..e059f7c272 100644
--- a/openpype/hosts/aftereffects/api/pipeline.py
+++ b/openpype/hosts/aftereffects/api/pipeline.py
@@ -74,11 +74,6 @@ class AfterEffectsHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
register_loader_plugin_path(LOAD_PATH)
register_creator_plugin_path(CREATE_PATH)
- log.info(PUBLISH_PATH)
-
- pyblish.api.register_callback(
- "instanceToggled", on_pyblish_instance_toggled
- )
register_event_callback("application.launched", application_launch)
@@ -186,11 +181,6 @@ def application_launch():
check_inventory()
-def on_pyblish_instance_toggled(instance, old_value, new_value):
- """Toggle layer visibility on instance toggles."""
- instance[0].Visible = new_value
-
-
def ls():
"""Yields containers from active AfterEffects document.
diff --git a/openpype/hosts/blender/plugins/load/load_blend.py b/openpype/hosts/blender/plugins/load/load_blend.py
index 25d6568889..3d6b634916 100644
--- a/openpype/hosts/blender/plugins/load/load_blend.py
+++ b/openpype/hosts/blender/plugins/load/load_blend.py
@@ -32,7 +32,7 @@ class BlendLoader(plugin.AssetLoader):
empties = [obj for obj in objects if obj.type == 'EMPTY']
for empty in empties:
- if empty.get(AVALON_PROPERTY):
+ if empty.get(AVALON_PROPERTY) and empty.parent is None:
return empty
return None
@@ -90,6 +90,7 @@ class BlendLoader(plugin.AssetLoader):
members.append(data)
container = self._get_asset_container(data_to.objects)
+ print(container)
assert container, "No asset group found"
container.name = group_name
@@ -100,8 +101,11 @@ class BlendLoader(plugin.AssetLoader):
# Link all the container children to the collection
for obj in container.children_recursive:
+ print(obj)
bpy.context.scene.collection.objects.link(obj)
+ print("")
+
# Remove the library from the blend file
library = bpy.data.libraries.get(bpy.path.basename(libpath))
bpy.data.libraries.remove(library)
diff --git a/openpype/hosts/blender/plugins/publish/collect_review.py b/openpype/hosts/blender/plugins/publish/collect_review.py
index 3bf2e39e24..2760ab9811 100644
--- a/openpype/hosts/blender/plugins/publish/collect_review.py
+++ b/openpype/hosts/blender/plugins/publish/collect_review.py
@@ -31,11 +31,12 @@ class CollectReview(pyblish.api.InstancePlugin):
focal_length = cameras[0].data.lens
- # get isolate objects list from meshes instance members .
+ # get isolate objects list from meshes instance members.
+ types = {"MESH", "GPENCIL"}
isolate_objects = [
obj
for obj in instance
- if isinstance(obj, bpy.types.Object) and obj.type == "MESH"
+ if isinstance(obj, bpy.types.Object) and obj.type in types
]
if not instance.data.get("remove"):
diff --git a/openpype/hosts/blender/plugins/publish/extract_blend.py b/openpype/hosts/blender/plugins/publish/extract_blend.py
index c8eeef7fd7..645314e50e 100644
--- a/openpype/hosts/blender/plugins/publish/extract_blend.py
+++ b/openpype/hosts/blender/plugins/publish/extract_blend.py
@@ -28,16 +28,22 @@ class ExtractBlend(publish.Extractor):
for obj in instance:
data_blocks.add(obj)
# Pack used images in the blend files.
- if obj.type == 'MESH':
- for material_slot in obj.material_slots:
- mat = material_slot.material
- if mat and mat.use_nodes:
- tree = mat.node_tree
- if tree.type == 'SHADER':
- for node in tree.nodes:
- if node.bl_idname == 'ShaderNodeTexImage':
- if node.image:
- node.image.pack()
+ if obj.type != 'MESH':
+ continue
+ for material_slot in obj.material_slots:
+ mat = material_slot.material
+ if not(mat and mat.use_nodes):
+ continue
+ tree = mat.node_tree
+ if tree.type != 'SHADER':
+ continue
+ for node in tree.nodes:
+ if node.bl_idname != 'ShaderNodeTexImage':
+ continue
+ # Check if image is not packed already
+ # and pack it if not.
+ if node.image and node.image.packed_file is None:
+ node.image.pack()
bpy.data.libraries.write(filepath, data_blocks)
diff --git a/openpype/hosts/fusion/plugins/load/load_usd.py b/openpype/hosts/fusion/plugins/load/load_usd.py
new file mode 100644
index 0000000000..4f1813a646
--- /dev/null
+++ b/openpype/hosts/fusion/plugins/load/load_usd.py
@@ -0,0 +1,87 @@
+from openpype.pipeline import (
+ load,
+ get_representation_path,
+)
+from openpype.hosts.fusion.api import (
+ imprint_container,
+ get_current_comp,
+ comp_lock_and_undo_chunk
+)
+from openpype.hosts.fusion.api.lib import get_fusion_module
+
+
+class FusionLoadUSD(load.LoaderPlugin):
+ """Load USD into Fusion
+
+ Support for USD was added since Fusion 18.5
+ """
+
+ families = ["*"]
+ representations = ["*"]
+ extensions = {"usd", "usda", "usdz"}
+
+ label = "Load USD"
+ order = -10
+ icon = "code-fork"
+ color = "orange"
+
+ tool_type = "uLoader"
+
+ @classmethod
+ def apply_settings(cls, project_settings, system_settings):
+ super(FusionLoadUSD, cls).apply_settings(project_settings,
+ system_settings)
+ if cls.enabled:
+ # Enable only in Fusion 18.5+
+ fusion = get_fusion_module()
+ version = fusion.GetVersion()
+ major = version[1]
+ minor = version[2]
+ is_usd_supported = (major, minor) >= (18, 5)
+ cls.enabled = is_usd_supported
+
+ def load(self, context, name, namespace, data):
+ # Fallback to asset name when namespace is None
+ if namespace is None:
+ namespace = context['asset']['name']
+
+ # Create the Loader with the filename path set
+ comp = get_current_comp()
+ with comp_lock_and_undo_chunk(comp, "Create tool"):
+
+ path = self.fname
+
+ args = (-32768, -32768)
+ tool = comp.AddTool(self.tool_type, *args)
+ tool["Filename"] = path
+
+ imprint_container(tool,
+ name=name,
+ namespace=namespace,
+ context=context,
+ loader=self.__class__.__name__)
+
+ def switch(self, container, representation):
+ self.update(container, representation)
+
+ def update(self, container, representation):
+
+ tool = container["_tool"]
+ assert tool.ID == self.tool_type, f"Must be {self.tool_type}"
+ comp = tool.Comp()
+
+ path = get_representation_path(representation)
+
+ with comp_lock_and_undo_chunk(comp, "Update tool"):
+ tool["Filename"] = path
+
+ # Update the imprinted representation
+ tool.SetData("avalon.representation", str(representation["_id"]))
+
+ def remove(self, container):
+ tool = container["_tool"]
+ assert tool.ID == self.tool_type, f"Must be {self.tool_type}"
+ comp = tool.Comp()
+
+ with comp_lock_and_undo_chunk(comp, "Remove tool"):
+ tool.Delete()
diff --git a/openpype/hosts/houdini/api/creator_node_shelves.py b/openpype/hosts/houdini/api/creator_node_shelves.py
index 1f9fef7417..14662dc419 100644
--- a/openpype/hosts/houdini/api/creator_node_shelves.py
+++ b/openpype/hosts/houdini/api/creator_node_shelves.py
@@ -173,6 +173,7 @@ def install():
os.remove(filepath)
icon = get_openpype_icon_filepath()
+ tab_menu_label = os.environ.get("AVALON_LABEL") or "AYON"
# Create context only to get creator plugins, so we don't reset and only
# populate what we need to retrieve the list of creator plugins
@@ -197,14 +198,14 @@ def install():
if not network_categories:
continue
- key = "openpype_create.{}".format(identifier)
+ key = "ayon_create.{}".format(identifier)
log.debug(f"Registering {key}")
script = CREATE_SCRIPT.format(identifier=identifier)
data = {
"script": script,
"language": hou.scriptLanguage.Python,
"icon": icon,
- "help": "Create OpenPype publish instance for {}".format(
+ "help": "Create Ayon publish instance for {}".format(
creator.label
),
"help_url": None,
@@ -213,7 +214,7 @@ def install():
"cop_viewer_categories": [],
"network_op_type": None,
"viewer_op_type": None,
- "locations": ["OpenPype"]
+ "locations": [tab_menu_label]
}
label = "Create {}".format(creator.label)
tool = hou.shelves.tool(key)
diff --git a/openpype/hosts/houdini/api/lib.py b/openpype/hosts/houdini/api/lib.py
index cadeaa8ed4..8b058e605d 100644
--- a/openpype/hosts/houdini/api/lib.py
+++ b/openpype/hosts/houdini/api/lib.py
@@ -11,20 +11,21 @@ import json
import six
from openpype.lib import StringTemplate
-from openpype.client import get_asset_by_name
+from openpype.client import get_project, get_asset_by_name
from openpype.settings import get_current_project_settings
from openpype.pipeline import (
+ Anatomy,
get_current_project_name,
get_current_asset_name,
- registered_host
-)
-from openpype.pipeline.context_tools import (
- get_current_context_template_data,
- get_current_project_asset
+ registered_host,
+ get_current_context,
+ get_current_host_name,
)
+from openpype.pipeline.create import CreateContext
+from openpype.pipeline.template_data import get_template_data
+from openpype.pipeline.context_tools import get_current_project_asset
from openpype.widgets import popup
from openpype.tools.utils.host_tools import get_tool_by_name
-from openpype.pipeline.create import CreateContext
import hou
@@ -568,9 +569,9 @@ def get_template_from_value(key, value):
return parm
-def get_frame_data(node, handle_start=0, handle_end=0, log=None):
- """Get the frame data: start frame, end frame, steps,
- start frame with start handle and end frame with end handle.
+def get_frame_data(node, log=None):
+ """Get the frame data: `frameStartHandle`, `frameEndHandle`
+ and `byFrameStep`.
This function uses Houdini node's `trange`, `t1, `t2` and `t3`
parameters as the source of truth for the full inclusive frame
@@ -578,20 +579,17 @@ def get_frame_data(node, handle_start=0, handle_end=0, log=None):
range including the handles.
The non-inclusive frame start and frame end without handles
- are computed by subtracting the handles from the inclusive
+ can be computed by subtracting the handles from the inclusive
frame range.
Args:
node (hou.Node): ROP node to retrieve frame range from,
the frame range is assumed to be the frame range
*including* the start and end handles.
- handle_start (int): Start handles.
- handle_end (int): End handles.
- log (logging.Logger): Logger to log to.
Returns:
- dict: frame data for start, end, steps,
- start with handle and end with handle
+ dict: frame data for `frameStartHandle`, `frameEndHandle`
+ and `byFrameStep`.
"""
@@ -622,11 +620,6 @@ def get_frame_data(node, handle_start=0, handle_end=0, log=None):
data["frameEndHandle"] = int(node.evalParm("f2"))
data["byFrameStep"] = node.evalParm("f3")
- data["handleStart"] = handle_start
- data["handleEnd"] = handle_end
- data["frameStart"] = data["frameStartHandle"] + data["handleStart"]
- data["frameEnd"] = data["frameEndHandle"] - data["handleEnd"]
-
return data
@@ -804,6 +797,45 @@ def get_camera_from_container(container):
return cameras[0]
+def get_current_context_template_data_with_asset_data():
+ """
+ TODOs:
+ Support both 'assetData' and 'folderData' in future.
+ """
+
+ context = get_current_context()
+ project_name = context["project_name"]
+ asset_name = context["asset_name"]
+ task_name = context["task_name"]
+ host_name = get_current_host_name()
+
+ anatomy = Anatomy(project_name)
+ project_doc = get_project(project_name)
+ asset_doc = get_asset_by_name(project_name, asset_name)
+
+ # get context specific vars
+ asset_data = asset_doc["data"]
+
+ # compute `frameStartHandle` and `frameEndHandle`
+ frame_start = asset_data.get("frameStart")
+ frame_end = asset_data.get("frameEnd")
+ handle_start = asset_data.get("handleStart")
+ handle_end = asset_data.get("handleEnd")
+ if frame_start is not None and handle_start is not None:
+ asset_data["frameStartHandle"] = frame_start - handle_start
+
+ if frame_end is not None and handle_end is not None:
+ asset_data["frameEndHandle"] = frame_end + handle_end
+
+ template_data = get_template_data(
+ project_doc, asset_doc, task_name, host_name
+ )
+ template_data["root"] = anatomy.roots
+ template_data["assetData"] = asset_data
+
+ return template_data
+
+
def get_context_var_changes():
"""get context var changes."""
@@ -823,7 +855,7 @@ def get_context_var_changes():
return houdini_vars_to_update
# Get Template data
- template_data = get_current_context_template_data()
+ template_data = get_current_context_template_data_with_asset_data()
# Set Houdini Vars
for item in houdini_vars:
@@ -978,7 +1010,7 @@ def self_publish():
def add_self_publish_button(node):
"""Adds a self publish button to the rop node."""
- label = os.environ.get("AVALON_LABEL") or "OpenPype"
+ label = os.environ.get("AVALON_LABEL") or "AYON"
button_parm = hou.ButtonParmTemplate(
"ayon_self_publish",
diff --git a/openpype/hosts/houdini/api/pipeline.py b/openpype/hosts/houdini/api/pipeline.py
index f8db45c56b..11135e20b2 100644
--- a/openpype/hosts/houdini/api/pipeline.py
+++ b/openpype/hosts/houdini/api/pipeline.py
@@ -3,7 +3,6 @@
import os
import sys
import logging
-import contextlib
import hou # noqa
@@ -66,10 +65,6 @@ class HoudiniHost(HostBase, IWorkfileHost, ILoadHost, IPublishHost):
register_event_callback("open", on_open)
register_event_callback("new", on_new)
- pyblish.api.register_callback(
- "instanceToggled", on_pyblish_instance_toggled
- )
-
self._has_been_setup = True
# add houdini vendor packages
hou_pythonpath = os.path.join(HOUDINI_HOST_DIR, "vendor")
@@ -406,54 +401,3 @@ def _set_context_settings():
lib.reset_framerange()
lib.update_houdini_vars_context()
-
-
-def on_pyblish_instance_toggled(instance, new_value, old_value):
- """Toggle saver tool passthrough states on instance toggles."""
- @contextlib.contextmanager
- def main_take(no_update=True):
- """Enter root take during context"""
- original_take = hou.takes.currentTake()
- original_update_mode = hou.updateModeSetting()
- root = hou.takes.rootTake()
- has_changed = False
- try:
- if original_take != root:
- has_changed = True
- if no_update:
- hou.setUpdateMode(hou.updateMode.Manual)
- hou.takes.setCurrentTake(root)
- yield
- finally:
- if has_changed:
- if no_update:
- hou.setUpdateMode(original_update_mode)
- hou.takes.setCurrentTake(original_take)
-
- if not instance.data.get("_allowToggleBypass", True):
- return
-
- nodes = instance[:]
- if not nodes:
- return
-
- # Assume instance node is first node
- instance_node = nodes[0]
-
- if not hasattr(instance_node, "isBypassed"):
- # Likely not a node that can actually be bypassed
- log.debug("Can't bypass node: %s", instance_node.path())
- return
-
- if instance_node.isBypassed() != (not old_value):
- print("%s old bypass state didn't match old instance state, "
- "updating anyway.." % instance_node.path())
-
- try:
- # Go into the main take, because when in another take changing
- # the bypass state of a note cannot be done due to it being locked
- # by default.
- with main_take(no_update=True):
- instance_node.bypass(not new_value)
- except hou.PermissionError as exc:
- log.warning("%s - %s", instance_node.path(), exc)
diff --git a/openpype/hosts/houdini/api/shelves.py b/openpype/hosts/houdini/api/shelves.py
index 4b5ebd4202..5093a90988 100644
--- a/openpype/hosts/houdini/api/shelves.py
+++ b/openpype/hosts/houdini/api/shelves.py
@@ -7,10 +7,11 @@ from openpype.settings import get_project_settings
from openpype.pipeline import get_current_project_name
from openpype.lib import StringTemplate
-from openpype.pipeline.context_tools import get_current_context_template_data
import hou
+from .lib import get_current_context_template_data_with_asset_data
+
log = logging.getLogger("openpype.hosts.houdini.shelves")
@@ -23,29 +24,33 @@ def generate_shelves():
# load configuration of houdini shelves
project_name = get_current_project_name()
project_settings = get_project_settings(project_name)
- shelves_set_config = project_settings["houdini"]["shelves"]
+ shelves_configs = project_settings["houdini"]["shelves"]
- if not shelves_set_config:
+ if not shelves_configs:
log.debug("No custom shelves found in project settings.")
return
# Get Template data
- template_data = get_current_context_template_data()
+ template_data = get_current_context_template_data_with_asset_data()
+
+ for config in shelves_configs:
+ selected_option = config["options"]
+ shelf_set_config = config[selected_option]
- for shelf_set_config in shelves_set_config:
shelf_set_filepath = shelf_set_config.get('shelf_set_source_path')
- shelf_set_os_filepath = shelf_set_filepath[current_os]
- if shelf_set_os_filepath:
- shelf_set_os_filepath = get_path_using_template_data(
- shelf_set_os_filepath, template_data
- )
- if not os.path.isfile(shelf_set_os_filepath):
- log.error("Shelf path doesn't exist - "
- "{}".format(shelf_set_os_filepath))
- continue
+ if shelf_set_filepath:
+ shelf_set_os_filepath = shelf_set_filepath[current_os]
+ if shelf_set_os_filepath:
+ shelf_set_os_filepath = get_path_using_template_data(
+ shelf_set_os_filepath, template_data
+ )
+ if not os.path.isfile(shelf_set_os_filepath):
+ log.error("Shelf path doesn't exist - "
+ "{}".format(shelf_set_os_filepath))
+ continue
- hou.shelves.newShelfSet(file_path=shelf_set_os_filepath)
- continue
+ hou.shelves.loadFile(shelf_set_os_filepath)
+ continue
shelf_set_name = shelf_set_config.get('shelf_set_name')
if not shelf_set_name:
diff --git a/openpype/hosts/houdini/plugins/create/create_bgeo.py b/openpype/hosts/houdini/plugins/create/create_bgeo.py
index a3f31e7e94..0f629cf9c9 100644
--- a/openpype/hosts/houdini/plugins/create/create_bgeo.py
+++ b/openpype/hosts/houdini/plugins/create/create_bgeo.py
@@ -3,6 +3,7 @@
from openpype.hosts.houdini.api import plugin
from openpype.pipeline import CreatedInstance, CreatorError
from openpype.lib import EnumDef
+import hou
class CreateBGEO(plugin.HoudiniCreator):
@@ -13,7 +14,6 @@ class CreateBGEO(plugin.HoudiniCreator):
icon = "gears"
def create(self, subset_name, instance_data, pre_create_data):
- import hou
instance_data.pop("active", None)
@@ -90,3 +90,9 @@ class CreateBGEO(plugin.HoudiniCreator):
return attrs + [
EnumDef("bgeo_type", bgeo_enum, label="BGEO Options"),
]
+
+ def get_network_categories(self):
+ return [
+ hou.ropNodeTypeCategory(),
+ hou.sopNodeTypeCategory()
+ ]
diff --git a/openpype/hosts/houdini/plugins/create/create_composite.py b/openpype/hosts/houdini/plugins/create/create_composite.py
index 9d4f7969bb..52ea6fa054 100644
--- a/openpype/hosts/houdini/plugins/create/create_composite.py
+++ b/openpype/hosts/houdini/plugins/create/create_composite.py
@@ -45,6 +45,11 @@ class CreateCompositeSequence(plugin.HoudiniCreator):
instance_node.setParms(parms)
+ # Manually set f1 & f2 to $FSTART and $FEND respectively
+ # to match other Houdini nodes default.
+ instance_node.parm("f1").setExpression("$FSTART")
+ instance_node.parm("f2").setExpression("$FEND")
+
# Lock any parameters in this list
to_lock = ["prim_to_detail_pattern"]
self.lock_parameters(instance_node, to_lock)
diff --git a/openpype/hosts/houdini/plugins/create/create_hda.py b/openpype/hosts/houdini/plugins/create/create_hda.py
index c4093bfbc6..ac075d2072 100644
--- a/openpype/hosts/houdini/plugins/create/create_hda.py
+++ b/openpype/hosts/houdini/plugins/create/create_hda.py
@@ -5,6 +5,7 @@ from openpype.client import (
get_subsets,
)
from openpype.hosts.houdini.api import plugin
+import hou
class CreateHDA(plugin.HoudiniCreator):
@@ -35,7 +36,6 @@ class CreateHDA(plugin.HoudiniCreator):
def create_instance_node(
self, node_name, parent, node_type="geometry"):
- import hou
parent_node = hou.node("/obj")
if self.selected_nodes:
@@ -81,3 +81,8 @@ class CreateHDA(plugin.HoudiniCreator):
pre_create_data) # type: plugin.CreatedInstance
return instance
+
+ def get_network_categories(self):
+ return [
+ hou.objNodeTypeCategory()
+ ]
diff --git a/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py b/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py
index b814dd9d57..3a4ab7008b 100644
--- a/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py
+++ b/openpype/hosts/houdini/plugins/create/create_redshift_proxy.py
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating Redshift proxies."""
from openpype.hosts.houdini.api import plugin
-from openpype.pipeline import CreatedInstance
+import hou
class CreateRedshiftProxy(plugin.HoudiniCreator):
@@ -12,7 +12,7 @@ class CreateRedshiftProxy(plugin.HoudiniCreator):
icon = "magic"
def create(self, subset_name, instance_data, pre_create_data):
- import hou # noqa
+
# Remove the active, we are checking the bypass flag of the nodes
instance_data.pop("active", None)
@@ -28,7 +28,7 @@ class CreateRedshiftProxy(plugin.HoudiniCreator):
instance = super(CreateRedshiftProxy, self).create(
subset_name,
instance_data,
- pre_create_data) # type: CreatedInstance
+ pre_create_data)
instance_node = hou.node(instance.get("instance_node"))
@@ -44,3 +44,9 @@ class CreateRedshiftProxy(plugin.HoudiniCreator):
# Lock some Avalon attributes
to_lock = ["family", "id", "prim_to_detail_pattern"]
self.lock_parameters(instance_node, to_lock)
+
+ def get_network_categories(self):
+ return [
+ hou.ropNodeTypeCategory(),
+ hou.sopNodeTypeCategory()
+ ]
diff --git a/openpype/hosts/houdini/plugins/create/create_staticmesh.py b/openpype/hosts/houdini/plugins/create/create_staticmesh.py
index ea0b36f03f..d0985198bd 100644
--- a/openpype/hosts/houdini/plugins/create/create_staticmesh.py
+++ b/openpype/hosts/houdini/plugins/create/create_staticmesh.py
@@ -54,6 +54,7 @@ class CreateStaticMesh(plugin.HoudiniCreator):
def get_network_categories(self):
return [
hou.ropNodeTypeCategory(),
+ hou.objNodeTypeCategory(),
hou.sopNodeTypeCategory()
]
diff --git a/openpype/hosts/houdini/plugins/create/create_vbd_cache.py b/openpype/hosts/houdini/plugins/create/create_vbd_cache.py
index 9c96e48e3a..69418f9575 100644
--- a/openpype/hosts/houdini/plugins/create/create_vbd_cache.py
+++ b/openpype/hosts/houdini/plugins/create/create_vbd_cache.py
@@ -40,6 +40,7 @@ class CreateVDBCache(plugin.HoudiniCreator):
def get_network_categories(self):
return [
hou.ropNodeTypeCategory(),
+ hou.objNodeTypeCategory(),
hou.sopNodeTypeCategory()
]
diff --git a/openpype/hosts/houdini/plugins/load/load_image.py b/openpype/hosts/houdini/plugins/load/load_image.py
index 663a93e48b..cff2b74e52 100644
--- a/openpype/hosts/houdini/plugins/load/load_image.py
+++ b/openpype/hosts/houdini/plugins/load/load_image.py
@@ -119,7 +119,8 @@ class ImageLoader(load.LoaderPlugin):
if not parent.children():
parent.destroy()
- def _get_file_sequence(self, root):
+ def _get_file_sequence(self, file_path):
+ root = os.path.dirname(file_path)
files = sorted(os.listdir(root))
first_fname = files[0]
diff --git a/openpype/hosts/houdini/plugins/publish/collect_arnold_rop.py b/openpype/hosts/houdini/plugins/publish/collect_arnold_rop.py
index b489f83b29..d95f763826 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_arnold_rop.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_arnold_rop.py
@@ -21,8 +21,8 @@ class CollectArnoldROPRenderProducts(pyblish.api.InstancePlugin):
label = "Arnold ROP Render Products"
# This specific order value is used so that
- # this plugin runs after CollectRopFrameRange
- order = pyblish.api.CollectorOrder + 0.4999
+ # this plugin runs after CollectFrames
+ order = pyblish.api.CollectorOrder + 0.11
hosts = ["houdini"]
families = ["arnold_rop"]
diff --git a/openpype/hosts/houdini/plugins/publish/collect_asset_handles.py b/openpype/hosts/houdini/plugins/publish/collect_asset_handles.py
new file mode 100644
index 0000000000..67a281639d
--- /dev/null
+++ b/openpype/hosts/houdini/plugins/publish/collect_asset_handles.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+"""Collector plugin for frames data on ROP instances."""
+import hou # noqa
+import pyblish.api
+from openpype.lib import BoolDef
+from openpype.pipeline import OpenPypePyblishPluginMixin
+
+
+class CollectAssetHandles(pyblish.api.InstancePlugin,
+ OpenPypePyblishPluginMixin):
+ """Apply asset handles.
+
+ If instance does not have:
+ - frameStart
+ - frameEnd
+ - handleStart
+ - handleEnd
+ But it does have:
+ - frameStartHandle
+ - frameEndHandle
+
+ Then we will retrieve the asset's handles to compute
+ the exclusive frame range and actual handle ranges.
+ """
+
+ hosts = ["houdini"]
+
+ # This specific order value is used so that
+ # this plugin runs after CollectAnatomyInstanceData
+ order = pyblish.api.CollectorOrder + 0.499
+
+ label = "Collect Asset Handles"
+ use_asset_handles = True
+
+ def process(self, instance):
+ # Only process instances without already existing handles data
+ # but that do have frameStartHandle and frameEndHandle defined
+ # like the data collected from CollectRopFrameRange
+ if "frameStartHandle" not in instance.data:
+ return
+ if "frameEndHandle" not in instance.data:
+ return
+
+ has_existing_data = {
+ "handleStart",
+ "handleEnd",
+ "frameStart",
+ "frameEnd"
+ }.issubset(instance.data)
+ if has_existing_data:
+ return
+
+ attr_values = self.get_attr_values_from_data(instance.data)
+ if attr_values.get("use_handles", self.use_asset_handles):
+ asset_data = instance.data["assetEntity"]["data"]
+ handle_start = asset_data.get("handleStart", 0)
+ handle_end = asset_data.get("handleEnd", 0)
+ else:
+ handle_start = 0
+ handle_end = 0
+
+ frame_start = instance.data["frameStartHandle"] + handle_start
+ frame_end = instance.data["frameEndHandle"] - handle_end
+
+ instance.data.update({
+ "handleStart": handle_start,
+ "handleEnd": handle_end,
+ "frameStart": frame_start,
+ "frameEnd": frame_end
+ })
+
+ # Log debug message about the collected frame range
+ if attr_values.get("use_handles", self.use_asset_handles):
+ self.log.debug(
+ "Full Frame range with Handles "
+ "[{frame_start_handle} - {frame_end_handle}]"
+ .format(
+ frame_start_handle=instance.data["frameStartHandle"],
+ frame_end_handle=instance.data["frameEndHandle"]
+ )
+ )
+ else:
+ self.log.debug(
+ "Use handles is deactivated for this instance, "
+ "start and end handles are set to 0."
+ )
+
+ # Log collected frame range to the user
+ message = "Frame range [{frame_start} - {frame_end}]".format(
+ frame_start=frame_start,
+ frame_end=frame_end
+ )
+ if handle_start or handle_end:
+ message += " with handles [{handle_start}]-[{handle_end}]".format(
+ handle_start=handle_start,
+ handle_end=handle_end
+ )
+ self.log.info(message)
+
+ if instance.data.get("byFrameStep", 1.0) != 1.0:
+ self.log.info(
+ "Frame steps {}".format(instance.data["byFrameStep"]))
+
+ # Add frame range to label if the instance has a frame range.
+ label = instance.data.get("label", instance.data["name"])
+ instance.data["label"] = (
+ "{label} [{frame_start_handle} - {frame_end_handle}]"
+ .format(
+ label=label,
+ frame_start_handle=instance.data["frameStartHandle"],
+ frame_end_handle=instance.data["frameEndHandle"]
+ )
+ )
+
+ @classmethod
+ def get_attribute_defs(cls):
+ return [
+ BoolDef("use_handles",
+ tooltip="Disable this if you want the publisher to"
+ " ignore start and end handles specified in the"
+ " asset data for this publish instance",
+ default=cls.use_asset_handles,
+ label="Use asset handles")
+ ]
diff --git a/openpype/hosts/houdini/plugins/publish/collect_frames.py b/openpype/hosts/houdini/plugins/publish/collect_frames.py
index 01df809d4c..cdef642174 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_frames.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_frames.py
@@ -11,7 +11,9 @@ from openpype.hosts.houdini.api import lib
class CollectFrames(pyblish.api.InstancePlugin):
"""Collect all frames which would be saved from the ROP nodes"""
- order = pyblish.api.CollectorOrder + 0.01
+ # This specific order value is used so that
+ # this plugin runs after CollectRopFrameRange
+ order = pyblish.api.CollectorOrder + 0.1
label = "Collect Frames"
families = ["vdbcache", "imagesequence", "ass",
"redshiftproxy", "review", "bgeo"]
@@ -20,8 +22,8 @@ class CollectFrames(pyblish.api.InstancePlugin):
ropnode = hou.node(instance.data["instance_node"])
- start_frame = instance.data.get("frameStart", None)
- end_frame = instance.data.get("frameEnd", None)
+ start_frame = instance.data.get("frameStartHandle", None)
+ end_frame = instance.data.get("frameEndHandle", None)
output_parm = lib.get_output_parameter(ropnode)
if start_frame is not None:
diff --git a/openpype/hosts/houdini/plugins/publish/collect_instances_usd_layered.py b/openpype/hosts/houdini/plugins/publish/collect_instances_usd_layered.py
index 0600730d00..d154cdc7c0 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_instances_usd_layered.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_instances_usd_layered.py
@@ -122,10 +122,6 @@ class CollectInstancesUsdLayered(pyblish.api.ContextPlugin):
instance.data.update(save_data)
instance.data["usdLayer"] = layer
- # Don't allow the Pyblish `instanceToggled` we have installed
- # to set this node to bypass.
- instance.data["_allowToggleBypass"] = False
-
instances.append(instance)
# Store the collected ROP node dependencies
diff --git a/openpype/hosts/houdini/plugins/publish/collect_karma_rop.py b/openpype/hosts/houdini/plugins/publish/collect_karma_rop.py
index fe0b8711fc..dac350a6ef 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_karma_rop.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_karma_rop.py
@@ -25,8 +25,8 @@ class CollectKarmaROPRenderProducts(pyblish.api.InstancePlugin):
label = "Karma ROP Render Products"
# This specific order value is used so that
- # this plugin runs after CollectRopFrameRange
- order = pyblish.api.CollectorOrder + 0.4999
+ # this plugin runs after CollectFrames
+ order = pyblish.api.CollectorOrder + 0.11
hosts = ["houdini"]
families = ["karma_rop"]
diff --git a/openpype/hosts/houdini/plugins/publish/collect_mantra_rop.py b/openpype/hosts/houdini/plugins/publish/collect_mantra_rop.py
index cc412f30a1..a3e7927807 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_mantra_rop.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_mantra_rop.py
@@ -25,8 +25,8 @@ class CollectMantraROPRenderProducts(pyblish.api.InstancePlugin):
label = "Mantra ROP Render Products"
# This specific order value is used so that
- # this plugin runs after CollectRopFrameRange
- order = pyblish.api.CollectorOrder + 0.4999
+ # this plugin runs after CollectFrames
+ order = pyblish.api.CollectorOrder + 0.11
hosts = ["houdini"]
families = ["mantra_rop"]
diff --git a/openpype/hosts/houdini/plugins/publish/collect_redshift_rop.py b/openpype/hosts/houdini/plugins/publish/collect_redshift_rop.py
index deb9eac971..0acddab011 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_redshift_rop.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_redshift_rop.py
@@ -25,8 +25,8 @@ class CollectRedshiftROPRenderProducts(pyblish.api.InstancePlugin):
label = "Redshift ROP Render Products"
# This specific order value is used so that
- # this plugin runs after CollectRopFrameRange
- order = pyblish.api.CollectorOrder + 0.4999
+ # this plugin runs after CollectFrames
+ order = pyblish.api.CollectorOrder + 0.11
hosts = ["houdini"]
families = ["redshift_rop"]
diff --git a/openpype/hosts/houdini/plugins/publish/collect_review_data.py b/openpype/hosts/houdini/plugins/publish/collect_review_data.py
index 3efb75e66c..9671945b9a 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_review_data.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_review_data.py
@@ -6,6 +6,8 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin):
"""Collect Review Data."""
label = "Collect Review Data"
+ # This specific order value is used so that
+ # this plugin runs after CollectRopFrameRange
order = pyblish.api.CollectorOrder + 0.1
hosts = ["houdini"]
families = ["review"]
@@ -41,8 +43,8 @@ class CollectHoudiniReviewData(pyblish.api.InstancePlugin):
return
if focal_length_parm.isTimeDependent():
- start = instance.data["frameStart"]
- end = instance.data["frameEnd"] + 1
+ start = instance.data["frameStartHandle"]
+ end = instance.data["frameEndHandle"] + 1
focal_length = [
focal_length_parm.evalAsFloatAtFrame(t)
for t in range(int(start), int(end))
diff --git a/openpype/hosts/houdini/plugins/publish/collect_rop_frame_range.py b/openpype/hosts/houdini/plugins/publish/collect_rop_frame_range.py
index 186244fedd..1e6bc3b16e 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_rop_frame_range.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_rop_frame_range.py
@@ -2,22 +2,15 @@
"""Collector plugin for frames data on ROP instances."""
import hou # noqa
import pyblish.api
-from openpype.lib import BoolDef
from openpype.hosts.houdini.api import lib
-from openpype.pipeline import OpenPypePyblishPluginMixin
-class CollectRopFrameRange(pyblish.api.InstancePlugin,
- OpenPypePyblishPluginMixin):
-
+class CollectRopFrameRange(pyblish.api.InstancePlugin):
"""Collect all frames which would be saved from the ROP nodes"""
hosts = ["houdini"]
- # This specific order value is used so that
- # this plugin runs after CollectAnatomyInstanceData
- order = pyblish.api.CollectorOrder + 0.499
+ order = pyblish.api.CollectorOrder
label = "Collect RopNode Frame Range"
- use_asset_handles = True
def process(self, instance):
@@ -30,78 +23,16 @@ class CollectRopFrameRange(pyblish.api.InstancePlugin,
return
ropnode = hou.node(node_path)
-
- attr_values = self.get_attr_values_from_data(instance.data)
-
- if attr_values.get("use_handles", self.use_asset_handles):
- asset_data = instance.data["assetEntity"]["data"]
- handle_start = asset_data.get("handleStart", 0)
- handle_end = asset_data.get("handleEnd", 0)
- else:
- handle_start = 0
- handle_end = 0
-
frame_data = lib.get_frame_data(
- ropnode, handle_start, handle_end, self.log
+ ropnode, self.log
)
if not frame_data:
return
# Log debug message about the collected frame range
- frame_start = frame_data["frameStart"]
- frame_end = frame_data["frameEnd"]
-
- if attr_values.get("use_handles", self.use_asset_handles):
- self.log.debug(
- "Full Frame range with Handles "
- "[{frame_start_handle} - {frame_end_handle}]"
- .format(
- frame_start_handle=frame_data["frameStartHandle"],
- frame_end_handle=frame_data["frameEndHandle"]
- )
- )
- else:
- self.log.debug(
- "Use handles is deactivated for this instance, "
- "start and end handles are set to 0."
- )
-
- # Log collected frame range to the user
- message = "Frame range [{frame_start} - {frame_end}]".format(
- frame_start=frame_start,
- frame_end=frame_end
+ self.log.debug(
+ "Collected frame_data: {}".format(frame_data)
)
- if handle_start or handle_end:
- message += " with handles [{handle_start}]-[{handle_end}]".format(
- handle_start=handle_start,
- handle_end=handle_end
- )
- self.log.info(message)
-
- if frame_data.get("byFrameStep", 1.0) != 1.0:
- self.log.info("Frame steps {}".format(frame_data["byFrameStep"]))
instance.data.update(frame_data)
-
- # Add frame range to label if the instance has a frame range.
- label = instance.data.get("label", instance.data["name"])
- instance.data["label"] = (
- "{label} [{frame_start} - {frame_end}]"
- .format(
- label=label,
- frame_start=frame_start,
- frame_end=frame_end
- )
- )
-
- @classmethod
- def get_attribute_defs(cls):
- return [
- BoolDef("use_handles",
- tooltip="Disable this if you want the publisher to"
- " ignore start and end handles specified in the"
- " asset data for this publish instance",
- default=cls.use_asset_handles,
- label="Use asset handles")
- ]
diff --git a/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py b/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py
index 53072aebc6..64de2079cd 100644
--- a/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py
+++ b/openpype/hosts/houdini/plugins/publish/collect_vray_rop.py
@@ -25,8 +25,8 @@ class CollectVrayROPRenderProducts(pyblish.api.InstancePlugin):
label = "VRay ROP Render Products"
# This specific order value is used so that
- # this plugin runs after CollectRopFrameRange
- order = pyblish.api.CollectorOrder + 0.4999
+ # this plugin runs after CollectFrames
+ order = pyblish.api.CollectorOrder + 0.11
hosts = ["houdini"]
families = ["vray_rop"]
diff --git a/openpype/hosts/houdini/plugins/publish/extract_ass.py b/openpype/hosts/houdini/plugins/publish/extract_ass.py
index 0d246625ba..be60217055 100644
--- a/openpype/hosts/houdini/plugins/publish/extract_ass.py
+++ b/openpype/hosts/houdini/plugins/publish/extract_ass.py
@@ -56,7 +56,7 @@ class ExtractAss(publish.Extractor):
'ext': ext,
"files": files,
"stagingDir": staging_dir,
- "frameStart": instance.data["frameStart"],
- "frameEnd": instance.data["frameEnd"],
+ "frameStart": instance.data["frameStartHandle"],
+ "frameEnd": instance.data["frameEndHandle"],
}
instance.data["representations"].append(representation)
diff --git a/openpype/hosts/houdini/plugins/publish/extract_bgeo.py b/openpype/hosts/houdini/plugins/publish/extract_bgeo.py
index c9625ec880..d13141b426 100644
--- a/openpype/hosts/houdini/plugins/publish/extract_bgeo.py
+++ b/openpype/hosts/houdini/plugins/publish/extract_bgeo.py
@@ -47,7 +47,7 @@ class ExtractBGEO(publish.Extractor):
"ext": ext.lstrip("."),
"files": output,
"stagingDir": staging_dir,
- "frameStart": instance.data["frameStart"],
- "frameEnd": instance.data["frameEnd"]
+ "frameStart": instance.data["frameStartHandle"],
+ "frameEnd": instance.data["frameEndHandle"]
}
instance.data["representations"].append(representation)
diff --git a/openpype/hosts/houdini/plugins/publish/extract_composite.py b/openpype/hosts/houdini/plugins/publish/extract_composite.py
index 7a1ab36b93..11cf83a46d 100644
--- a/openpype/hosts/houdini/plugins/publish/extract_composite.py
+++ b/openpype/hosts/houdini/plugins/publish/extract_composite.py
@@ -41,8 +41,8 @@ class ExtractComposite(publish.Extractor):
"ext": ext,
"files": output,
"stagingDir": staging_dir,
- "frameStart": instance.data["frameStart"],
- "frameEnd": instance.data["frameEnd"],
+ "frameStart": instance.data["frameStartHandle"],
+ "frameEnd": instance.data["frameEndHandle"],
}
from pprint import pformat
diff --git a/openpype/hosts/houdini/plugins/publish/extract_fbx.py b/openpype/hosts/houdini/plugins/publish/extract_fbx.py
index 7993b3352f..7dc193c6a9 100644
--- a/openpype/hosts/houdini/plugins/publish/extract_fbx.py
+++ b/openpype/hosts/houdini/plugins/publish/extract_fbx.py
@@ -40,9 +40,9 @@ class ExtractFBX(publish.Extractor):
}
# A single frame may also be rendered without start/end frame.
- if "frameStart" in instance.data and "frameEnd" in instance.data:
- representation["frameStart"] = instance.data["frameStart"]
- representation["frameEnd"] = instance.data["frameEnd"]
+ if "frameStartHandle" in instance.data and "frameEndHandle" in instance.data: # noqa
+ representation["frameStart"] = instance.data["frameStartHandle"]
+ representation["frameEnd"] = instance.data["frameEndHandle"]
# set value type for 'representations' key to list
if "representations" not in instance.data:
diff --git a/openpype/hosts/houdini/plugins/publish/extract_opengl.py b/openpype/hosts/houdini/plugins/publish/extract_opengl.py
index 6c36dec5f5..38808089ac 100644
--- a/openpype/hosts/houdini/plugins/publish/extract_opengl.py
+++ b/openpype/hosts/houdini/plugins/publish/extract_opengl.py
@@ -39,8 +39,8 @@ class ExtractOpenGL(publish.Extractor):
"ext": instance.data["imageFormat"],
"files": output,
"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": instance.data.get("review_camera")
diff --git a/openpype/hosts/houdini/plugins/publish/extract_redshift_proxy.py b/openpype/hosts/houdini/plugins/publish/extract_redshift_proxy.py
index 1d99ac665c..ef5991924f 100644
--- a/openpype/hosts/houdini/plugins/publish/extract_redshift_proxy.py
+++ b/openpype/hosts/houdini/plugins/publish/extract_redshift_proxy.py
@@ -44,8 +44,8 @@ class ExtractRedshiftProxy(publish.Extractor):
}
# A single frame may also be rendered without start/end frame.
- if "frameStart" in instance.data and "frameEnd" in instance.data:
- representation["frameStart"] = instance.data["frameStart"]
- representation["frameEnd"] = instance.data["frameEnd"]
+ if "frameStartHandle" in instance.data and "frameEndHandle" in instance.data: # noqa
+ representation["frameStart"] = instance.data["frameStartHandle"]
+ representation["frameEnd"] = instance.data["frameEndHandle"]
instance.data["representations"].append(representation)
diff --git a/openpype/hosts/houdini/plugins/publish/extract_vdb_cache.py b/openpype/hosts/houdini/plugins/publish/extract_vdb_cache.py
index 4bca758f08..89af8e1756 100644
--- a/openpype/hosts/houdini/plugins/publish/extract_vdb_cache.py
+++ b/openpype/hosts/houdini/plugins/publish/extract_vdb_cache.py
@@ -40,7 +40,7 @@ class ExtractVDBCache(publish.Extractor):
"ext": "vdb",
"files": output,
"stagingDir": staging_dir,
- "frameStart": instance.data["frameStart"],
- "frameEnd": instance.data["frameEnd"],
+ "frameStart": instance.data["frameStartHandle"],
+ "frameEnd": instance.data["frameEndHandle"],
}
instance.data["representations"].append(representation)
diff --git a/openpype/hosts/houdini/plugins/publish/validate_frame_range.py b/openpype/hosts/houdini/plugins/publish/validate_frame_range.py
index 6a66f3de9f..1b12fa7096 100644
--- a/openpype/hosts/houdini/plugins/publish/validate_frame_range.py
+++ b/openpype/hosts/houdini/plugins/publish/validate_frame_range.py
@@ -57,7 +57,17 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
return
rop_node = hou.node(instance.data["instance_node"])
- if instance.data["frameStart"] > instance.data["frameEnd"]:
+ frame_start = instance.data.get("frameStart")
+ frame_end = instance.data.get("frameEnd")
+
+ if frame_start is None or frame_end is None:
+ cls.log.debug(
+ "Skipping frame range validation for "
+ "instance without frame data: {}".format(rop_node.path())
+ )
+ return
+
+ if frame_start > frame_end:
cls.log.info(
"The ROP node render range is set to "
"{0[frameStartHandle]} - {0[frameEndHandle]} "
@@ -89,7 +99,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
.format(instance))
return
- created_instance.publish_attributes["CollectRopFrameRange"]["use_handles"] = False # noqa
+ created_instance.publish_attributes["CollectAssetHandles"]["use_handles"] = False # noqa
create_context.save_changes()
cls.log.debug("use asset handles is turned off for '{}'"
diff --git a/openpype/hosts/houdini/startup/MainMenuCommon.xml b/openpype/hosts/houdini/startup/MainMenuCommon.xml
index b2e32a70f9..0903aef7bc 100644
--- a/openpype/hosts/houdini/startup/MainMenuCommon.xml
+++ b/openpype/hosts/houdini/startup/MainMenuCommon.xml
@@ -4,7 +4,7 @@