-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathcompositing_save.py
More file actions
302 lines (240 loc) · 9.23 KB
/
compositing_save.py
File metadata and controls
302 lines (240 loc) · 9.23 KB
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
import bpy
from . import compositing_io_util as comp_util
# ----------------------------------------------------------------------------------------------------
# 定数
# ----------------------------------------------------------------------------------------------------
COMPOSITING_OPTION_NAME = "CompositingOption"
# ----------------------------------------------------------------------------------------------------
# Public Functions
# ----------------------------------------------------------------------------------------------------
# -- Get --
def get_compositing_option():
""" Compositingの設定を取得
Returns:
Dictionary: Compositing設定
"""
# 新規シーンなどはノードがない
if bpy.context.scene.node_tree == None:
return None
# 標準情報の設定
data = {}
data["name"] = COMPOSITING_OPTION_NAME
# 各プロパティの設定
data["render_engine"] = bpy.context.scene.render.engine
data["node_groups"] = _get_node_groups_names()
data["nodes"] = _get_nodes_property(bpy.context.scene.node_tree)
data["links"] = _get_links(bpy.context.scene.node_tree)
data["render_layers"] = _search_in_render_layer_all()
return data
# ----------------------------------------------------------------------------------------------------
# Private Functions
# ----------------------------------------------------------------------------------------------------
# -- Get --
def _get_node_groups_names():
""" NodeGroupsの名称リストを取得
Returns:
str[]: NodeGroupsの名称リスト
"""
node_groups_names = []
for ng in bpy.data.node_groups:
if ng.type != "COMPOSITING":
continue
if ng.library != None:
continue
node_groups_names.append(ng.name)
return node_groups_names
def _get_nodes_property(tree):
""" ノードリストのプロパティを取得
Args:
tree (bpy.types.NodeTree): ノードツリー
Returns:
Dictionary: ノードリストのプロパティ
"""
nodes = {}
for node in tree.nodes:
prop = {}
prop["auto_prop"] = _get_auto_property(node)
sp_prop = {}
# Parentの接続
if node.parent != None:
sp_prop["parent"] = node.parent.name
# Groupの場合
if node.bl_idname == "CompositorNodeGroup":
sp_prop["group_name"] = node.node_tree.name
# FileOutputの場合
if node.bl_idname == "CompositorNodeOutputFile":
sp_prop["format"] = _get_auto_property(node.format)
sp_prop["layer_slots"] = [slot.name for slot in node.layer_slots]
sp_prop["file_slots"] = [slot.path for slot in node.file_slots]
_get_inputs(node, sp_prop)
prop["sp_prop"] = sp_prop
nodes[node.name] = prop
return nodes
def _get_inputs(node, sp_prop):
""" inputsの取得
Args:
node (bpy.types.Node): 対象ノード
sp_prop (dict): 設定プロパティ
"""
if not hasattr(node, "inputs"):
return
for i in node.inputs:
try:
if (i.bl_idname == "NodeSocketFloat" or
i.bl_idname == "NodeSocketFloatFactor"):
sp_prop[i.identifier] = i.default_value
elif i.bl_idname == "NodeSocketColor":
sp_prop[i.identifier] = (i.default_value[0], i.default_value[1], i.default_value[2], i.default_value[3])
except Exception as e:
# FileOutputなどカラーで文字列が入ったりノードに応じて特殊パターンがあるため除外
# 特殊パターンは別途ノードを判定して個別対応
print(f"[{node.name}] {i.name} <- {i.identifier} : {e}")
def _get_links(tree):
""" リンク情報を取得
Args:
tree (bpy.types.NodeTree): ノードツリー
Returns:
Dictionary: リンク情報
"""
links = {}
count = 1
for link in tree.links:
link_data = {}
link_data["from_node"] = link.from_node.name
link_data["from_socket"] = link.from_socket.identifier
link_data["to_node"] = link.to_node.name
# FileOutputの場合にソケット名が生成時にはlayer_slots名になっている
if link.to_node.bl_idname == "CompositorNodeOutputFile":
index = None
for i, input in enumerate(link.to_node.inputs):
if input.identifier == link.to_socket.identifier:
index = i
break
name = None
# OpenEXR MultiLayerだとピンの名前の保存先が異なる
if link.to_node.format.file_format == "OPEN_EXR_MULTILAYER":
name = link.to_node.layer_slots[index].name
else:
name = link.to_node.file_slots[index].path
link_data["to_socket"] = name
else:
link_data["to_socket"] = link.to_socket.identifier
links[str(count).zfill(3)] = link_data
count += 1
return links
def _get_linestyle_names():
""" 全てのLineStyleの名前を取得
LineStyleは元データからAppend出来るので名前だけ取得
Returns:
Dictionary: 全てのLineStyleの名前
"""
linestyle_names = []
for ls in bpy.data.linestyles:
linestyle_names.append(ls.name)
return linestyle_names
def _search_in_render_layer_all():
""" 全てのViewLayerのレンダリングプロパティを取得
Returns:
[type]: [description]
"""
render_layer_settings = {}
render_layer_settings["scene_use_freestyle"] = bpy.context.scene.render.use_freestyle
render_layer_settings["linestyle_names"] = _get_linestyle_names()
render_layer_props = {}
for vl in bpy.context.scene.view_layers:
render_layer_props[vl.name] = _search_in_render_layer( vl )
render_layer_settings["render_layer_props"] = render_layer_props
return render_layer_settings
def _search_in_render_layer(vl):
""" ViewLayerのレンダリングプロパティを取得
Args:
vl (bpy.type.ViewLayer): ViewLayer
Returns:
Dictionary: ViewLayerのレンダリングプロパティ
"""
render_layer = {}
# Passes, Filters設定
render_layer["vl_simple"] = _get_auto_property(vl)
# AOV設定
aovs = []
target_aovs = None
if hasattr( vl, "aovs" ):
# 2.93以降
target_aovs = vl.aovs
else:
# 2.91以前
target_aovs = vl.cycles.aovs
for aov in target_aovs:
# 既にあったら追加しない
target_aov = [a for a in aovs if a["name"] == aov.name]
if len(target_aov):
continue
prop = {
"name": aov.name
, "type": aov.type
}
aovs.append(prop)
render_layer["aovs"] = aovs
# Freestyle設定
if vl.freestyle_settings.as_render_pass:
fs = {}
fs["fs_simple"] = _get_auto_property(vl.freestyle_settings)
linesets = {}
for ls in vl.freestyle_settings.linesets:
lineset = {}
lineset["auto_props"] = _get_auto_property(ls)
lineset["manual_props"] = _get_lineset_manual_props(ls)
linesets[ls.name] = lineset
fs["linesets"] = linesets
render_layer["free_style"] = fs
return render_layer
def _get_lineset_manual_props(lineset):
""" LineSetの手動設定が必要なプロパティの取得
Args:
lineset (bpy.types.LineSet): 対象ラインセット
Returns:
Dictionary: LineSetの手動設定が必要なプロパティ
"""
props = {}
props["linestyle_name"] = lineset.linestyle.name
return props
def _get_auto_property(obj):
""" 自動取得出来るプロパティを取得
Args:
obj (Object): プロパティを取得するクラス
Returns:
Dictionary: 自動取得したプロパティ
"""
auto_prop = {}
for attr in dir(obj):
if not hasattr(obj, attr):
continue
if _is_read_only_property(obj, attr):
continue
val = getattr(obj, attr)
# そのまま代入できるものはそのまま
if comp_util.can_substitute_type(val):
auto_prop[attr] = val
# Vectorはそのままdumps出来ないので変換
if str(type(val)) == "<class 'Vector'>":
auto_prop[attr] = (val[0], val[1])
# Color
if str(type(val)) == "<class 'Color'>":
auto_prop[attr] = (val[0], val[1], val[2])
return auto_prop
# -- Check --
def _is_read_only_property(obj, attr):
""" 読み取り専用のプロパティか?
Args:
obj (Object): 対象オブジェクト
attr (str): プロパティ名
Returns:
bool: True = Yes, Fale = No
"""
is_read_only = False
val = getattr(obj, attr)
try:
setattr(obj, attr, val)
except:
is_read_only = True
return is_read_only