Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions NodeToPython/compositor/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,15 +254,16 @@ def execute(self, context):

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

self._create_header(self.compositor_name)
self._create_bl_info(self.compositor_name)
self._create_imports()
self._class_name = clean_string(self.compositor_name, lower=False)
self._init_operator(comp_var, self.compositor_name)

self._write("def execute(self, context):", 1)
else:
self._file = StringIO("")
if self._include_imports:
self._file.write("import bpy\nimport mathutils\n\n\n")
self._create_imports()

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

node_trees_to_process = self._topological_sort(self._base_node_tree)

self._import_essential_libs()

for node_tree in node_trees_to_process:
self._process_node_tree(node_tree)

Expand Down
9 changes: 6 additions & 3 deletions NodeToPython/geometry/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def _set_geo_tree_properties(self, node_tree: GeometryNodeTree) -> None:

if bpy.app.version >= (5, 0, 0):
if node_tree.show_modifier_manage_panel:
self._write(f"{nt_var}.show_modifier_manager_panel = True")
self._write(f"{nt_var}.show_modifier_manage_panel = True")
self._write("", 0)

def _process_node_tree(self, node_tree: GeometryNodeTree) -> None:
Expand Down Expand Up @@ -196,18 +196,21 @@ def execute(self, context):

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

self._create_header(nt.name)
self._create_bl_info(nt.name)
self._create_imports()
self._class_name = clean_string(nt.name, lower = False)
self._init_operator(nt_var, nt.name)
self._write("def execute(self, context):", 1)
else:
self._file = StringIO("")
if self._include_imports:
self._file.write("import bpy\nimport mathutils\n\n\n")
self._create_imports()


node_trees_to_process = self._topological_sort(nt)

self._import_essential_libs()

for node_tree in node_trees_to_process:
self._process_node_tree(node_tree)

Expand Down
94 changes: 84 additions & 10 deletions NodeToPython/ntp_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import datetime
import os
import pathlib
import shutil
from typing import TextIO, Callable

Expand All @@ -26,14 +27,24 @@
IMAGE_PATH = "image_path"
ITEM = "item"
BASE_DIR = "base_dir"
DATAFILES_PATH = "datafiles_path"
LIB_RELPATH = "lib_relpath"
LIB_PATH = "lib_path"
DATA_SRC = "data_src"
DATA_DST = "data_dst"

RESERVED_NAMES = {
INDEX,
IMAGE_DIR_NAME,
IMAGE_PATH,
ITEM,
BASE_DIR
}
INDEX,
IMAGE_DIR_NAME,
IMAGE_PATH,
ITEM,
BASE_DIR,
DATAFILES_PATH,
LIB_RELPATH,
LIB_PATH,
DATA_SRC,
DATA_DST
}

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

self._link_external_node_groups = True

self._lib_trees: dict[pathlib.Path, list[bpy.types.NodeTree]] = {}

if bpy.app.version >= (3, 4, 0):
# Set default values for hidden sockets
self._set_unavailable_defaults = False
Expand Down Expand Up @@ -154,6 +169,8 @@ def _setup_options(self, options: NTP_PG_Options) -> bool:
elif options.indentation_type == 'TABS':
self._indentation = "\t"

self._link_external_node_groups = options.link_external_node_groups

if bpy.app.version >= (3, 4, 0):
self._set_unavailable_defaults = options.set_unavailable_defaults

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

return True

def _create_header(self, name: str) -> None:
def _create_bl_info(self, name: str) -> None:
"""
Sets up the bl_info and imports the Blender API

Expand All @@ -228,9 +245,12 @@ def _create_header(self, name: str) -> None:
category = self._custom_category
self._write(f"\"category\" : {str_to_py_str(category)},", 1)
self._write("}\n", 0)

def _create_imports(self) -> None:
self._write("import bpy", 0)
self._write("import mathutils", 0)
self._write("import os\n", 0)
self._write("import os", 0)
self._write("\n", 0)

def _init_operator(self, idname: str, label: str) -> None:
"""
Expand Down Expand Up @@ -282,18 +302,68 @@ def dfs(nt: NodeTree) -> None:
self.report({'ERROR'}, "NodeToPython: Found an invalid node tree. "
"Are all data blocks valid?")
return

if self._link_external_node_groups and nt.library is not None:
bpy_lib_path = bpy.path.abspath(nt.library.filepath)
lib_path = pathlib.Path(os.path.realpath(bpy_lib_path))
bpy_datafiles_path = bpy.path.abspath(
bpy.utils.system_resource('DATAFILES')
)
datafiles_path = pathlib.Path(os.path.realpath(bpy_datafiles_path))
is_lib_essential = lib_path.is_relative_to(datafiles_path)

