Skip to content

Commit 6f1bf76

Browse files
Merge pull request #79 from BrendanParmer/BlenderV4_0Nodes
Blender v4.0 nodes
2 parents 521be11 + eca90aa commit 6f1bf76

File tree

10 files changed

+1164
-975
lines changed

10 files changed

+1164
-975
lines changed

compositor/node_settings.py

Lines changed: 220 additions & 187 deletions
Large diffs are not rendered by default.

compositor/operator.py

Lines changed: 113 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import bpy
22

3-
from bpy.types import Node
3+
from bpy.types import Node, CompositorNodeColorBalance, CompositorNodeTree
44

55
from ..ntp_operator import NTP_Operator
66
from ..ntp_node_tree import NTP_NodeTree
@@ -37,125 +37,139 @@ def __init__(self):
3737

3838
def _create_scene(self, indent: str):
3939
#TODO: wrap in more general unique name util function
40-
self._write(f"{indent}# Generate unique scene name\n")
41-
self._write(f"{indent}{BASE_NAME_VAR} = {str_to_py_str(self.compositor_name)}\n")
42-
self._write(f"{indent}{END_NAME_VAR} = {BASE_NAME_VAR}\n")
43-
self._write(f"{indent}if bpy.data.scenes.get({END_NAME_VAR}) != None:\n")
44-
self._write(f"{indent}\ti = 1\n")
45-
self._write(f"{indent}\t{END_NAME_VAR} = {BASE_NAME_VAR} + f\".{{i:03d}}\"\n")
46-
self._write(f"{indent}\twhile bpy.data.scenes.get({END_NAME_VAR}) != None:\n")
47-
self._write(f"{indent}\t\t{END_NAME_VAR} = {BASE_NAME_VAR} + f\".{{i:03d}}\"\n")
48-
self._write(f"{indent}\t\ti += 1\n\n")
49-
50-
self._write(f"{indent}{SCENE_VAR} = bpy.context.window.scene.copy()\n\n")
51-
self._write(f"{indent}{SCENE_VAR}.name = {END_NAME_VAR}\n")
52-
self._write(f"{indent}{SCENE_VAR}.use_fake_user = True\n")
53-
self._write(f"{indent}bpy.context.window.scene = {SCENE_VAR}\n")
54-
55-
def _initialize_compositor_node_tree(self, outer, nt_var, level, inner, nt_name):
56-
#initialize node group
57-
self._write(f"{outer}#initialize {nt_var} node group\n")
58-
self._write(f"{outer}def {nt_var}_node_group():\n")
59-
60-
if self._is_outermost_node_group(level): #outermost node group
61-
self._write(f"{inner}{nt_var} = {SCENE_VAR}.node_tree\n")
62-
self._write(f"{inner}#start with a clean node tree\n")
63-
self._write(f"{inner}for node in {nt_var}.nodes:\n")
64-
self._write(f"{inner}\t{nt_var}.nodes.remove(node)\n")
40+
self._write(f"# Generate unique scene name", indent)
41+
self._write(f"{BASE_NAME_VAR} = {str_to_py_str(self.compositor_name)}",
42+
indent)
43+
self._write(f"{END_NAME_VAR} = {BASE_NAME_VAR}", indent)
44+
self._write(f"if bpy.data.scenes.get({END_NAME_VAR}) != None:", indent)
45+
46+
indent2 = f"{indent}\t"
47+
self._write(f"i = 1", indent2)
48+
self._write(f"{END_NAME_VAR} = {BASE_NAME_VAR} + f\".{{i:03d}}\"",
49+
indent2)
50+
self._write(f"while bpy.data.scenes.get({END_NAME_VAR}) != None:",
51+
indent2)
52+
53+
indent3 = f"{indent}\t\t"
54+
self._write(f"{END_NAME_VAR} = {BASE_NAME_VAR} + f\".{{i:03d}}\"", indent3)
55+
self._write(f"i += 1\n", indent3)
56+
57+
self._write(f"{SCENE_VAR} = bpy.context.window.scene.copy()\n", indent)
58+
self._write(f"{SCENE_VAR}.name = {END_NAME_VAR}", indent)
59+
self._write(f"{SCENE_VAR}.use_fake_user = True", indent)
60+
self._write(f"bpy.context.window.scene = {SCENE_VAR}", indent)
61+
62+
def _initialize_compositor_node_tree(self, ntp_nt, nt_name):
63+
#initialize node group
64+
self._write(f"#initialize {nt_name} node group", self._outer)
65+
self._write(f"def {ntp_nt.var}_node_group():", self._outer)
66+
67+
if ntp_nt.node_tree == self._base_node_tree:
68+
self._write(f"{ntp_nt.var} = {SCENE_VAR}.node_tree")
69+
self._write(f"#start with a clean node tree")
70+
self._write(f"for node in {ntp_nt.var}.nodes:")
71+
self._write(f"\t{ntp_nt.var}.nodes.remove(node)")
6572
else:
66-
self._write((f"{inner}{nt_var}"
67-
f"= bpy.data.node_groups.new("
73+
self._write((f"{ntp_nt.var} = bpy.data.node_groups.new("
6874
f"type = \'CompositorNodeTree\', "
69-
f"name = {str_to_py_str(nt_name)})\n"))
70-
self._write("\n")
71-
72-
def _process_node(self, node: Node, ntp_nt: NTP_NodeTree, inner: str, level: int):
73-
if node.bl_idname == 'CompositorNodeGroup':
74-
node_nt = node.node_tree
75-
if node_nt is not None and node_nt not in self._node_trees:
76-
self._process_comp_node_group(node_nt, level + 1, self._node_vars,
77-
self._used_vars)
78-
self._node_trees.add(node_nt)
79-
80-
node_var: str = self._create_node(node, inner, ntp_nt.var)
75+
f"name = {str_to_py_str(nt_name)})"))
76+
self._write("")
77+
78+
def _set_color_balance_settings(self, node: CompositorNodeColorBalance
79+
) -> None:
80+
"""
81+
Sets the color balance settings so we only set the active variables,
82+
preventing conflict
83+
84+
node (CompositorNodeColorBalance): the color balance node
85+
"""
86+
if node.correction_method == 'LIFT_GAMMA_GAIN':
87+
lst = [("correction_method", ST.ENUM),
88+
("gain", ST.COLOR),
89+
("gamma", ST.COLOR),
90+
("lift", ST.COLOR)]
91+
else:
92+
lst = [("correction_method", ST.ENUM),
93+
("offset", ST.COLOR),
94+
("offset_basis", ST.FLOAT),
95+
("power", ST.COLOR),
96+
("slope", ST.COLOR)]
97+
98+
self._settings['CompositorNodeColorBalance'] = lst
99+
100+
def _process_node(self, node: Node, ntp_nt: NTP_NodeTree):
101+
"""
102+
Create node and set settings, defaults, and cosmetics
103+
104+
Parameters:
105+
node (Node): node to process
106+
ntp_nt (NTP_NodeTree): the node tree that node belongs to
107+
"""
108+
node_var: str = self._create_node(node, ntp_nt.var)
81109

