Skip to content

Commit 92d3e2f

Browse files
Merge pull request #183 from BrendanParmer/EssentialNodeGroups
Essential node groups
2 parents 80a4298 + 0dc4c05 commit 92d3e2f

File tree

6 files changed

+108
-18
lines changed

6 files changed

+108
-18
lines changed

NodeToPython/compositor/operator.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,15 +254,16 @@ def execute(self, context):
254254

255255
self._file = open(f"{self._addon_dir}/__init__.py", "w")
256256

257-
self._create_header(self.compositor_name)
257+
self._create_bl_info(self.compositor_name)
258+
self._create_imports()
258259
self._class_name = clean_string(self.compositor_name, lower=False)
259260
self._init_operator(comp_var, self.compositor_name)
260261

261262
self._write("def execute(self, context):", 1)
262263
else:
263264
self._file = StringIO("")
264265
if self._include_imports:
265-
self._file.write("import bpy\nimport mathutils\n\n\n")
266+
self._create_imports()
266267

267268
if self.is_scene:
268269
if self._mode == 'ADDON':
@@ -273,6 +274,8 @@ def execute(self, context):
273274

274275
node_trees_to_process = self._topological_sort(self._base_node_tree)
275276

277+
self._import_essential_libs()
278+
276279
for node_tree in node_trees_to_process:
277280
self._process_node_tree(node_tree)
278281

NodeToPython/geometry/operator.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def _set_geo_tree_properties(self, node_tree: GeometryNodeTree) -> None:
113113

114114
if bpy.app.version >= (5, 0, 0):
115115
if node_tree.show_modifier_manage_panel:
116-
self._write(f"{nt_var}.show_modifier_manager_panel = True")
116+
self._write(f"{nt_var}.show_modifier_manage_panel = True")
117117
self._write("", 0)
118118

119119
def _process_node_tree(self, node_tree: GeometryNodeTree) -> None:
@@ -196,18 +196,21 @@ def execute(self, context):
196196

197197
self._file = open(f"{self._addon_dir}/__init__.py", "w")
198198

199-
self._create_header(nt.name)
199+
self._create_bl_info(nt.name)
200+
self._create_imports()
200201
self._class_name = clean_string(nt.name, lower = False)
201202
self._init_operator(nt_var, nt.name)
202203
self._write("def execute(self, context):", 1)
203204
else:
204205
self._file = StringIO("")
205206
if self._include_imports:
206-
self._file.write("import bpy\nimport mathutils\n\n\n")
207+
self._create_imports()
207208

208209

209210
node_trees_to_process = self._topological_sort(nt)
210211

212+
self._import_essential_libs()
213+
211214
for node_tree in node_trees_to_process:
212215
self._process_node_tree(node_tree)
213216

NodeToPython/ntp_operator.py

Lines changed: 84 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import datetime
1414
import os
15+
import pathlib
1516
import shutil
1617
from typing import TextIO, Callable
1718

@@ -26,14 +27,24 @@
2627
IMAGE_PATH = "image_path"
2728
ITEM = "item"
2829
BASE_DIR = "base_dir"
30+
DATAFILES_PATH = "datafiles_path"
31+
LIB_RELPATH = "lib_relpath"
32+
LIB_PATH = "lib_path"
33+
DATA_SRC = "data_src"
34+
DATA_DST = "data_dst"
2935

3036
RESERVED_NAMES = {
31-
INDEX,
32-
IMAGE_DIR_NAME,
33-
IMAGE_PATH,
34-
ITEM,
35-
BASE_DIR
36-
}
37+
INDEX,
38+
IMAGE_DIR_NAME,
39+
IMAGE_PATH,
40+
ITEM,
41+
BASE_DIR,
42+
DATAFILES_PATH,
43+
LIB_RELPATH,
44+
LIB_PATH,
45+
DATA_SRC,
46+
DATA_DST
47+
}
3748

