Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Fusion: new creator for image product type #6057

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
72a7494
Introduced image product type
kalisp Dec 14, 2023
fb872a2
Updated logging
kalisp Dec 14, 2023
00b6edd
Refactor moved generic creaor class to better location
kalisp Dec 14, 2023
0d6cacc
Update openpype/settings/entities/schemas/projects_schema/schema_proj…
kalisp Dec 14, 2023
faa6761
Change label
kalisp Dec 14, 2023
6b83013
Merge remote-tracking branch 'origin/enhancement/OP-7470_Fusion-new-c…
kalisp Dec 14, 2023
19a9927
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
jakubjezek001 Dec 15, 2023
120ac78
OP-7470 - fix name
kalisp Dec 18, 2023
abf7a90
OP-7470 - update docstring
kalisp Dec 18, 2023
67ed0f7
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
jakubjezek001 Dec 18, 2023
0a1b156
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
jakubjezek001 Dec 18, 2023
8cf057f
Implementing changes from #6060
jakubjezek001 Dec 18, 2023
3dfd284
Update openpype/settings/defaults/project_settings/fusion.json
kalisp Dec 19, 2023
68bd122
Update server_addon/fusion/server/settings.py
kalisp Dec 19, 2023
9cbd729
Update openpype/hosts/fusion/api/plugin.py
kalisp Dec 19, 2023
ed91294
OP-7470 - added explicit frame field
kalisp Dec 19, 2023
078a73b
OP-7470 - fix name and logging
kalisp Dec 19, 2023
1ef332d
OP-7470 - update instance label
kalisp Dec 19, 2023
298b74c
Update openpype/hosts/fusion/plugins/create/create_image_saver.py
kalisp Dec 20, 2023
53fe536
OP-7470 - fix documentation
kalisp Dec 20, 2023
48c4c47
OP-7470 - moved frame range resolution earlier
kalisp Dec 20, 2023
6bc96a8
OP-7470 - added new validator for single frame
kalisp Dec 20, 2023
24f9943
OP-7470 - Hound
kalisp Dec 20, 2023
774871b
Merge remote-tracking branch 'origin/develop' into enhancement/OP-747…
kalisp Jan 4, 2024
38d3979
OP-7470 - removed unnecessary as label
kalisp Jan 4, 2024
a4bb656
OP-7470 - use internal class anatomy
kalisp Jan 4, 2024
4a71f9a
OP-7470 - add explicit settings_category to propagete values from Set…
kalisp Jan 4, 2024
db34eb1
OP-7470 - typo
kalisp Jan 4, 2024
dad0db9
OP-7470 - update docstring
kalisp Jan 4, 2024
cbbcd07
OP-7470 - update formatting data
kalisp Jan 4, 2024
d2201fb
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
jakubjezek001 Jan 9, 2024
f492055
OP-7470 - moved around only proper fields
kalisp Jan 9, 2024
27f2408
OP-7470 - added defaults to Settings
kalisp Jan 9, 2024
ddb7887
Merge branch 'enhancement/OP-7470_Fusion-new-creator-plugin-single-fr…
kalisp Jan 9, 2024
b4b10cc
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
jakubjezek001 Jan 10, 2024
453e01f
OP-7470 - fixed typo
kalisp Jan 10, 2024
70d5622
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
kalisp Jan 10, 2024
a12b3fb
OP-7470 - bumped up version
kalisp Jan 10, 2024
14b24b4
Merge branch 'enhancement/OP-7470_Fusion-new-creator-plugin-single-fr…
kalisp Jan 10, 2024
5e01dd2
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
jakubjezek001 Jan 11, 2024
fdb5f18
Update openpype/hosts/fusion/plugins/publish/collect_instances.py
jakubjezek001 Jan 12, 2024
d1d0000
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
jakubjezek001 Jan 12, 2024
e66ffc7
OP-7470 - removed unnecessary variables
kalisp Jan 12, 2024
3c83193
Merge remote-tracking branch 'origin/develop' into enhancement/OP-747…
kalisp Jan 12, 2024
ec1e8f6
OP-7470 - update to error message
kalisp Jan 12, 2024
b028970
OP-7470 - removed unneded method
kalisp Jan 12, 2024
487bfa9
Merge branch 'develop' into enhancement/OP-7470_Fusion-new-creator-pl…
kalisp Jan 15, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
221 changes: 221 additions & 0 deletions openpype/hosts/fusion/api/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
from copy import deepcopy
import os

