Skip to content

Commit

Permalink
Merge branch 'develop' into feature/AY-5417_Houdini-workfile-templates
Browse files Browse the repository at this point in the history
  • Loading branch information
MustafaJafar committed Jul 12, 2024
2 parents c92791c + 077af32 commit ea17050
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 102 deletions.
18 changes: 2 additions & 16 deletions client/ayon_houdini/addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,11 @@ class HoudiniAddon(AYONAddon, IHostAddon):
host_name = "houdini"

def add_implementation_envs(self, env, _app):
# Add requirements to HOUDINI_PATH and HOUDINI_MENU_PATH
# Add requirements to HOUDINI_PATH
startup_path = os.path.join(HOUDINI_HOST_DIR, "startup")
new_houdini_path = [startup_path]
new_houdini_menu_path = [startup_path]

old_houdini_path = env.get("HOUDINI_PATH") or ""
old_houdini_menu_path = env.get("HOUDINI_MENU_PATH") or ""

for path in old_houdini_path.split(os.pathsep):
if not path:
continue
Expand All @@ -28,20 +25,9 @@ def add_implementation_envs(self, env, _app):
if norm_path not in new_houdini_path:
new_houdini_path.append(norm_path)

for path in old_houdini_menu_path.split(os.pathsep):
if not path:
continue

norm_path = os.path.normpath(path)
if norm_path not in new_houdini_menu_path:
new_houdini_menu_path.append(norm_path)

# Add ampersand for unknown reason (Maybe is needed in Houdini?)
# Add & (ampersand), it represents "the standard Houdini Path contents"
new_houdini_path.append("&")
new_houdini_menu_path.append("&")

env["HOUDINI_PATH"] = os.pathsep.join(new_houdini_path)
env["HOUDINI_MENU_PATH"] = os.pathsep.join(new_houdini_menu_path)

def get_launch_hook_paths(self, app):
if app.host_name != self.host_name:
Expand Down
15 changes: 13 additions & 2 deletions client/ayon_houdini/api/hda_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,14 @@ def update_info(node, context):
if node.evalParm(key) != value}
parms["load_message"] = "" # clear any warnings/errors

# Update the product type filter to match the type
current = node.evalParm("product_type")
product_type = context["product"]["productType"]
if current and current != product_type:
# If current is empty we consider no filtering applied and we allow
# that to be a state that needs no switching
parms["product_type"] = product_type

# Note that these never trigger any parm callbacks since we do not
# trigger the `parm.pressButton` and programmatically setting values
# in Houdini does not trigger callbacks automatically
Expand Down Expand Up @@ -569,13 +577,16 @@ def get_available_products(node):
if not folder_entity:
return []

# Apply filter only if any value is set
product_types = [product_type] if product_type else None

products = ayon_api.get_products(
project_name,
folder_ids=[folder_entity["id"]],
product_types=[product_type]
product_types=product_types
)

return [product["name"] for product in products]
return list(sorted(product["name"] for product in products))


def set_to_latest_version(node):
Expand Down
115 changes: 56 additions & 59 deletions client/ayon_houdini/api/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@
from ayon_core.settings import get_current_project_settings
from ayon_core.pipeline import (
Anatomy,
get_current_project_name,
get_current_folder_path,
registered_host,
get_current_context,
get_current_host_name,
)
from ayon_core.pipeline.create import CreateContext
from ayon_core.pipeline.template_data import get_template_data
from ayon_core.pipeline.context_tools import get_current_folder_entity
from ayon_core.pipeline.context_tools import get_current_task_entity
from ayon_core.tools.utils import PopupUpdateKeys, SimplePopup
from ayon_core.tools.utils.host_tools import get_tool_by_name

Expand All @@ -35,12 +33,12 @@
JSON_PREFIX = "JSON:::"


def get_folder_fps(folder_entity=None):
"""Return current folder fps."""
def get_entity_fps(entity=None):
"""Return current task fps or fps from an entity."""

if folder_entity is None:
folder_entity = get_current_folder_entity(fields=["attrib.fps"])
return folder_entity["attrib"]["fps"]
if entity is None:
entity = get_current_task_entity(fields=["attrib.fps"])
return entity["attrib"]["fps"]


