-
Notifications
You must be signed in to change notification settings - Fork 127
/
create_render.py
327 lines (265 loc) · 12 KB
/
create_render.py
1
2
3
4
5
6
7
8
9
10
11
12
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
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
# -*- coding: utf-8 -*-
"""Create ``Render`` instance in Maya."""
from maya import cmds
from maya.app.renderSetup.model import renderSetup
from openpype.hosts.maya.api import (
lib,
lib_rendersettings,
plugin
)
from openpype.lib import (
BoolDef,
NumberDef
)
from openpype.pipeline import legacy_io
from openpype.pipeline.create import (
CreatorError,
Creator,
HiddenCreator,
CreatedInstance
)
def ensure_namespace(namespace):
"""Make sure the namespace exists.
Args:
namespace (str): The preferred namespace name.
Returns:
str: The generated or existing namespace
"""
exists = cmds.namespace(exists=namespace)
if exists:
return namespace
else:
return cmds.namespace(add=namespace)
class CreateRender(Creator):
"""Create *render* instance.
This render instance is not visible in the UI as an instance nor does
it by itself publish. Instead, whenever this is created the
CreateRenderlayer creator collects the active scene's actual renderlayers
as individual instances to submit for publishing.
This Creator is solely to SHOW in the "Create" of the new publisher.
See Also:
https://pype.club/docs/artist_hosts_maya#creating-basic-render-setup
"""
identifier = "io.openpype.creators.maya.render"
label = "Render"
family = "rendering"
icon = "eye"
render_settings = {}
@classmethod
def apply_settings(cls, project_settings, system_settings):
cls.render_settings = project_settings["maya"]["RenderSettings"]
def create(self, subset_name, instance_data, pre_create_data):
# Only allow a single render instance to exist
nodes = lib.lsattr("pre_creator_identifier", self.identifier)
if nodes:
raise CreatorError("A Render instance already exists - only "
"one can be configured.")
# Apply default project render settings on create
if self.render_settings.get("apply_render_settings"):
lib_rendersettings.RenderSettings().set_default_renderer_settings()
# if no render layers are present, create default one with
# asterisk selector
rs = renderSetup.instance()
if not rs.getRenderLayers():
render_layer = rs.createRenderLayer('Main')
collection = render_layer.createCollection("defaultCollection")
collection.getSelector().setPattern('*')
with lib.undo_chunk():
node = cmds.sets(empty=True, name=subset_name)
lib.imprint(node, data={
"pre_creator_identifier": self.identifier
})
# By RenderLayerCreator.create we make it so that the renderlayer
# instances directly appear even though it just collects scene
# renderlayers. This doesn't actually 'create' any scene contents.
self.create_context.create(
CreateRenderlayer.identifier,
instance_data={},
source_data=instance_data
)
def collect_instances(self):
# We never show this instance in the publish UI
return
def update_instances(self, update_list):
return
def remove_instances(self, instances):
return
class CreateRenderlayer(HiddenCreator, plugin.MayaCreatorBase):
"""Create and manges renderlayer subset per renderLayer in workfile.
This does no do ANYTHING until a CreateRender subset exists in the
scene, created by the CreateRender creator.
"""
identifier = "io.openpype.creators.maya.renderlayer"
family = "renderlayer"
label = "Renderlayer"
icon = "eye"
enable_all_lights = False
@classmethod
def apply_settings(cls, project_settings, system_settings):
render_settings = project_settings["maya"]["RenderSettings"]
cls.enable_all_lights = render_settings.get("enable_all_lights",
cls.enable_all_lights)
def create(self, instance_data, source_data):
# A Renderlayer is never explicitly created using the create method.
# Instead, renderlayers from the scene are collected. Thus "create"
# would only ever be called to say, 'hey, please refresh collect'
self.collect_instances()
def collect_instances(self):
# We only collect if a CreateRender instance exists
if not lib.lsattr("pre_creator_identifier", CreateRender.identifier):
return
rs = renderSetup.instance()
layers = rs.getRenderLayers()
for layer in layers:
layer_instance_node = self.find_layer_instance_node(layer)
if layer_instance_node:
data = self.read_instance_node(layer_instance_node)
instance = CreatedInstance.from_existing(data, creator=self)
else:
# No existing scene instance node for this layer. Note that
# this instance will not have the `instance_node` data yet
# until it's been saved/persisted at least once.
subset_name = "render" + layer.name()
instance_data = {
"asset": legacy_io.Session["AVALON_ASSET"],
"task": legacy_io.Session["AVALON_TASK"],
"variant": layer.name(),
}
instance = CreatedInstance(
family=self.family,
subset_name=subset_name,
data=instance_data,
creator=self
)
instance.transient_data["layer"] = layer
self._add_instance_to_context(instance)
def find_layer_instance_node(self, layer):
connected_sets = cmds.listConnections(
"{}.message".format(layer.name()),
source=False,
destination=True,
type="objectSet"
) or []
for node in connected_sets:
if not cmds.attributeQuery("creator_identifier",
node=node,
exists=True):
continue
creator_identifier = cmds.getAttr(node + ".creator_identifier")
if creator_identifier == self.identifier:
print(f"Found node: {node}")
return node
def _create_layer_instance_node(self, layer):
# We only collect if a CreateRender instance exists
create_render_sets = lib.lsattr("pre_creator_identifier",
CreateRender.identifier)
if not create_render_sets:
raise CreatorError("Creating a renderlayer instance node is not "
"allowed if no 'CreateRender' instance exists")
create_render_set = create_render_sets[0]
namespace = "_renderingMain"
namespace = ensure_namespace(namespace)
name = "{}:{}".format(namespace, layer.name())
render_set = cmds.sets(name=name, empty=True)
# Keep an active link with the renderlayer so we can retrieve it
# later by a physical maya connection instead of relying on the layer
# name
cmds.addAttr(render_set, longName="renderlayer", at="message")
cmds.connectAttr(layer.name() + ".message",
render_set + ".renderlayer", force=True)
# Add the set to the 'CreateRender' set.
cmds.sets(render_set, forceElement=create_render_set)
return render_set
def update_instances(self, update_list):
# We only generate the persisting layer data into the scene once
# we save with the UI on e.g. validate or publish
for instance, _changes in update_list:
instance_node = instance.data.get("instance_node")
# Ensure a node exists to persist the data to
if not instance_node:
layer = instance.transient_data["layer"]
instance_node = self._create_layer_instance_node(layer)
instance.data["instance_node"] = instance_node
else:
# TODO: Keep name in sync with the actual renderlayer?
pass
self.imprint_instance_node(instance_node,
data=instance.data_to_store())
def imprint_instance_node(self, node, data):
# Do not ever try to update the `renderlayer` since it'll try
# to remove the attribute and recreate it but fail to keep it a
# message attribute link. We only ever imprint that on the initial
# node creation.
# TODO: Improve how this is handled
data.pop("renderlayer", None)
data.get("creator_attributes", {}).pop("renderlayer", None)
return super(CreateRenderlayer, self).imprint_instance_node(node,
data=data)
def remove_instances(self, instances):
"""Remove specified instance from the scene.
This is only removing `id` parameter so instance is no longer
instance, because it might contain valuable data for artist.
"""
# Instead of removing the single instance or renderlayers we instead
# remove the CreateRender node this creator relies on to decide whether
# it should collect anything at all.
nodes = lib.lsattr("pre_creator_identifier", CreateRender.identifier)
if nodes:
cmds.delete(nodes)
# Remove ALL of the instances even if only one gets deleted
for instance in list(self.create_context.instances):
if instance.get("creator_identifier") == self.identifier:
self._remove_instance_from_context(instance)
# Remove the stored settings per renderlayer too
node = instance.data.get("instance_node")
if node and cmds.objExists(node):
cmds.delete(node)
def get_instance_attr_defs(self):
"""Create instance settings."""
return [
BoolDef("review",
label="Review",
tooltip="Mark as reviewable",
default=True),
BoolDef("extendFrames",
label="Extend Frames",
tooltip="Extends the frames on top of the previous "
"publish.\nIf the previous was 1001-1050 and you "
"would now submit 1020-1070 only the new frames "
"1051-1070 would be rendered and published "
"together with the previously rendered frames.\n"
"If 'overrideExistingFrame' is enabled it *will* "
"render any existing frames.",
default=False),
BoolDef("overrideExistingFrame",
label="Override Existing Frame",
tooltip="Mark as reviewable",
default=True),
# TODO: Should these move to submit_maya_deadline plugin?
# Tile rendering
BoolDef("tileRendering",
label="Enable tiled rendering",
default=False),
NumberDef("tilesX",
label="Tiles X",
default=2,
minimum=1,
decimals=0),
NumberDef("tilesY",
label="Tiles Y",
default=2,
minimum=1,
decimals=0),
# Additional settings
BoolDef("convertToScanline",
label="Convert to Scanline",
tooltip="Convert the output images to scanline images",
default=False),
BoolDef("useReferencedAovs",
label="Use Referenced AOVs",
tooltip="Consider the AOVs from referenced scenes as well",
default=False),
BoolDef("renderSetupIncludeLights",
label="Render Setup Include Lights",
default=self.enable_all_lights)
]