82110
if node.bl_idname == 'CompositorNodeColorBalance':
83-
if node.correction_method == 'LIFT_GAMMA_GAIN':
84-
lst = [("correction_method", ST.ENUM),
85-
("gain", ST.COLOR),
86-
("gamma", ST.COLOR),
87-
("lift", ST.COLOR)]
88-
else:
89-
lst = [("correction_method", ST.ENUM),
90-
("offset", ST.COLOR),
91-
("offset_basis", ST.FLOAT),
92-
("power", ST.COLOR),
93-
("slope", ST.COLOR)]
94-
95-
self._settings['CompositorNodeColorBalance'] = lst
96-
97-
self._set_settings_defaults(node, inner, node_var)
98-
self._hide_hidden_sockets(node, inner, node_var)
99-
100-
if node.bl_idname == 'CompositorNodeGroup':
101-
if node.node_tree is not None:
102-
self._write((f"{inner}{node_var}.node_tree = "
103-
f"bpy.data.node_groups"
104-
f"[\"{node.node_tree.name}\"]\n"))
105-
elif node.bl_idname == 'NodeGroupInput' and not inputs_set:
106-
self._group_io_settings(node, inner, "input", ntp_nt)
107-
inputs_set = True
108-
109-
elif node.bl_idname == 'NodeGroupOutput' and not outputs_set:
110-
self._group_io_settings(node, inner, "output", ntp_nt)
111-
outputs_set = True
112-
113-
self._set_socket_defaults(node, node_var, inner)
111+
self._set_color_balance_settings(node)
112+
113+
self._set_settings_defaults(node)
114+
self._hide_hidden_sockets(node)
115+
116+
if bpy.app.version < (4, 0, 0):
117+
if node.bl_idname == 'NodeGroupInput' and not ntp_nt.inputs_set:
118+
self._group_io_settings(node, "input", ntp_nt)
119+
ntp_nt.inputs_set = True
120+
121+
elif node.bl_idname == 'NodeGroupOutput' and not ntp_nt.outputs_set:
122+
self._group_io_settings(node, "output", ntp_nt)
123+
ntp_nt.outputs_set = True
124+
125+
self._set_socket_defaults(node)
114126

