node_to_json 0.1.1__tar.gz

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,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: node_to_json
3
+ Version: 0.1.1
4
+ Summary: Blender 5.2 Serialize Node Groups to JSON files.Load Node Groups from JSON files.
5
+ Author: Demingo Hill (Noizirom) (C)
6
+ License-Expression: GPL-3.0-only
7
+ Requires-Python: >=3.13
8
+ Description-Content-Type: text/markdown
9
+
10
+ # Node To JSON
11
+
12
+ ## Convert Blender 5.2 Nodes to JSON files. Convert JSON objects to Blender Nodes.
13
+ ### Created to work with Blender Addons and Extensions to serialize nodes. It is only intended to work within Blender's python interpreter and requires the bpy module.
@@ -0,0 +1,4 @@
1
+ # Node To JSON
2
+
3
+ ## Convert Blender 5.2 Nodes to JSON files. Convert JSON objects to Blender Nodes.
4
+ ### Created to work with Blender Addons and Extensions to serialize nodes. It is only intended to work within Blender's python interpreter and requires the bpy module.
@@ -0,0 +1,18 @@
1
+ [project]
2
+ name = "node_to_json"
3
+ version = "0.1.1"
4
+ description = "Blender 5.2 Serialize Node Groups to JSON files.Load Node Groups from JSON files."
5
+ readme = "README.md"
6
+ requires-python = ">=3.13"
7
+ license = "GPL-3.0-only"
8
+ dependencies = []
9
+
10
+ [[project.authors]]
11
+ name = "Demingo Hill (Noizirom) (C)"
12
+
13
+ [build-system]
14
+ requires = ["uv_build>=0.12.5,<0.13"]
15
+ build-backend = "uv_build"
16
+
17
+ [tool.uv.build-backend]
18
+ module-name = "node_to_json"
@@ -0,0 +1,18 @@
1
+ [project]
2
+ name = "node_to_json"
3
+ version = "0.1.1"
4
+ description = "Blender 5.2 Serialize Node Groups to JSON files.Load Node Groups from JSON files."
5
+ readme = "README.md"
6
+ authors = [
7
+ {name = "Demingo Hill (Noizirom) (C)"}
8
+ ]
9
+ requires-python = ">=3.13"
10
+ license = "GPL-3.0-only"
11
+ dependencies = []
12
+
13
+ [build-system]
14
+ requires = ["uv_build>=0.12.5,<0.13"]
15
+ build-backend = "uv_build"
16
+
17
+ [tool.uv.build-backend]
18
+ module-name = "node_to_json"
@@ -0,0 +1,53 @@
1
+ """
2
+ Blender 5.2
3
+ Serialize Node Groups to JSON files.
4
+ Load Node Groups from JSON files.
5
+ Author: Demingo Hill (Noizirom) (C)
6
+ """
7
+ from .asset_funcs import get_asset_nodes, asset_node_group, nodes_dir
8
+ from .func_util import nested_dict, convert_attr, convert_default_value, get_node_group_data_type, get_node_groups_by_type, get_reversed_node_groups_by_type
9
+ from .json_io import dict_to_json, json_to_dict
10
+ from .node_getters import serialize_node_group, serialize_mat_group, serialize_material, serialize_comp_group, serialize_compositor, get_node_build_data, get_node_group_interface, get_node_group_names
11
+ from .node_registry import bpy_types_funcs, register_node_setter, ng_getter_funcs, register_ng_getter, ng_setter_funcs, register_ng_setter
12
+ from .node_setters import create_node_group_from_data, add_node_group, create_shader_group_from_data, create_material, add_material_to_ob, create_comp_group, add_comp_group, create_texture_group_from_data, process_node, set_interface
13
+
14
+ __all__ = [
15
+ "get_asset_nodes",
16
+ "asset_node_group",
17
+ "nodes_dir",
18
+ "nested_dict",
19
+ "convert_attr",
20
+ "convert_default_value",
21
+ "get_node_group_data_type",
22
+ "get_node_groups_by_type",
23
+ "get_reversed_node_groups_by_type",
24
+ "dict_to_json",
25
+ "json_to_dict",
26
+ "serialize_node_group",
27
+ "serialize_mat_group",
28
+ "serialize_material",
29
+ "serialize_comp_group",
30
+ "serialize_compositor",
31
+ "get_node_build_data",
32
+ "get_node_group_interface",
33
+ "get_node_group_names",
34
+ "bpy_types_funcs",
35
+ "register_node_setter",
36
+ "ng_getter_funcs",
37
+ "register_ng_getter",
38
+ "ng_setter_funcs",
39
+ "register_ng_setter",
40
+ "create_node_group_from_data",
41
+ "add_node_group",
42
+ "create_shader_group_from_data",
43
+ "create_material",
44
+ "add_material_to_ob",
45
+ "create_comp_group",
46
+ "add_comp_group",
47
+ "create_texture_group_from_data",
48
+ "process_node",
49
+ "set_interface",
50
+ ]
51
+
52
+ def __dir__():
53
+ return __all__
@@ -0,0 +1,74 @@
1
+ import bpy
2
+ from pathlib import Path
3
+ from typing import Generator
4
+ from .type_util import BGroup
5
+
6
+ datafiles_path = Path(bpy.utils.system_resource('DATAFILES'))
7
+ lib_relpath = Path("assets").joinpath("nodes")
8
+ nodes_dir = datafiles_path.joinpath(lib_relpath)
9
+ hair_nodes = nodes_dir.joinpath("procedural_hair_node_assets.blend")
10
+ comp_nodes = nodes_dir.joinpath("compositing_nodes_essentials.blend")
11
+ dyn_nodes = nodes_dir.joinpath("geometry_nodes_dynamics_assets.blend")
12
+ geom_nodes = nodes_dir.joinpath("geometry_nodes_essentials.blend")
13
+ principal_nodes = nodes_dir.joinpath("principal_components.blend")
14
+ shading_nodes = nodes_dir.joinpath("shading_nodes_essentials.blend")
15
+
16
+
17
+ def get_asset_nodes_helper(file: str) -> Generator[str, None, None]:
18
+ with bpy.data.libraries.load(str(file), link=True) as (data_src, data_dst):
19
+ groups = (g for g in data_src.node_groups)
20
+ return groups
21
+
22
+
23
+ def get_asset_nodes() -> Generator[str, None, None]:
24
+ for file in nodes_dir.rglob('*blend'):
25
+ yield from get_asset_nodes_helper(file)
26
+
27
+
28
+
29
+ def get_asset_file(name: str) -> str | None:
30
+ for file in nodes_dir.rglob('*blend'):
31
+ if name in get_asset_nodes_helper(file):
32
+ return file
33
+ return
34
+
35
+
36
+ def append_asset_nodes(*node_groups: str) -> None:
37
+ for group in node_groups:
38
+ name = group.split(".")[0]
39
+ file = get_asset_file(name)
40
+ if file:
41
+ with bpy.data.libraries.load(str(file), link=False) as (data_src, data_dst):
42
+ data_dst.node_groups = [name]
43
+ return
44
+
45
+
46
+ def get_loaded_node_groups() -> Generator[BGroup, None, None]:
47
+ return (group for group in bpy.data.node_groups)
48
+
49
+
50
+ def get_loaded_node_group_names() -> list[str]:
51
+ return [group.name for group in get_loaded_node_groups()]
52
+
53
+
54
+ def load_asset_node(node_group: str) -> Generator[BGroup, None, None] | None:
55
+ asset_nodes = set(get_asset_nodes())
56
+ loaded = set(get_loaded_node_groups())
57
+ if node_group.split(".")[0] in asset_nodes:
58
+ append_asset_nodes(node_group.split(".")[0])
59
+ new_ = get_loaded_node_groups()
60
+ return (n for n in new_ if n not in loaded)
61
+ return
62
+
63
+
64
+ def asset_node_group(node_group: str) -> BGroup | None:
65
+ """Add an asset node group or return None.
66
+
67
+ :param node_group: Node group name to load.
68
+ :type node_group: str
69
+ :return: Return node group if it is an asset node else return None.
70
+ :rtype: Node Group | None"""
71
+ anode = load_asset_node(node_group)
72
+ if anode not in [None, []]:
73
+ return next((g for g in anode if g.name.split(".")[0] == node_group.split(".")[0]))
74
+ return
@@ -0,0 +1,90 @@
1
+ import bpy
2
+ from mathutils import Color, Euler, Vector, Matrix, Quaternion
3
+ from idprop.types import IDPropertyArray
4
+ from collections import defaultdict
5
+ from .type_util import PYObject, PYDict, BGroup, BNode
6
+ from operator import itemgetter
7
+ from typing import Generator
8
+
9
+
10
+ def nested_dict() -> defaultdict:
11
+ """Create a nested dict of dicts.
12
+
13
+ :return: dict[str, dict]
14
+ :rtype: defaultdict"""
15
+
16
+ return defaultdict(nested_dict)
17
+
18
+
19
+ def convert_attr(val: PYObject | list[PYObject]) -> PYObject | list[PYObject]:
20
+ """Convert a value based on its bpy type.
21
+
22
+ :param val: Value to modify if it is not serializable.
23
+ :type val: Any
24
+ :return: Modified value if it is not serializable, else the original value.
25
+ :rtype: Any"""
26
+
27
+ if isinstance(val, (Color, Vector, Euler, Quaternion, bpy.types.bpy_prop_array, IDPropertyArray, set, tuple)):
28
+ return list(val)
29
+ if isinstance(val, (Matrix,)):
30
+ return [list(v) for v in val]
31
+ if isinstance(val, (bpy.types.Object, bpy.types.Material, bpy.types.Image, bpy.types.ImageTexture, bpy.types.Texture, bpy.types.Collection, bpy.types.Scene, bpy.types.Sound, bpy.types.MovieClip, bpy.types.AnimData, bpy.types.Action, bpy.types.Annotation, bpy.types.Mask)):
32
+ return None
33
+ if isinstance(val, (bpy.types.Node, bpy.types.GeometryNodeTree, bpy.types.ShaderNodeTree, bpy.types.CompositorNodeTree)):
34
+ return val.name
35
+ if isinstance(val, (bpy.types.NodeTreeInterfaceItem, bpy.types.NodeTreeInterfacePanel)):
36
+ if hasattr(val, 'persistent_uid'):
37
+ return [val.name, val.persistent_uid]
38
+ return val.name
39
+ return val
40
+
41
+
42
+ def convert_default_value(_io: BGroup | BNode, val: PYObject) -> PYObject:
43
+ """Convert a value to a suitable value.
44
+
45
+ :param _io: An object. typically an input / output socket.
46
+ :type _io: NodeSocket
47
+ :param val: Default value to set socket to.
48
+ :type val: Any
49
+ :return: The modified value if it meets the condition, else it returns the original value.
50
+ :rtype: Any"""
51
+ if getattr(_io, 'type', None) == 'INT':
52
+ val = int(val)
53
+ if getattr(_io, 'type', None) == 'BOOLEAN':
54
+ val = bool(val)
55
+ return val
56
+
57
+
58
+ def get_node_group_data_type(data: PYDict) -> str | None:
59
+ """Get Node Group type from data dict.
60
+
61
+ :param data: Dict with node group build data.
62
+ :type data: dict[str, Any]
63
+ :return: Node Group type of data.
64
+ :rtype: str | None"""
65
+ get_type = itemgetter('bl_idname')
66
+ gtype = next(get_type(value) for key, value in data.items())
67
+ for t in ["GeometryNodeTree", "ShaderNodeTree", "CompositorNodeTree"]:
68
+ if t == gtype:
69
+ return t
70
+ return
71
+
72
+
73
+ def get_node_groups_by_type(ng_type: str) -> Generator[bpy.types.NodeGroup, None, None]:
74
+ """Get a generator of all node groups by type.
75
+
76
+ :param ng_type: Type of node groups to return.
77
+ :type ng_type: str
78
+ :return: Generator of node groups by type if they exist.
79
+ :rtype: Generator[NodeGroup, None, None]"""
80
+ return (ng for ng in bpy.data.node_groups if ng.bl_idname == ng_type)
81
+
82
+
83
+ def get_reversed_node_groups_by_type(ng_type: str) -> Generator[bpy.types.NodeGroup, None, None]:
84
+ """Get a reversed generator of all node groups by type.
85
+
86
+ :param ng_type: Type of node groups to return.
87
+ :type ng_type: str
88
+ :return: Generator of node groups by type in reverse if they exist.
89
+ :rtype: Generator[NodeGroup, None, None]"""
90
+ return (ng for ng in reversed(bpy.data.node_groups[:]) if ng.bl_idname == ng_type)
@@ -0,0 +1,31 @@
1
+ from json import dump, load
2
+ from .type_util import PYDict
3
+
4
+
5
+ def dict_to_json(file: str, data: PYDict, indent: int | None = None) -> None:
6
+ """Convert python dict to json object.
7
+
8
+ :param file: File path of file to save to.
9
+ :type file: str
10
+ :param data: Dict of data to convert.
11
+ :type data: dict[str, Any]
12
+ :param indent: How many spaces to indent json object. (None = no indent.)
13
+ :type indent: int | None"""
14
+ with open(file, 'w') as f:
15
+ if indent:
16
+ dump(data, f, indent=indent)
17
+ else:
18
+ dump(data, f)
19
+
20
+
21
+ def json_to_dict(file: str) -> PYDict:
22
+ """Convert json object to python dict.
23
+
24
+ :param file: File path to load from.
25
+ :type file: str
26
+ :return: Dict of converted json object
27
+ :rtype: dict[str, Any]"""
28
+ with open(file, 'r') as f:
29
+ data = load(f)
30
+ return data
31
+