if is_lib_essential:
relative_path = lib_path.relative_to(datafiles_path)
if relative_path not in self._lib_trees:
self._lib_trees[relative_path] = []
self._lib_trees[relative_path].append(nt)
return
else:
print(f"Library {lib_path} didn't seem essential, copying node groups")

if nt not in visited:
visited.add(nt)
for group_node in [node for node in nt.nodes
if node.bl_idname == group_node_type]:
if group_node.node_tree not in visited:
if group_node.node_tree is None:
self.report(
{'ERROR'},
"NodeToPython: Found an invalid node tree. "
"Are all data blocks valid?"
)
dfs(group_node.node_tree)
result.append(nt)

dfs(node_tree)

return result

def _import_essential_libs(self) -> None:
self._inner_indent_level -= 1
self._write("# Import node groups from Blender essentials library")
self._write(f"{DATAFILES_PATH} = bpy.utils.system_resource('DATAFILES')")
for path, node_trees in self._lib_trees.items():
self._write(f"{LIB_RELPATH} = {str_to_py_str(str(path))}")
self._write(f"{LIB_PATH} = os.path.join({DATAFILES_PATH}, {LIB_RELPATH})")
self._write(f"with bpy.data.libraries.load({LIB_PATH}, link=True) "
f" as ({DATA_SRC}, {DATA_DST}):")
self._write(f"\t{DATA_DST}.node_groups = []")
for node_tree in node_trees:
name_str = str_to_py_str(node_tree.name)
self._write(f"\tif {name_str} in {DATA_SRC}.node_groups:")
self._write(f"\t\t{DATA_DST}.node_groups.append({name_str})")
# TODO: handle bad case with warning (in both script and addon mode)

for i, node_tree in enumerate(node_trees):
nt_var = self._create_var(node_tree.name)
self._node_tree_vars[node_tree] = nt_var
self._write(f"{nt_var} = {DATA_DST}.node_groups[{i}]")
self._write("\n")
self._inner_indent_level += 1



def _create_var(self, name: str) -> str:
"""
Creates a unique variable name for a node tree
Expand Down Expand Up @@ -1208,13 +1278,17 @@ def _node_tree_settings(self, node: Node, attr_name: str) -> None:
node_tree = getattr(node, attr_name)
if node_tree is None:
return

if node_tree in self._node_tree_vars:
nt_var = self._node_tree_vars[node_tree]
node_var = self._node_vars[node]
self._write(f"{node_var}.{attr_name} = {nt_var}")
else:
self.report({'WARNING'}, (f"NodeToPython: Node tree dependency graph "
f"wasn't properly initialized"))
self.report(
{'WARNING'},
f"NodeToPython: Node tree dependency graph "
f"wasn't properly initialized! Couldn't find "
f"node tree {node_tree.name}")

def _save_image(self, img: bpy.types.Image) -> bool:
"""
Expand Down
6 changes: 6 additions & 0 deletions NodeToPython/ntp_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ def __init__(self, *args, **kwargs):
default = 'SPACES_4'
)

link_external_node_groups : bpy.props.BoolProperty(
name = "Link External Node Groups",
description="Simply tries to link external node groups, otherwise copies",
default = True
)

if bpy.app.version >= (3, 4, 0):
set_unavailable_defaults : bpy.props.BoolProperty(
name = "Set unavailable defaults",
Expand Down
7 changes: 5 additions & 2 deletions NodeToPython/shader/operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,15 +269,16 @@ def execute(self, context):

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

self._create_header(self.name)
self._create_bl_info(self.name)
self._create_imports()
self._class_name = clean_string(self.name, lower=False)
self._init_operator(self.obj_var, self.name)

self._write("def execute(self, context):", 1)
else:
self._file = StringIO("")
if self._include_imports:
self._file.write("import bpy\nimport mathutils\n\n\n")
self._create_imports()

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

node_trees_to_process = self._topological_sort(self._base_node_tree)

self._import_essential_libs()

for node_tree in node_trees_to_process:
self._process_node_tree(node_tree)

Expand Down
3 changes: 2 additions & 1 deletion NodeToPython/ui/generation_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ def draw(self, context):
generation_options = [
"set_group_defaults",
"set_node_sizes",
"indentation_type"
"indentation_type",
"link_external_node_groups"
]
if bpy.app.version >= (3, 4, 0):
generation_options.append("set_unavailable_defaults")
Expand Down