Skip to content

Commit 89d9d48

Browse files
committed
feat: 4.0 node tree changes
1 parent 344e4c5 commit 89d9d48

File tree

5 files changed

+236
-72
lines changed

5 files changed

+236
-72
lines changed

compositor/operator.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,11 @@ def _process_node(self, node: Node, ntp_nt: NTP_NodeTree, inner: str, level: int
103103
f"bpy.data.node_groups"
104104
f"[\"{node.node_tree.name}\"]\n"))
105105
elif node.bl_idname == 'NodeGroupInput' and not inputs_set:
106-
if bpy.app.version < (4, 0, 0):
107-
self._group_io_settings_v3(node, inner, "input", ntp_nt)
106+
self._group_io_settings(node, inner, "input", ntp_nt)
108107
inputs_set = True
109108

110109
elif node.bl_idname == 'NodeGroupOutput' and not outputs_set:
111-
if bpy.app.version < (4, 0, 0):
112-
self._group_io_settings_v3(node, inner, "output", ntp_nt)
110+
self._group_io_settings(node, inner, "output", ntp_nt)
113111
outputs_set = True
114112

115113
self._set_socket_defaults(node, node_var, inner)

geometry/operator.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,11 @@ def _process_node(self, node: Node, ntp_node_tree: NTP_GeoNodeTree,
5555
self._process_group_node_tree(node, node_var, level, inner)
5656

5757
elif node.bl_idname == 'NodeGroupInput' and not ntp_node_tree.inputs_set:
58-
if bpy.app.version < (4, 0, 0):
59-
self._group_io_settings_v3(node, inner, "input", ntp_node_tree)
58+
self._group_io_settings(node, inner, "input", ntp_node_tree)
6059
ntp_node_tree.inputs_set = True
6160

6261
elif node.bl_idname == 'NodeGroupOutput' and not ntp_node_tree.outputs_set:
63-
if bpy.app.version < (4, 0, 0):
64-
self._group_io_settings_v3(node, inner, "output", ntp_node_tree)
62+
self._group_io_settings(node, inner, "output", ntp_node_tree)
6563
ntp_node_tree.outputs_set = True
6664

6765
elif node.bl_idname == 'GeometryNodeSimulationInput':

material/operator.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,13 +52,11 @@ def _process_node(self, node: Node, ntp_node_tree: NTP_NodeTree, inner: str, lev
5252
if node.bl_idname == 'ShaderNodeGroup':
5353
self._process_group_node_tree(node, node_var, level, inner)
5454
elif node.bl_idname == 'NodeGroupInput' and not ntp_node_tree.inputs_set:
55-
if bpy.app.version < (4, 0, 0):
56-
self._group_io_settings_v3(node, inner, "input", ntp_node_tree)
55+
self._group_io_settings(node, inner, "input", ntp_node_tree)
5756
ntp_node_tree.inputs_set = True
5857

5958
elif node.bl_idname == 'NodeGroupOutput' and not ntp_node_tree.outputs_set:
60-
if bpy.app.version < (4, 0, 0):
61-
self._group_io_settings_v3(node, inner, "output", ntp_node_tree)
59+
self._group_io_settings(node, inner, "output", ntp_node_tree)
6260
ntp_node_tree.outputs_set = True
6361

6462
self._hide_hidden_sockets(node, inner, node_var)

ntp_operator.py

Lines changed: 230 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,20 @@ class NTP_Operator(Operator):
2929
]
3030
)
3131

32+
#node tree input sockets that have default properties
33+
if bpy.app.version < (4, 0, 0):
34+
default_sockets_v3 = {'VALUE', 'INT', 'BOOLEAN', 'VECTOR', 'RGBA'}
35+
else:
36+
nondefault_sockets_v4 = {
37+
bpy.types.NodeTreeInterfaceSocketCollection,
38+
bpy.types.NodeTreeInterfaceSocketGeometry,
39+
bpy.types.NodeTreeInterfaceSocketImage,
40+
bpy.types.NodeTreeInterfaceSocketMaterial,
41+
bpy.types.NodeTreeInterfaceSocketObject,
42+
bpy.types.NodeTreeInterfaceSocketShader,
43+
bpy.types.NodeTreeInterfaceSocketTexture
44+
}
45+
3246
def __init__(self):
3347
super().__init__()
3448