3849
#node input sockets that are messy to set default values for
3950
DONT_SET_DEFAULTS = {
@@ -120,6 +131,10 @@ def __init__(self, *args, **kwargs):
120131
# Indentation string (default four spaces)
121132
self._indentation = " "
122133

134+
self._link_external_node_groups = True
135+
136+
self._lib_trees: dict[pathlib.Path, list[bpy.types.NodeTree]] = {}
137+
123138
if bpy.app.version >= (3, 4, 0):
124139
# Set default values for hidden sockets
125140
self._set_unavailable_defaults = False
@@ -154,6 +169,8 @@ def _setup_options(self, options: NTP_PG_Options) -> bool:
154169
elif options.indentation_type == 'TABS':
155170
self._indentation = "\t"
156171

172+
self._link_external_node_groups = options.link_external_node_groups
173+
157174
if bpy.app.version >= (3, 4, 0):
158175
self._set_unavailable_defaults = options.set_unavailable_defaults
159176

@@ -204,7 +221,7 @@ def _setup_addon_directories(self, context: Context, obj_var: str) -> bool:
204221

205222
return True
206223

207-
def _create_header(self, name: str) -> None:
224+
def _create_bl_info(self, name: str) -> None:
208225
"""
209226
Sets up the bl_info and imports the Blender API
210227
@@ -228,9 +245,12 @@ def _create_header(self, name: str) -> None:
228245
category = self._custom_category
229246
self._write(f"\"category\" : {str_to_py_str(category)},", 1)
230247
self._write("}\n", 0)
248+
249+
def _create_imports(self) -> None:
231250
self._write("import bpy", 0)
232251
self._write("import mathutils", 0)
233-
self._write("import os\n", 0)
252+
self._write("import os", 0)
253+
self._write("\n", 0)
234254

235255
def _init_operator(self, idname: str, label: str) -> None:
236256
"""
@@ -282,18 +302,68 @@ def dfs(nt: NodeTree) -> None:
282302
self.report({'ERROR'}, "NodeToPython: Found an invalid node tree. "
283303
"Are all data blocks valid?")
284304
return
305+
306+
if self._link_external_node_groups and nt.library is not None:
307+
bpy_lib_path = bpy.path.abspath(nt.library.filepath)
308+
lib_path = pathlib.Path(os.path.realpath(bpy_lib_path))
309+
bpy_datafiles_path = bpy.path.abspath(
310+
bpy.utils.system_resource('DATAFILES')
311+
)
312+
datafiles_path = pathlib.Path(os.path.realpath(bpy_datafiles_path))
313+
is_lib_essential = lib_path.is_relative_to(datafiles_path)
314+
315+
if is_lib_essential:
316+
relative_path = lib_path.relative_to(datafiles_path)
317+
if relative_path not in self._lib_trees:
318+
self._lib_trees[relative_path] = []
319+
self._lib_trees[relative_path].append(nt)
320+
return
321+
else:
322+
print(f"Library {lib_path} didn't seem essential, copying node groups")
323+
285324
if nt not in visited:
286325
visited.add(nt)
287326
for group_node in [node for node in nt.nodes
288327
if node.bl_idname == group_node_type]:
289328
if group_node.node_tree not in visited:
329+
if group_node.node_tree is None:
330+
self.report(
331+
{'ERROR'},
332+
"NodeToPython: Found an invalid node tree. "
333+
"Are all data blocks valid?"
334+
)
290335
dfs(group_node.node_tree)
291336
result.append(nt)
292337

293338
dfs(node_tree)
294339

295340
return result
296341

342+
def _import_essential_libs(self) -> None:
343+
self._inner_indent_level -= 1
344+
self._write("# Import node groups from Blender essentials library")
345+
self._write(f"{DATAFILES_PATH} = bpy.utils.system_resource('DATAFILES')")
346+
for path, node_trees in self._lib_trees.items():
347+
self._write(f"{LIB_RELPATH} = {str_to_py_str(str(path))}")
348+
self._write(f"{LIB_PATH} = os.path.join({DATAFILES_PATH}, {LIB_RELPATH})")
349+
self._write(f"with bpy.data.libraries.load({LIB_PATH}, link=True) "
350+
f" as ({DATA_SRC}, {DATA_DST}):")
351+
self._write(f"\t{DATA_DST}.node_groups = []")
352+
for node_tree in node_trees:
353+
name_str = str_to_py_str(node_tree.name)
354+
self._write(f"\tif {name_str} in {DATA_SRC}.node_groups:")
355+
self._write(f"\t\t{DATA_DST}.node_groups.append({name_str})")
356+
# TODO: handle bad case with warning (in both script and addon mode)
357+
358+
for i, node_tree in enumerate(node_trees):
359+
nt_var = self._create_var(node_tree.name)
360+
self._node_tree_vars[node_tree] = nt_var
361+
self._write(f"{nt_var} = {DATA_DST}.node_groups[{i}]")
362+
self._write("\n")
363+
self._inner_indent_level += 1
364+
365+
366+
297367
def _create_var(self, name: str) -> str:
298368
"""
299369
Creates a unique variable name for a node tree
@@ -1208,13 +1278,17 @@ def _node_tree_settings(self, node: Node, attr_name: str) -> None:
12081278
node_tree = getattr(node, attr_name)
12091279
if node_tree is None:
12101280
return
1281+
12111282
if node_tree in self._node_tree_vars:
12121283
nt_var = self._node_tree_vars[node_tree]
12131284
node_var = self._node_vars[node]
12141285
self._write(f"{node_var}.{attr_name} = {nt_var}")
12151286
else:
1216-
self.report({'WARNING'}, (f"NodeToPython: Node tree dependency graph "
1217-
f"wasn't properly initialized"))
1287+
self.report(
1288+
{'WARNING'},
1289+
f"NodeToPython: Node tree dependency graph "
1290+
f"wasn't properly initialized! Couldn't find "
1291+
f"node tree {node_tree.name}")
12181292

12191293
def _save_image(self, img: bpy.types.Image) -> bool:
12201294
"""

NodeToPython/ntp_options.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,12 @@ def __init__(self, *args, **kwargs):
4747
default = 'SPACES_4'
4848
)
4949