def get_output_parameter(node):
Expand Down Expand Up @@ -121,7 +119,7 @@ def validate_fps():
"""

fps = get_folder_fps()
fps = get_entity_fps()
current_fps = hou.fps() # returns float

if current_fps != fps:
Expand Down Expand Up @@ -392,30 +390,23 @@ def parm_values(overrides):
def reset_framerange(fps=True, frame_range=True):
"""Set frame range and FPS to current folder."""

project_name = get_current_project_name()
folder_path = get_current_folder_path()

folder_entity = ayon_api.get_folder_by_path(project_name, folder_path)
folder_attributes = folder_entity["attrib"]
task_entity = get_current_task_entity(fields={"attrib"})

# Set FPS
if fps:
fps = get_folder_fps(folder_entity)
fps = get_entity_fps(task_entity)
print("Setting scene FPS to {}".format(int(fps)))
set_scene_fps(fps)

if frame_range:

# Set Start and End Frames
frame_start = folder_attributes.get("frameStart")
frame_end = folder_attributes.get("frameEnd")

if frame_start is None or frame_end is None:
log.warning("No edit information found for '%s'", folder_path)
return
task_attrib = task_entity["attrib"]
frame_start = task_attrib.get("frameStart", 0)
frame_end = task_attrib.get("frameEnd", 0)

handle_start = folder_attributes.get("handleStart", 0)
handle_end = folder_attributes.get("handleEnd", 0)
handle_start = task_attrib.get("handleStart", 0)
handle_end = task_attrib.get("handleEnd", 0)

frame_start -= int(handle_start)
frame_end += int(handle_end)
Expand Down Expand Up @@ -507,7 +498,7 @@ def get_frame_data(node, log=None):

log.info(
"Node '{}' has 'Render current frame' set.\n"
"Folder Handles are ignored.\n"
"Task handles are ignored.\n"
"frameStart and frameEnd are set to the "
"current frame.".format(node.path())
)
Expand Down Expand Up @@ -646,43 +637,44 @@ def get_output_children(output_node, include_sops=True):
return out_list


def get_resolution_from_folder(folder_entity):
"""Get resolution from the given folder entity.
def get_resolution_from_entity(entity):
"""Get resolution from the given entity.
Args:
folder_entity (dict[str, Any]): Folder entity.
entity (dict[str, Any]): Project, Folder or Task entity.
Returns:
Union[Tuple[int, int], None]: Resolution width and height.
"""
if not folder_entity or "attrib" not in folder_entity:
print("Entered folder is not valid. \"{}\"".format(
str(folder_entity)
))
return None
if not entity or "attrib" not in entity:
raise ValueError(f"Entity is not valid: \"{entity}\"")

folder_attributes = folder_entity["attrib"]
resolution_width = folder_attributes.get("resolutionWidth")
resolution_height = folder_attributes.get("resolutionHeight")
attributes = entity["attrib"]
resolution_width = attributes.get("resolutionWidth")
resolution_height = attributes.get("resolutionHeight")

# Make sure both width and height are set
if resolution_width is None or resolution_height is None:
print("No resolution information found for '{}'".format(
folder_entity["path"]
))
print(f"No resolution information found in entity: '{entity}'")
return None

return int(resolution_width), int(resolution_height)


def set_camera_resolution(camera, folder_entity=None):
"""Apply resolution to camera from folder entity of the publish"""
def set_camera_resolution(camera, entity=None):
"""Apply resolution to camera from task or folder entity.
Arguments:
camera (hou.OpNode): Camera node.
entity (Optional[Dict[str, Any]]): Folder or task entity.
If not provided falls back to current task entity.
"""

if not folder_entity:
folder_entity = get_current_folder_entity()
if not entity:
entity = get_current_task_entity()

resolution = get_resolution_from_folder(folder_entity)
resolution = get_resolution_from_entity(entity)