from openpype.hosts.fusion.api import (
get_current_comp,
comp_lock_and_undo_chunk,
)

from openpype.lib import (
BoolDef,
EnumDef,
)
from openpype.pipeline import (
legacy_io,
Creator,
CreatedInstance
)


class GenericCreateSaver(Creator):
default_variants = ["Main", "Mask"]
description = "Fusion Saver to generate image sequence"
icon = "fa5.eye"

instance_attributes = [
"reviewable"
]

settings_category = "fusion"

image_format = "exr"

kalisp marked this conversation as resolved.
Show resolved Hide resolved
# TODO: This should be renamed together with Nuke so it is aligned
temp_rendering_path_template = (
"{workdir}/renders/fusion/{subset}/{subset}.{frame}.{ext}")

def create(self, subset_name, instance_data, pre_create_data):
self.pass_pre_attributes_to_instance(instance_data, pre_create_data)

instance = CreatedInstance(
family=self.family,
subset_name=subset_name,
data=instance_data,
creator=self,
)
data = instance.data_to_store()
comp = get_current_comp()
with comp_lock_and_undo_chunk(comp):
args = (-32768, -32768) # Magical position numbers
saver = comp.AddTool("Saver", *args)

self._update_tool_with_data(saver, data=data)

# Register the CreatedInstance
self._imprint(saver, data)

# Insert the transient data
instance.transient_data["tool"] = saver

self._add_instance_to_context(instance)

return instance

def collect_instances(self):
comp = get_current_comp()
tools = comp.GetToolList(False, "Saver").values()
for tool in tools:
data = self.get_managed_tool_data(tool)
if not data:
continue

# Add instance
created_instance = CreatedInstance.from_existing(data, self)

# Collect transient data
created_instance.transient_data["tool"] = tool

self._add_instance_to_context(created_instance)

def update_instances(self, update_list):
for created_inst, _changes in update_list:
new_data = created_inst.data_to_store()
tool = created_inst.transient_data["tool"]
self._update_tool_with_data(tool, new_data)
self._imprint(tool, new_data)

def remove_instances(self, instances):
for instance in instances:
# Remove the tool from the scene

tool = instance.transient_data["tool"]
if tool:
tool.Delete()

# Remove the collected CreatedInstance to remove from UI directly
self._remove_instance_from_context(instance)

def _imprint(self, tool, data):
# Save all data in a "openpype.{key}" = value data

# Instance id is the tool's name so we don't need to imprint as data
data.pop("instance_id", None)

active = data.pop("active", None)
if active is not None:
# Use active value to set the passthrough state
tool.SetAttrs({"TOOLB_PassThrough": not active})

for key, value in data.items():
tool.SetData(f"openpype.{key}", value)

def _update_tool_with_data(self, tool, data):
"""Update tool node name and output path based on subset data"""
if "subset" not in data:
return

original_subset = tool.GetData("openpype.subset")
original_format = tool.GetData(
"openpype.creator_attributes.image_format"
)

subset = data["subset"]
if (
original_subset != subset
or original_format != data["creator_attributes"]["image_format"]
):
self._configure_saver_tool(data, tool, subset)

def _configure_saver_tool(self, data, tool, subset):
formatting_data = deepcopy(data)

# get frame padding from anatomy templates
frame_padding = self.project_anatomy.templates["frame_padding"]

# get output format
ext = data["creator_attributes"]["image_format"]