115-
def _process_node_tree(self, node_tree, level):
127+
def _process_node_tree(self, node_tree: CompositorNodeTree):
116128
"""
117129
Generates a Python function to recreate a compositor node tree
118130
119131
Parameters:
120-
node_tree (NodeTree): node tree to be recreated
121-
level (int): number of tabs to use for each line
122-
132+
node_tree (CompositorNodeTree): node tree to be recreated
123133
"""
124-
if self._is_outermost_node_group(level):
134+
if node_tree == self._base_node_tree:
125135
nt_var = self._create_var(self.compositor_name)
126136
nt_name = self.compositor_name
127137
else:
128138
nt_var = self._create_var(node_tree.name)
129139
nt_name = node_tree.name
130140

131-
outer, inner = make_indents(level)
141+
self._node_tree_vars[node_tree] = nt_var
132142

133-
self._initialize_compositor_node_tree(outer, nt_var, level, inner, nt_name)
134-
135143
ntp_nt = NTP_NodeTree(node_tree, nt_var)
144+
self._initialize_compositor_node_tree(ntp_nt, nt_name)
145+
146+
if bpy.app.version >= (4, 0, 0):
147+
self._tree_interface_settings(ntp_nt)
136148

137149
#initialize nodes
138-
self._write(f"{inner}#initialize {nt_var} nodes\n")
150+
self._write(f"#initialize {nt_var} nodes")
139151

140152
for node in node_tree.nodes:
141-
self._process_node(node, ntp_nt, inner, level)
153+
self._process_node(node, ntp_nt)
142154

143-
self._set_parents(node_tree, inner)
144-
self._set_locations(node_tree, inner)
145-
self._set_dimensions(node_tree, inner)
155+
self._set_parents(node_tree)
156+
self._set_locations(node_tree)
157+
self._set_dimensions(node_tree)
146158

147-
self._init_links(node_tree, inner, nt_var)
159+
self._init_links(node_tree)
148160

149-
self._write(f"\n{outer}{nt_var}_node_group()\n\n")
161+
self._write(f"return {nt_var}\n")
162+
163+
self._write(f"{nt_var} = {nt_var}_node_group()\n", self._outer)
150164

151165
def execute(self, context):
152166
#find node group to replicate
153167
if self.is_scene:
154-
nt = bpy.data.scenes[self.compositor_name].node_tree
168+
self._base_node_tree = bpy.data.scenes[self.compositor_name].node_tree
155169
else:
156-
nt = bpy.data.node_groups[self.compositor_name]
170+
self._base_node_tree = bpy.data.node_groups[self.compositor_name]
157171

158-
if nt is None:
172+
if self._base_node_tree is None:
159173
#shouldn't happen
160174
self.report({'ERROR'},("NodeToPython: This doesn't seem to be a "
161175
"valid compositor node tree. Is Use Nodes "
@@ -166,6 +180,9 @@ def execute(self, context):
166180
comp_var = clean_string(self.compositor_name)
167181

168182
if self.mode == 'ADDON':
183+
self._outer = "\t\t"
184+
self._inner = "\t\t\t"
185+
169186
self._setup_addon_directories(context, comp_var)
170187

171188
self._file = open(f"{self._addon_dir}/__init__.py", "w")
@@ -174,7 +191,7 @@ def execute(self, context):
174191
self._class_name = clean_string(self.compositor_name, lower=False)
175192
self._init_operator(comp_var, self.compositor_name)
176193

177-
self._write("\tdef execute(self, context):\n")
194+
self._write("def execute(self, context):", "\t")
178195
else:
179196
self._file = StringIO("")
180197

@@ -183,15 +200,14 @@ def execute(self, context):
183200
self._create_scene("\t\t")
184201
elif self.mode == 'SCRIPT':
185202
self._create_scene("")
186-
187-
if self.mode == 'ADDON':
188-
level = 2
189-
else:
190-
level = 0
191-
self._process_node_tree(nt, level)
203+
204+
node_trees_to_process = self._topological_sort(self._base_node_tree)
205+
206+
for node_tree in node_trees_to_process:
207+
self._process_node_tree(node_tree)
192208

193209
if self.mode == 'ADDON':
194-
self._write("\t\treturn {'FINISHED'}\n\n")
210+
self._write("return {'FINISHED'}\n", self._outer)
195211

196212
self._create_menu_func()
197213
self._create_register_func()

0 commit comments

Comments
 (0)