if resolution:
print("Setting camera resolution: {} -> {}x{}".format(
Expand All @@ -705,10 +697,13 @@ def get_camera_from_container(container):
return cameras[0]


def get_current_context_template_data_with_folder_attrs():
"""
def get_current_context_template_data_with_entity_attrs():
"""Return template data including current context folder and task attribs.
Output contains 'folderAttributes' key with folder attribute values.
Output contains:
- Regular template data from `get_template_data`
- 'folderAttributes' key with folder attribute values.
- 'taskAttributes' key with task attribute values.
Returns:
dict[str, Any]: Template data to fill templates.
Expand All @@ -729,23 +724,25 @@ def get_current_context_template_data_with_folder_attrs():

# get context specific vars
folder_attributes = folder_entity["attrib"]
task_attributes = task_entity["attrib"]

# compute `frameStartHandle` and `frameEndHandle`
frame_start = folder_attributes.get("frameStart")
frame_end = folder_attributes.get("frameEnd")
handle_start = folder_attributes.get("handleStart")
handle_end = folder_attributes.get("handleEnd")
if frame_start is not None and handle_start is not None:
folder_attributes["frameStartHandle"] = frame_start - handle_start

if frame_end is not None and handle_end is not None:
folder_attributes["frameEndHandle"] = frame_end + handle_end
for attributes in [folder_attributes, task_attributes]:
frame_start = attributes.get("frameStart")
frame_end = attributes.get("frameEnd")
handle_start = attributes.get("handleStart")
handle_end = attributes.get("handleEnd")
if frame_start is not None and handle_start is not None:
attributes["frameStartHandle"] = frame_start - handle_start
if frame_end is not None and handle_end is not None:
attributes["frameEndHandle"] = frame_end + handle_end

template_data = get_template_data(
project_entity, folder_entity, task_entity, host_name
)
template_data["root"] = anatomy.roots
template_data["folderAttributes"] = folder_attributes
template_data["taskAttributes"] = task_attributes

return template_data

Expand Down Expand Up @@ -806,7 +803,7 @@ def get_context_var_changes():
return houdini_vars_to_update

# Get Template data
template_data = get_current_context_template_data_with_folder_attrs()
template_data = get_current_context_template_data_with_entity_attrs()

# Set Houdini Vars
for item in houdini_vars:
Expand Down Expand Up @@ -838,7 +835,7 @@ def get_context_var_changes():


def update_houdini_vars_context():
"""Update folder context variables"""
"""Update task context variables"""

for var, (_old, new, is_directory) in get_context_var_changes().items():
if is_directory:
Expand All @@ -857,7 +854,7 @@ def update_houdini_vars_context():


def update_houdini_vars_context_dialog():
"""Show pop-up to update folder context variables"""
"""Show pop-up to update task context variables"""
update_vars = get_context_var_changes()
if not update_vars:
# Nothing to change
Expand All @@ -873,7 +870,7 @@ def update_houdini_vars_context_dialog():
parent = hou.ui.mainQtWindow()
dialog = SimplePopup(parent=parent)
dialog.setModal(True)
dialog.setWindowTitle("Houdini scene has outdated folder variables")
dialog.setWindowTitle("Houdini scene has outdated task variables")
dialog.set_message(message)
dialog.set_button_text("Fix")

Expand Down
4 changes: 2 additions & 2 deletions client/ayon_houdini/api/shelves.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

import hou

from .lib import get_current_context_template_data_with_folder_attrs
from .lib import get_current_context_template_data_with_entity_attrs

log = logging.getLogger("ayon_houdini.shelves")

Expand All @@ -31,7 +31,7 @@ def generate_shelves():
return

# Get Template data
template_data = get_current_context_template_data_with_folder_attrs()
template_data = get_current_context_template_data_with_entity_attrs()

for config in shelves_configs:
selected_option = config["options"]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
get_camera_from_container,
set_camera_resolution
)
from ayon_core.pipeline.context_tools import get_current_folder_entity
from ayon_core.pipeline.context_tools import get_current_task_entity


class SetCameraResolution(InventoryAction):
Expand All @@ -19,8 +19,8 @@ def is_compatible(container):
)

def process(self, containers):
folder_entity = get_current_folder_entity()
task_entity = get_current_task_entity()
for container in containers:
node = container["node"]
camera = get_camera_from_container(node)
set_camera_resolution(camera, folder_entity)
set_camera_resolution(camera, task_entity)
2 changes: 1 addition & 1 deletion client/ayon_houdini/plugins/load/load_camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def load(self, context, name=None, namespace=None, data=None):

camera = get_camera_from_container(node)
self._match_maya_render_mask(camera)
set_camera_resolution(camera, folder_entity=context["folder"])
set_camera_resolution(camera, entity=context["folder"])
self[:] = nodes

return pipeline.containerise(node_name,
Expand Down
Loading

0 comments on commit ea17050

Please sign in to comment.