# Subset change detected
workdir = os.path.normpath(legacy_io.Session["AVALON_WORKDIR"])
formatting_data.update({
"workdir": workdir,
"frame": "0" * frame_padding,
"ext": ext,
"product": {
"name": formatting_data["subset"],
"type": formatting_data["family"],
},
})

# build file path to render
filepath = self.temp_rendering_path_template.format(**formatting_data)

comp = get_current_comp()
tool["Clip"] = comp.ReverseMapPath(os.path.normpath(filepath))

# Rename tool
if tool.Name != subset:
print(f"Renaming {tool.Name} -> {subset}")
tool.SetAttrs({"TOOLS_Name": subset})

def get_managed_tool_data(self, tool):
"""Return data of the tool if it matches creator identifier"""
data = tool.GetData("openpype")
if not isinstance(data, dict):
return

required = {
"id": "pyblish.avalon.instance",
"creator_identifier": self.identifier,
}
for key, value in required.items():
if key not in data or data[key] != value:
return

# Get active state from the actual tool state
attrs = tool.GetAttrs()
passthrough = attrs["TOOLB_PassThrough"]
data["active"] = not passthrough

# Override publisher's UUID generation because tool names are
# already unique in Fusion in a comp
data["instance_id"] = tool.Name

return data

def get_instance_attr_defs(self):
"""Settings for publish page"""
return self.get_pre_create_attr_defs()

def pass_pre_attributes_to_instance(self, instance_data, pre_create_data):
creator_attrs = instance_data["creator_attributes"] = {}
for pass_key in pre_create_data.keys():
creator_attrs[pass_key] = pre_create_data[pass_key]

def _get_render_target_enum(self):
rendering_targets = {
"local": "Local machine rendering",
"frames": "Use existing frames",
}
if "farm_rendering" in self.instance_attributes:
rendering_targets["farm"] = "Farm rendering"

return EnumDef(
"render_target", items=rendering_targets, label="Render target"
)

def _get_reviewable_bool(self):
return BoolDef(
"review",
default=("reviewable" in self.instance_attributes),
label="Review",
)

def _get_image_format_enum(self):
image_format_options = ["exr", "tga", "tif", "png", "jpg"]
return EnumDef(
"image_format",
items=image_format_options,
default=self.image_format,
label="Output Image Format",
)
64 changes: 64 additions & 0 deletions openpype/hosts/fusion/plugins/create/create_image_saver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
from openpype.lib import NumberDef

from openpype.hosts.fusion.api.plugin import GenericCreateSaver
from openpype.hosts.fusion.api import get_current_comp
kalisp marked this conversation as resolved.
Show resolved Hide resolved


class CreateImageSaver(GenericCreateSaver):
"""Fusion Saver to generate single image.

Created to explicitly separate single ('image') or
multi frame('render) outputs.

This might be temporary creator until 'alias' functionality will be
implemented to limit creation of additional product types with similar, but
not the same workflows.
"""
identifier = "io.openpype.creators.fusion.imagesaver"
label = "Image (saver)"
name = "image"
family = "image"
description = "Fusion Saver to generate image"

default_frame = 0

def get_detail_description(self):
return """Fusion Saver to generate single image.

This creator is expected for publishing of single frame `image` product
type.

Artist should provide frame number (integer) to specify which frame
should be published. It must be inside of global timeline frame range.

Supports local and deadline rendering.

Supports selection from predefined set of output file extensions:
- exr
- tga
- png
- tif
- jpg

Created to explicitly separate single frame ('image') or
multi frame ('render') outputs.
"""

def get_pre_create_attr_defs(self):
"""Settings for create page"""
attr_defs = [
self._get_render_target_enum(),
self._get_reviewable_bool(),
self._get_frame_int(),
self._get_image_format_enum(),
]
return attr_defs

def _get_frame_int(self):
return NumberDef(
"frame",
default=self.default_frame,
label="Frame",
tooltip="Set frame to be rendered, must be inside of global "
"timeline range"
)
Loading
Loading