50+
link_external_node_groups : bpy.props.BoolProperty(
51+
name = "Link External Node Groups",
52+
description="Simply tries to link external node groups, otherwise copies",
53+
default = True
54+
)
55+
5056
if bpy.app.version >= (3, 4, 0):
5157
set_unavailable_defaults : bpy.props.BoolProperty(
5258
name = "Set unavailable defaults",

NodeToPython/shader/operator.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,16 @@ def execute(self, context):
269269

270270
self._file = open(f"{self._addon_dir}/__init__.py", "w")
271271

272-
self._create_header(self.name)
272+
self._create_bl_info(self.name)
273+
self._create_imports()
273274
self._class_name = clean_string(self.name, lower=False)
274275
self._init_operator(self.obj_var, self.name)
275276

276277
self._write("def execute(self, context):", 1)
277278
else:
278279
self._file = StringIO("")
279280
if self._include_imports:
280-
self._file.write("import bpy\nimport mathutils\n\n\n")
281+
self._create_imports()
281282

282283
if self.group_type == 'MATERIAL':
283284
self._create_material()
@@ -290,6 +291,8 @@ def execute(self, context):
290291

291292
node_trees_to_process = self._topological_sort(self._base_node_tree)
292293

294+
self._import_essential_libs()
295+
293296
for node_tree in node_trees_to_process:
294297
self._process_node_tree(node_tree)
295298

NodeToPython/ui/generation_settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ def draw(self, context):
2727
generation_options = [
2828
"set_group_defaults",
2929
"set_node_sizes",
30-
"indentation_type"
30+
"indentation_type",
31+
"link_external_node_groups"
3132
]
3233
if bpy.app.version >= (3, 4, 0):
3334
generation_options.append("set_unavailable_defaults")

0 commit comments

Comments
 (0)