@@ -275,7 +289,7 @@ def _set_group_socket_default_v3(self, socket_interface: NodeSocketInterface,
275289
inner (str): indentation string
276290
socket_var (str): variable name for the socket
277291
"""
278-
if socket_interface.type not in default_sockets:
292+
if socket_interface.type not in self.default_sockets_v3:
279293
return
280294

281295
if socket_interface.type == 'RGBA':
@@ -295,77 +309,236 @@ def _set_group_socket_default_v3(self, socket_interface: NodeSocketInterface,
295309
max_val = socket_interface.max_value
296310
self._write((f"{inner}{socket_var}.max_value = {max_val}\n"))
297311

298-
def _group_io_settings(self, node: bpy.types.Node, inner: str,
299-
io: str, # TODO: convert to enum
300-
ntp_node_tree: NTP_NodeTree) -> None:
301-
"""
302-
Set the settings for group input and output sockets
312+
def _group_io_settings_v3(self, node: bpy.types.Node, inner: str,
313+
io: str, # TODO: convert to enum
314+
ntp_node_tree: NTP_NodeTree) -> None:
315+
"""
316+
Set the settings for group input and output sockets
303317
304-
Parameters:
305-
node (bpy.types.Node) : group input/output node
306-
inner (str): indentation string
307-
io (str): whether we're generating the input or output settings
308-
node_tree_var (str): variable name of the generated node tree
309-
node_tree (bpy.types.NodeTree): node tree that we're generating input
310-
and output settings for
311-
"""
312-
node_tree_var = ntp_node_tree.var
313-
node_tree = ntp_node_tree.node_tree
318+
Parameters:
319+
node (bpy.types.Node) : group input/output node
320+
inner (str): indentation string
321+
io (str): whether we're generating the input or output settings
322+
node_tree_var (str): variable name of the generated node tree
323+
node_tree (bpy.types.NodeTree): node tree that we're generating
324+
input and output settings for
325+
"""
326+
node_tree_var = ntp_node_tree.var
327+
node_tree = ntp_node_tree.node_tree
314328

315-
if io == "input":
316-
io_sockets = node.outputs
317-
io_socket_interfaces = node_tree.inputs
318-
else:
319-
io_sockets = node.inputs
320-
io_socket_interfaces = node_tree.outputs
329+
if io == "input":
330+
io_sockets = node.outputs
331+
io_socket_interfaces = node_tree.inputs
332+
else:
333+
io_sockets = node.inputs
334+
io_socket_interfaces = node_tree.outputs
335+
336+
self._write(f"{inner}#{node_tree_var} {io}s\n")
337+
for i, inout in enumerate(io_sockets):
338+
if inout.bl_idname == 'NodeSocketVirtual':
339+
continue
340+
self._write(f"{inner}#{io} {inout.name}\n")
341+
idname = enum_to_py_str(inout.bl_idname)
342+
name = str_to_py_str(inout.name)
343+
self._write(
344+
f"{inner}{node_tree_var}.{io}s.new({idname}, {name})\n")
345+
socket_interface = io_socket_interfaces[i]
346+
socket_var = f"{node_tree_var}.{io}s[{i}]"
347+
348+
self._set_group_socket_default_v3(socket_interface, inner,
349+
socket_var)
350+
351+
# default attribute name
352+
if hasattr(socket_interface, "default_attribute_name"):
353+
if socket_interface.default_attribute_name != "":
354+
dan = str_to_py_str(
355+
socket_interface.default_attribute_name)
356+
self._write((f"{inner}{socket_var}"
357+
f".default_attribute_name = {dan}\n"))
358+
359+
# attribute domain
360+
if hasattr(socket_interface, "attribute_domain"):
361+
ad = enum_to_py_str(socket_interface.attribute_domain)
362+
self._write(f"{inner}{socket_var}.attribute_domain = {ad}\n")
363+
364+
# tooltip
365+
if socket_interface.description != "":
366+
description = str_to_py_str(socket_interface.description)
367+
self._write(
368+
(f"{inner}{socket_var}.description = {description}\n"))
321369

322-
self._write(f"{inner}#{node_tree_var} {io}s\n")
323-
for i, inout in enumerate(io_sockets):
324-
if inout.bl_idname == 'NodeSocketVirtual':
325-
continue
326-
self._write(f"{inner}#{io} {inout.name}\n")
327-
idname = enum_to_py_str(inout.bl_idname)
328-
name = str_to_py_str(inout.name)
329-
self._write(
330-
f"{inner}{node_tree_var}.{io}s.new({idname}, {name})\n")
331-
socket_interface = io_socket_interfaces[i]
332-
socket_var = f"{node_tree_var}.{io}s[{i}]"
370+
# hide_value
371+
if socket_interface.hide_value is True:
372+
self._write(f"{inner}{socket_var}.hide_value = True\n")
373+
374+
# hide in modifier
375+
if hasattr(socket_interface, "hide_in_modifier"):
376+
if socket_interface.hide_in_modifier is True:
377+
self._write(
378+
f"{inner}{socket_var}.hide_in_modifier = True\n")
379+
380+
self._write("\n")
381+
self._write("\n")
382+
383+
elif bpy.app.version >= (4, 0, 0):
384+
def _set_group_socket_default_v4(self, socket_interface: bpy.types.NodeTreeInterfaceSocket,
385+
inner: str, socket_var: str) -> None:
386+
"""
387+
Set a node group input/output's default properties if they exist
388+
389+
Parameters:
390+
socket_interface (NodeTreeInterfaceSocket): socket interface associated
391+
with the input/output
392+
inner (str): indentation string
393+
socket_var (str): variable name for the socket
394+
"""
395+
if type(socket_interface) in self.nondefault_sockets_v4:
396+
return
333397

334-
if bpy.app.version < (4, 0, 0):
335-
self._set_group_socket_default_v3(
336-
socket_interface, inner, socket_var)
398+
dv = socket_interface.default_value
337399

338-
# default attribute name
339-
if hasattr(socket_interface, "default_attribute_name"):
400+
if type(socket_interface) == bpy.types.NodeTreeInterfaceSocketColor:
401+
dv = vec4_to_py_str(dv)
402+
elif type(dv) in {mathutils.Vector, mathutils.Euler}:
403+
dv = vec3_to_py_str(dv)
404+
elif type(dv) == str:
405+
dv = str_to_py_str(dv)
406+
self._write(f"{inner}{socket_var}.default_value = {dv}\n")
407+
408+
# min value
409+
if hasattr(socket_interface, "min_value"):
410+
min_val = socket_interface.min_value
411+
self._write(f"{inner}{socket_var}.min_value = {min_val}\n")
412+
# max value
413+
if hasattr(socket_interface, "min_value"):
414+
max_val = socket_interface.max_value
415+
self._write((f"{inner}{socket_var}.max_value = {max_val}\n"))
416+
417+
def _group_io_settings_v4(self, node: bpy.types.Node, inner: str,
418+
io: str, # TODO: convert to enum
419+
ntp_node_tree: NTP_NodeTree) -> None:
420+
"""
421+
Set the settings for group input and output sockets
422+
423+
Parameters:
424+
node (bpy.types.Node) : group input/output node
425+
inner (str): indentation string
426+
io (str): whether we're generating the input or output settings
427+
node_tree_var (str): variable name of the generated node tree
428+
node_tree (bpy.types.NodeTree): node tree that we're generating
429+
input and output settings for
430+
"""
431+
node_tree_var = ntp_node_tree.var
432+
node_tree = ntp_node_tree.node_tree
433+
434+
if io == "input":
435+
io_sockets = node.outputs # Might be removeable,
436+
# think we can get all the info from the inouts
437+
# from the socket interfaces, need to double check.
438+
# If so, then we can just run these at the initialization
439+
# of the node tree, meaning we can clean up the clunky
440+
# Group Input/Group Output node reliance, two calls
441+
# Should be pretty easy to add in panels afterwards,
442+
# looks like those are tied fairly close to the new socket
443+
# system
444+
items_tree = node_tree.interface.items_tree
445+
io_socket_interfaces = [item for item in items_tree
446+
if item.item_type == 'SOCKET'
447+
and item.in_out == 'INPUT']
448+
else:
449+
io_sockets = node.inputs
450+
items_tree = node_tree.interface.items_tree
451+
io_socket_interfaces = [item for item in items_tree
452+
if item.item_type == 'SOCKET'
453+
and item.in_out == 'OUTPUT']
454+
455+
self._write(f"{inner}#{node_tree_var} {io}s\n")
456+
for i, socket_interface in enumerate(io_socket_interfaces):
457+
self._write(f"{inner}#{io} {socket_interface.name}\n")
458+
459+
socket_interface: bpy.types.NodeTreeInterfaceSocket = io_socket_interfaces[i]
460+
461+
#initialization
462+
socket_var = clean_string(socket_interface.name) + "_socket"
463+
name = str_to_py_str(socket_interface.name)
464+
in_out_enum = enum_to_py_str(socket_interface.in_out)
465+
466+
socket_type = enum_to_py_str(socket_interface.bl_socket_idname)
467+
"""
468+
I might be missing something, but the Python API's set up a bit
469+
weird here now. The new socket initialization only accepts types
470+
from a list of basic ones, but there doesn't seem to be a way of
471+
retrieving just this basic typewithout the subtype information.
472+
"""
473+
if 'Float' in socket_type:
474+
socket_type = enum_to_py_str('NodeSocketFloat')
475+
elif 'Int' in socket_type:
476+
socket_type = enum_to_py_str('NodeSocketInt')
477+
elif 'Vector' in socket_type:
478+
socket_type = enum_to_py_str('NodeSocketVector')
479+
480+
481+
self._write(f"{inner}{socket_var} = "
482+
f"{node_tree_var}.interface.new_socket("
483+
f"name = {name}, in_out={in_out_enum}, "
484+
f"socket_type = {socket_type})\n")
485+
486+
#subtype
487+
if hasattr(socket_interface, "subtype"):
488+
subtype = enum_to_py_str(socket_interface.subtype)
489+
self._write(f"{inner}{socket_var}.subtype = {subtype}\n")
490+
491+
self._set_group_socket_default_v4(socket_interface, inner,
492+
socket_var)
493+
494+
# default attribute name
340495
if socket_interface.default_attribute_name != "":
341-
dan = str_to_py_str(
342-
socket_interface.default_attribute_name)
343-
self._write((f"{inner}{socket_var}"
344-
f".default_attribute_name = {dan}\n"))
496+
dan = str_to_py_str(socket_interface.default_attribute_name)
497+
self._write((f"{inner}{socket_var}.default_attribute_name = {dan}\n"))
345498

346-
# attribute domain
347-
if hasattr(socket_interface, "attribute_domain"):
499+
# attribute domain
348500
ad = enum_to_py_str(socket_interface.attribute_domain)
349501
self._write(f"{inner}{socket_var}.attribute_domain = {ad}\n")
350502

351-
# tooltip
352-
if socket_interface.description != "":
353-
description = str_to_py_str(socket_interface.description)
354-
self._write(
355-
(f"{inner}{socket_var}.description = {description}\n"))
503+
# tooltip
504+
if socket_interface.description != "":
505+
description = str_to_py_str(socket_interface.description)
506+
self._write(
507+
(f"{inner}{socket_var}.description = {description}\n"))
356508

357-
# hide_value
358-
if socket_interface.hide_value is True:
359-
self._write(f"{inner}{socket_var}.hide_value = True\n")
509+
# hide_value
510+
if socket_interface.hide_value is True:
511+
self._write(f"{inner}{socket_var}.hide_value = True\n")
360512

361-
# hide in modifier
362-
if hasattr(socket_interface, "hide_in_modifier"):
513+
# hide in modifier
363514
if socket_interface.hide_in_modifier is True:
364-
self._write(
365-
f"{inner}{socket_var}.hide_in_modifier = True\n")
515+
self._write(f"{inner}{socket_var}.hide_in_modifier = True\n")
516+
517+
#force non field
518+
if socket_interface.force_non_field is True:
519+
self._write(f"{inner}{socket_var}.force_non_field = True\n")
366520

521+
self._write("\n")
367522
self._write("\n")
368-
self._write("\n")
523+
524+
def _group_io_settings(self, node: bpy.types.Node, inner: str,
525+
io: str, # TODO: convert to enum
526+
ntp_node_tree: NTP_NodeTree) -> None:
527+
"""
528+
Set the settings for group input and output sockets
529+
530+
Parameters:
531+
node (bpy.types.Node) : group input/output node
532+
inner (str): indentation string
533+
io (str): whether we're generating the input or output settings
534+
node_tree_var (str): variable name of the generated node tree
535+
node_tree (bpy.types.NodeTree): node tree that we're generating
536+
input and output settings for
537+
"""
538+
if bpy.app.version < (4, 0, 0):
539+
self._group_io_settings_v3(node, inner, io, ntp_node_tree)
540+
else:
541+
self._group_io_settings_v4(node, inner, io, ntp_node_tree)
369542

370543
def _set_input_defaults(self, node: bpy.types.Node, inner: str,
371544
node_var: str) -> None:

utils.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,6 @@ class ST(Enum):
5555
FILE_SLOTS = auto()
5656
LAYER_SLOTS = auto() #unimplemented
5757

58-
#node tree input sockets that have default properties
59-
default_sockets = {'VALUE', 'INT', 'BOOLEAN', 'VECTOR', 'RGBA'}
60-
6158
def clean_string(string: str, lower: bool = True) -> str:
6259
"""
6360
Cleans up a string for use as a variable or file name

0 commit comments

Comments
 (0)