13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239 | class CreateSaver(GenericCreateSaver):
"""Fusion Saver to generate image sequence of 'render' product type.
Original Saver creator targeted for 'render' product type. It uses
original not to descriptive name because of values in Settings.
"""
identifier = "io.openpype.creators.fusion.saver"
label = "Render (saver)"
name = "render"
product_base_type = "render"
product_type = product_base_type
description = "Fusion Saver to generate image sequence"
default_frame_range_option = "current_context"
def get_detail_description(self):
return inspect.cleandoc(
"""Fusion Saver to generate image sequence.
This creator is expected for publishing of image sequences for
'render' product type. (But can publish even single frame
'render'.)
Select what should be source of render range:
- "Current Folder context" - values set on folder on AYON server
- "From render in/out" - from node itself
- "From composition timeline" - from timeline
Supports local and farm rendering.
Supports selection from predefined set of output file extensions:
- exr
- tga
- png
- tif
- jpg
"""
)
def register_callbacks(self):
self.create_context.add_value_changed_callback(self.on_values_changed)
def on_values_changed(self, event):
"""Update instance attribute definitions on attribute changes."""
for instance_change in event["changes"]:
# First check if there's a change we want to respond to
instance = instance_change["instance"]
if instance is None:
# Change is on context
continue
if instance["creator_identifier"] != self.identifier:
continue
value_changes = instance_change["changes"]
if (
"frame_range_source"
not in value_changes.get("creator_attributes", {})
):
continue
# Update the attribute definitions
new_attrs = self.get_attr_defs_for_instance(instance)
instance.set_create_attr_defs(new_attrs)
def get_managed_tool_data(self, tool):
data = super().get_managed_tool_data(tool)
if not data:
return data
# Backwards compatibility: The 'current_context' frame range source
# was stored as 'current_task' before. Convert it explicitly because
# otherwise the unknown value would fall back to the default frame
# range source of the settings, which may be a different source.
creator_attributes = data.get("creator_attributes")
if (
isinstance(creator_attributes, dict)
and creator_attributes.get("frame_range_source") == "current_task"
):
creator_attributes["frame_range_source"] = "current_context"
return data
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_range_enum(),
self._get_image_format_enum(),
*self._get_custom_frame_range_attribute_defs()
]
return attr_defs
def get_attr_defs_for_instance(self, instance):
return [
self._get_render_target_enum(),
self._get_reviewable_bool(),
self._get_frame_range_enum(),
self._get_image_format_enum(),
*self._get_custom_frame_range_attribute_defs(instance)
]
def _get_frame_range_enum(self):
frame_range_options = {
"current_context": "Current context",
"render_range": "From render in/out",
"comp_range": "From composition timeline",
"custom_range": "Custom frame range",
}
return EnumDef(
"frame_range_source",
items=frame_range_options,
label="Frame range source",
default=self.default_frame_range_option
)
@staticmethod
def _get_custom_frame_range_attribute_defs(instance=None) -> list:
# If an instance is provided and 'custom_range' is not the frame
# range source, then we will disable the custom frame range attributes
custom_enabled = True
if instance is not None:
frame_range_source = instance.get(
"creator_attributes", {}).get("frame_range_source")
custom_enabled = frame_range_source == "custom_range"
# Define custom frame range defaults based on current comp
# timeline settings (if a comp is currently open)
comp = get_current_comp()
if comp is not None:
attrs = comp.GetAttrs()
global_start = attrs["COMPN_GlobalStart"]
global_end = attrs["COMPN_GlobalEnd"]
render_start = attrs["COMPN_RenderStart"]
render_end = attrs["COMPN_RenderEnd"]
# Whenever render ranges are undefined fall back
# to the comp's global start and end
if render_start == -1000000000:
render_start = global_start
if render_end == -1000000000:
render_end = global_end
# The render range is the frame range and the remainder up to
# the global range are the handles, so that the range including
# handles matches the comp's global range.
frame_defaults = {
"frameStart": int(render_start),
"frameEnd": int(render_end),
"handleStart": int(render_start - global_start),
"handleEnd": int(global_end - render_end),
}
else:
frame_defaults = {
"frameStart": 1001,
"frameEnd": 1100,
"handleStart": 0,
"handleEnd": 0
}
attr_defs = []
if custom_enabled:
# UILabelDef does not support `hidden` argument so we exclude it
# manually
attr_defs.append(
UILabelDef(
label="<br><b>Custom Frame Range</b>",
),
)
attr_defs.extend([
NumberDef(
"custom_frameStart",
label="Frame Start",
default=frame_defaults["frameStart"],
minimum=0,
decimals=0,
tooltip=(
"Set the start frame for the export.\n"
"Only used if frame range source is 'Custom frame range'."
),
visible=custom_enabled
),
NumberDef(
"custom_frameEnd",
label="Frame End",
default=frame_defaults["frameEnd"],
minimum=0,
decimals=0,
tooltip=(
"Set the end frame for the export.\n"
"Only used if frame range source is 'Custom frame range'."
),
visible=custom_enabled
),
NumberDef(
"custom_handleStart",
label="Handle Start",
default=frame_defaults["handleStart"],
minimum=0,
decimals=0,
tooltip=(
"Set the start handles for the export, this will be "
"added before the start frame.\n"
"Only used if frame range source is 'Custom frame range'."
),
visible=custom_enabled
),
NumberDef(
"custom_handleEnd",
label="Handle End",
default=frame_defaults["handleEnd"],
minimum=0,
decimals=0,
tooltip=(
"Set the end handles for the export, this will be added "
"after the end frame.\n"
"Only used if frame range source is 'Custom frame range'."
),
visible=custom_enabled
)
])
return attr_defs
|