node_to_json 0.1.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,433 @@
1
+ import bpy
2
+ from .asset_funcs import get_asset_nodes, asset_node_group
3
+ from .setter_funcs import set_node_from_data
4
+ from .type_util import BNode, BGroup, PYDict
5
+ from .func_util import convert_default_value
6
+ from .node_registry import register_ng_setter
7
+ from typing import Generator
8
+ from itertools import tee, repeat
9
+
10
+
11
+ socket_attr = [
12
+ 'description',
13
+ 'enabled',
14
+ 'hide',
15
+ 'hide_value',
16
+ 'name',
17
+ 'pin_gizmo',
18
+ 'show_expanded',
19
+ 'type',
20
+ 'is_linked',
21
+ 'default_value',
22
+ ]
23
+
24
+ tree_exclude = [
25
+ 'name',
26
+ 'description',
27
+ 'in_out',
28
+ 'socket_type',
29
+ 'default_closed',
30
+ 'index',
31
+ 'position',
32
+ 'parent',
33
+ 'identifier',
34
+ 'is_multi_input',
35
+ 'item_type',
36
+ 'persistent_uid',
37
+ ]
38
+
39
+
40
+ ########################## NODE ###########################
41
+
42
+
43
+ def set_socket_attr(node: BNode, attr: PYDict, set_enum: bool=False) -> None:
44
+ def _set_sock(fil):
45
+ for sock, a in fil:
46
+ try:
47
+ setattr(sock, 'default_value', convert_default_value(sock, a['default_value']))
48
+ except Exception as e:
49
+ continue
50
+ condition = lambda i: i[1]['type'] not in ['CUSTOM'] and (True if set_enum else i[0].type not in ['MENU']) and not i[1]['is_linked'] and 'default_value' in i[1].keys()
51
+ inputs = getattr(node, 'inputs', None)
52
+ if inputs:
53
+ ifil = filter(condition, zip(inputs[:], attr['inputs']))
54
+ _set_sock(ifil)
55
+
56
+
57
+ def set_parent(node: BNode, attr: PYDict) -> None:
58
+ if hasattr(node, 'parent'):
59
+ if 'parent' in attr.keys():
60
+ if attr['parent'] not in [None, '']:
61
+ try:
62
+ setattr(node, 'parent', node.id_data.nodes[attr['parent']])
63
+ except Exception as e:
64
+ pass
65
+
66
+
67
+ def set_hide(node: BNode, attr: PYDict) -> None:
68
+ if 'inputs' in attr.keys():
69
+ if attr['inputs'] not in [None, []]:
70
+ try:
71
+ node.inputs.foreach_set('hide', list(i['hide'] for i in attr['inputs']))
72
+ except Exception as e:
73
+ pass
74
+ if 'outputs' in attr.keys():
75
+ if attr['outputs'] not in [None, []]:
76
+ try:
77
+ node.outputs.foreach_set('hide', list(i['hide'] for i in attr['outputs']))
78
+ except Exception as e:
79
+ pass
80
+
81
+
82
+ def process_node(node: BNode, data: PYDict, group_dict: dict[str, BGroup] | dict[None], parent: bool=True, set_enum: bool=False) -> None:
83
+ """Set node attributes from data.
84
+
85
+ :param node: Node to set the data to.
86
+ :type node: Node
87
+ :param data: Data used to set the node attributes.
88
+ :type data: dict[str, Any]
89
+ :param group_dict: Dict containing the node group objects.
90
+ :type group_dict: dict[str, Node Group]
91
+ :param parent: Set parent node if available.
92
+ :type parent: bool
93
+ :param set_enum: Dict containing the node group enum sockets.
94
+ :type set_enum: bool"""
95
+ try:
96
+ set_node_from_data(node, data, group_dict)
97
+ set_socket_attr(node, data, set_enum=set_enum)
98
+ if node.bl_idname not in ['NodeReroute']:
99
+ set_hide(node, data)
100
+ if parent:
101
+ set_parent(node, data)
102
+ except Exception as e:
103
+ pass
104
+
105
+
106
+ def set_nodes_from_data(data_dict: PYDict, node_dict: dict[str, BNode], group_dict: dict[str, BGroup]) -> None:
107
+ asset_nodes = set(get_asset_nodes())
108
+ for group, nodes in node_dict.items():
109
+ if group.split(".")[0] not in asset_nodes:
110
+ for _node, node in nodes.items():
111
+ process_node(node, data_dict[group]['nodes'][_node], group_dict)
112
+
113
+
114
+ ####################### NODE GROUP ########################
115
+
116
+
117
+ def create_node_groups_from_data(data: PYDict, group_type: str='GeometryNodeTree') -> Generator[tuple[str, BGroup], None, None]:
118
+ asset_nodes = set(get_asset_nodes())
119
+ return ((group, asset_node_group(group.split(".")[0]) if group.split(".")[0] in asset_nodes else bpy.data.node_groups.new(group, group_type)) for group in reversed(data.keys()))
120
+
121
+
122
+ def create_node_tree_nodes(node_tree: BGroup, data_nodes: PYDict) -> Generator[tuple[str, BNode], None, None]:
123
+ for node in data_nodes:
124
+ _node = node_tree.nodes.new(data_nodes[node]['bl_idname'])
125
+ _node.name = data_nodes[node]['name']
126
+ yield node, _node
127
+
128
+
129
+ def node_map(data: PYDict, groups: dict[str, BNode]) -> Generator[tuple[str, dict[str, BNode]], None, None]:
130
+ for group in groups:
131
+ if group[1] not in [None, ''] and data[group[0]]['nodes'] not in [None, '']:
132
+ yield group[0], dict(create_node_tree_nodes(group[1], data[group[0]]['nodes']))
133
+
134
+
135
+ def node_group_map(data: PYDict, group_type: str='GeometryNodeTree') -> tuple[str, Generator[tuple[str, dict[str, BNode]], None, None]]:
136
+ groups, _groups = tee(create_node_groups_from_data(data, group_type=group_type), 2)
137
+ return groups, node_map(data, _groups)
138
+
139
+
140
+ def set_interface(data: PYDict, node_tree: BGroup) -> None:
141
+ """Set sockets of node group from data.
142
+
143
+ :param data: Data to set the sockets.
144
+ :type data: dict[str, Any]
145
+ :param node_tree: Node group to set data to.
146
+ :type node_tree: Node Group"""
147
+ global tree_exclude
148
+ if 'interface' in data.keys() and data['interface'] != None and hasattr(node_tree, 'interface'):
149
+ for item in data['interface']:
150
+ for attr in item.keys():
151
+ if attr not in tree_exclude:
152
+ it = node_tree.interface.items_tree[data['interface']['index']]
153
+ if hasattr(it, attr) and item[attr] not in [None, '', []]:
154
+ try:
155
+ setattr(it, attr, item[attr])
156
+ except Exception:
157
+ try:
158
+ gi = getattr(it, attr, None)
159
+ if isinstance(gi, bpy.types.bpy_prop_array):
160
+ ct = len(gi)
161
+ _ct = len(item[attr])
162
+ count = ct if _ct > ct else _ct
163
+ val = list(repeat(0.0, ct))
164
+ for i in range(count):
165
+ val[i] = item[attr][i]
166
+ setattr(it, attr, val)
167
+ except Exception:
168
+ continue
169
+ return
170
+
171
+
172
+ def set_node_groups_from_data(data: PYDict, group_type: str='GeometryNodeTree') -> tuple[dict[str, BGroup], PYDict, PYDict, dict[str, BNode]]:
173
+ """Set node tree attributes from build data.
174
+
175
+ :param data: Node group build data.
176
+ :type data: dict[str, Any]
177
+ :param group_type: String indicating the bpy type of the node group.
178
+ :type group_type: str
179
+ :return: Tuple containing dictionaries for node group data, enum socket data, data to build node group, and node data.
180
+ :rtype: tuple[dict[str, node tree], dict[node_group.interface.items_tree, item], dict[str, Any], dict[str, node]]"""
181
+ global tree_exclude
182
+ group_dict, node_dict = node_group_map(data, group_type=group_type)
183
+ group_dict, group_dict_, _group_dict = tee(group_dict, 3)
184
+ [[setattr(group[1], a, data[group[0]][a]) for a in data[group[0]] if hasattr(group[1], a) and a not in ['interface', 'links', 'nodes', 'node_tree', 'bl_idname']] for group in group_dict]
185
+ def _set_interface(_group):
186
+ group, node_tree = _group
187
+ enums = []
188
+ if 'interface' in data[group].keys() and data[group]['interface'] != None and hasattr(node_tree, 'interface'):
189
+ for idx, item in enumerate(data[group]['interface']):
190
+ if item['item_type'] == 'SOCKET':
191
+ it = node_tree.interface.new_socket(item['name'], description=item['description'], in_out=item['in_out'], socket_type=item['socket_type'])
192
+ if item['socket_type'] == 'NodeSocketMenu' and (hasattr(it, 'default_value') and item['default_value'] not in [None, '']):
193
+ enums.append([it, idx])
194
+ elif item['item_type'] == 'PANEL':
195
+ it = node_tree.interface.new_panel(item['name'], description=item['description'], default_closed=item['default_closed'])
196
+ else:
197
+ it = None
198
+ for attr in item.keys():
199
+ if attr not in tree_exclude:
200
+ if it != None:
201
+ try:
202
+ if hasattr(it, attr) and item[attr] not in [None, '', []]:
203
+ if attr == 'default_value' and data[group]['interface'][idx]['socket_type'] == 'NodeSocketMenu':
204
+ continue
205
+ else:
206
+ setattr(it, attr, item[attr])
207
+ except Exception:
208
+ try:
209
+ gi = getattr(it, attr, None)
210
+ if isinstance(gi, bpy.types.bpy_prop_array):
211
+ ct = len(gi)
212
+ _ct = len(item[attr])
213
+ count = ct if _ct > ct else _ct
214
+ val = list(repeat(0.0, ct))
215
+ for i in range(count):
216
+ val[i] = item[attr][i]
217
+ setattr(it, attr, val)
218
+ except Exception:
219
+ continue
220
+ data[group]['interface'][idx]['item'] = it
221
+ return group, enums
222
+ enum_dict = dict(_set_interface(group) for group in group_dict_)
223
+ return dict(_group_dict), enum_dict, data, dict(node_dict)
224
+
225
+
226
+ def _new_link(link: bpy.types.NodeLink, node_tree: BGroup) -> None:
227
+ from_node = link[0]['node']
228
+ to_node = link[1]['node']
229
+ from_socket = link[0]['socket']
230
+ to_socket = link[1]['socket']
231
+ output = node_tree.nodes[from_node]
232
+ input = node_tree.nodes[to_node]
233
+ node_tree.links.new(output.outputs[from_socket], input.inputs[to_socket], verify_limits=True, handle_dynamic_sockets=True)
234
+
235
+
236
+ def set_node_group_links(data: PYDict, group_dict: dict[str, BGroup]) -> None:
237
+ asset_nodes = set(get_asset_nodes())
238
+ for tree in reversed(data.keys()):
239
+ if tree.split(".")[0] not in asset_nodes:
240
+ node_tree = group_dict[tree]
241
+ if not isinstance(node_tree, type(None)) and data[tree]['links'] != None:
242
+ for link in data[tree]['links']:
243
+ try:
244
+ _new_link(link, node_tree)
245
+ except IndexError:
246
+ continue
247
+
248
+
249
+ def set_node_groups_enums(data: PYDict, enum_dict: PYDict) -> None:
250
+ for group in enum_dict:
251
+ for item in enum_dict[group]:
252
+ try:
253
+ item[0].default_value = data[group]['interface'][item[1]]['default_value']
254
+ except Exception:
255
+ continue
256
+
257
+
258
+ def set_node_groups_interface_parents(data: PYDict, group_dict: dict[str, BGroup]) -> None:
259
+ asset_nodes = set(get_asset_nodes())
260
+ for group in reversed(data.keys()):
261
+ if group.split(".")[0] not in asset_nodes:
262
+ node_tree = group_dict[group]
263
+ _interface = data[group]['interface']
264
+ if _interface:
265
+ for i in range(len(_interface)):
266
+ try:
267
+ parent, pid = _interface[i]['parent']
268
+ position = _interface[i]['index']
269
+ item = _interface[i]['item']
270
+ if parent != "":
271
+ parent_ = next((p['item'] for p in (_ for _ in _interface if _['item_type'] == 'PANEL') if [parent, pid] == [p['name'], p['persistent_uid']]))
272
+ node_tree.interface.move_to_parent(item, parent_, position)
273
+ except Exception:
274
+ continue
275
+
276
+
277
+ @register_ng_setter('GeometryNodeTree')
278
+ def create_node_group_from_data(data: PYDict, group_type: str='GeometryNodeTree') -> BGroup:
279
+ """Create a node group from build data.
280
+
281
+ :param data: Dictionary containing data to build node grouo.
282
+ :type data: dict[str, Any]
283
+ :param group_type: String indicating the bpy type of the node group.
284
+ :type group_type: str
285
+ :return: NodeGroup.
286
+ :rtype: NodeGroup"""
287
+ group_dict, enum_dict, data, node_dict = set_node_groups_from_data(data, group_type=group_type)
288
+ set_nodes_from_data(data, node_dict, group_dict)
289
+ set_node_group_links(data, group_dict)
290
+ set_node_groups_enums(data, enum_dict)
291
+ set_node_groups_interface_parents(data, group_dict)
292
+ node_group = next(g for g in reversed(group_dict.values()))
293
+ return node_group
294
+
295
+
296
+ def add_node_group(ob: bpy.types.Object, name: str, data: PYDict) -> bpy.types.Modifier:
297
+ """Create Geometry Node modifier and node group.
298
+
299
+ :param ob: Object to add modifier to.
300
+ :type ob: Object
301
+ :param name: Modifier name.
302
+ :type name: str
303
+ :param data: Data to build node group to assign to the modifier.
304
+ :type data: dict[str, Any]
305
+ :return: Object modifier.
306
+ :rtype: Modifier"""
307
+ modifier = ob.modifiers.new(name, 'NODES')
308
+ node_group = create_node_group_from_data(data, group_type='GeometryNodeTree')
309
+ modifier.node_group = node_group
310
+ return modifier
311
+
312
+
313
+ ######################## MATERIAL #########################
314
+
315
+
316
+ @register_ng_setter('ShaderNodeTree')
317
+ def create_shader_group_from_data(data: PYDict, group_type: str='ShaderNodeTree', return_group_dict: bool=False) -> dict[str, BGroup] | BGroup:
318
+ """Create Shader Node node group.
319
+
320
+ :param data: Data dict to build shader node group.
321
+ :type data: dict[str, Any]
322
+ :param group_type: String indicating the bpy type of the node group.
323
+ :type group_type: str
324
+ :param return_group_dict: Choose to return the group_dict or the node group.
325
+ :type return_group_dict: bool
326
+ :return: Return a group_dict or node_group.
327
+ :rtype: dict[str, Node Group] | Node Group"""
328
+ group_dict, enum_dict, data, node_dict = set_node_groups_from_data(data, group_type=group_type)
329
+ set_nodes_from_data(data, node_dict, group_dict)
330
+ set_node_group_links(data, group_dict)
331
+ set_node_groups_enums(data, enum_dict)
332
+ set_node_groups_interface_parents(data, group_dict)
333
+ if return_group_dict:
334
+ return group_dict
335
+ node_group = next(g for g in reversed(group_dict.values()))
336
+ return node_group
337
+
338
+
339
+ def set_shader_from_data(data_dict: PYDict, node_dict: dict[str, BNode], group_dict: dict[str, BGroup]) -> None:
340
+ for _node, node in node_dict.items():
341
+ process_node(node, data_dict[_node], group_dict, set_enum=True)
342
+
343
+
344
+ def create_material(data: PYDict) -> bpy.types.Material:
345
+ """Create material and Shader Node node group.
346
+
347
+ :param data: Data dict to build material.
348
+ :type data: dict[str, Any]
349
+ :return: Material.
350
+ :rtype: Material"""
351
+ global tree_exclude
352
+ mat_name = list(data.keys())[0]
353
+ material = bpy.data.materials.new(mat_name)
354
+ for a in data[mat_name]:
355
+ if a not in ['node_tree'] and hasattr(material, a):
356
+ setattr(material, a, data[mat_name][a])
357
+ node_tree = getattr(material, 'node_tree', None)
358
+ if node_tree:
359
+ node_tree.nodes.clear()
360
+ mat_data = data[mat_name]['node_tree']
361
+ group_data = mat_data['node_groups']
362
+ nodes_data = mat_data['nodes']
363
+ link_data = mat_data['links']
364
+ group_dict = create_shader_group_from_data(group_data, group_type='ShaderNodeTree', return_group_dict=True)
365
+ nodes_dict = dict(create_node_tree_nodes(node_tree, nodes_data))
366
+ set_shader_from_data(nodes_data, nodes_dict, group_dict)
367
+ for link in link_data:
368
+ _new_link(link, node_tree)
369
+ return material
370
+
371
+
372
+ def add_material_to_ob(ob: bpy.types.Object, data: PYDict) -> bpy.types.Material:
373
+ """Create material from data and add to object material slot.
374
+
375
+ :param ob: Object to add material to.
376
+ :type ob: Object
377
+ :param data: Data dict to build material.
378
+ :type data: dict[str, Any]
379
+ :return: Material.
380
+ :rtype: Material"""
381
+ materials = ob.data.materials
382
+ material = create_material(data)
383
+ materials.append(material)
384
+ return material
385
+
386
+
387
+ ####################### COMPOSITOR ########################
388
+
389
+
390
+ @register_ng_setter('CompositorNodeTree')
391
+ def create_comp_group(data: PYDict) -> BGroup:
392
+ """Create compositor node group.
393
+
394
+ :param data: Data dict to build compositor node group.
395
+ :type data: dict[str, Any]
396
+ :return: Compositor NodeGroup.
397
+ :rtype: NodeGroup"""
398
+ try:
399
+ node_group = create_node_group_from_data(data, group_type='CompositorNodeTree')
400
+ return node_group
401
+ except KeyError as k:
402
+ print("[create_comp_group]:", k)
403
+ return
404
+
405
+
406
+ def add_comp_group(scene: bpy.types.Scene, data: PYDict) -> BGroup:
407
+ """Create compositor node group and add to scene.
408
+
409
+ :param scene: Scene to add compositor node group to.
410
+ :type scene: Scene
411
+ :param data: Data dict to build compositor node group.
412
+ :type data: dict[str, Any]
413
+ :return: Compositor NodeGroup.
414
+ :rtype: NodeGroup"""
415
+ scene.compositing_node_group = None
416
+ node_group = create_comp_group(data['node_groups'])
417
+ scene.compositing_node_group = node_group
418
+ return node_group
419
+
420
+
421
+ ####################### TEXTURE ########################
422
+
423
+
424
+ @register_ng_setter('TextureNodeTree')
425
+ def create_texture_group_from_data(data: PYDict) -> BGroup:
426
+ """Create a node group from build data.
427
+
428
+ :param data: Dictionary containing data to build node grouo.
429
+ :type data: dict[str, Any]
430
+ :return: NodeGroup.
431
+ :rtype: NodeGroup"""
432
+ node_group = create_node_group_from_data(data, group_type='TextureNodeTree')
433
+ return node_group
node_to_json/py.typed ADDED
File without changes