Compare commits
	
		
			No commits in common. "90aa72a767cf44d5f491e02b285acfda27a0ad82" and "94627debc6580513136b18bffd327ed41511a8d1" have entirely different histories.
		
	
	
		
			90aa72a767
			...
			94627debc6
		
	
		
							
								
								
									
										0
									
								
								core/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								core/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										211
									
								
								core/_node.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								core/_node.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,211 @@ | ||||
| import bpy | ||||
| from mathutils import Color, Vector | ||||
| 
 | ||||
| from .sockets import Input, Output | ||||
| 
 | ||||
| 
 | ||||
| class Node: | ||||
|     """Blender Node abstraction.""" | ||||
| 
 | ||||
|     def __init__(self, bl_node, parent): | ||||
|         self.bl_node = bl_node | ||||
|         self.tree = parent | ||||
|         self.id = hex(id(self.bl_node)) | ||||
| 
 | ||||
|         self.data = {} | ||||
|         self.parameters = [] | ||||
| 
 | ||||
|         self._parent = None | ||||
|         self._scene = None | ||||
| 
 | ||||
|         for prop in self.bl_node.bl_rna.properties: | ||||
|             if prop.is_readonly: | ||||
|                 continue | ||||
| 
 | ||||
|             prop_id = prop.identifier | ||||
| 
 | ||||
|             setattr(self, prop_id, getattr(self.bl_node, prop_id)) | ||||
|             self.parameters.append(prop_id) | ||||
| 
 | ||||
|         self.inputs = [Input(ipt, self.tree) for ipt in self.bl_node.inputs] | ||||
|         self.outputs = [Output(opt, self.tree) for opt in self.bl_node.outputs] | ||||
| 
 | ||||
|     @property | ||||
|     def parent(self): | ||||
|         """Get the Node from all the other nodes in the tree checking that the | ||||
|         parent of its blender node is the same as the blender node we are comparing. | ||||
| 
 | ||||
|         Returns: | ||||
|             Node: Node parent. | ||||
|         """ | ||||
| 
 | ||||
|         if self._parent: | ||||
|             return self._parent | ||||
| 
 | ||||
|         # if blender node doesn't have a parent | ||||
|         if not self.bl_node.parent: | ||||
|             self._parent = None | ||||
|             return self._parent | ||||
| 
 | ||||
|         for node in self.tree.nodes: | ||||
|             if node.bl_node == self.bl_node.parent: | ||||
|                 self._parent = node | ||||
|                 return self._parent | ||||
| 
 | ||||
|     @parent.setter | ||||
|     def parent(self, value): | ||||
|         """Set the Node parent, using the python object, it's id or the blender node. | ||||
| 
 | ||||
|         Args: | ||||
|             value (Node|str|bpy.types.Node): Node, id or blender node to set as parent. | ||||
|         """ | ||||
| 
 | ||||
|         # Node object case | ||||
|         if isinstance(value, Node): | ||||
|             self._parent = value | ||||
| 
 | ||||
|         # Node id case | ||||
|         elif isinstance(value, str) and value.startswith("0x"): | ||||
|             for node in self.tree.nodes: | ||||
|                 if node.id == value: | ||||
|                     self._parent = node | ||||
|             else: | ||||
|                 print("Cannot find parent") | ||||
| 
 | ||||
|         # blender node case | ||||
|         elif isinstance(value, bpy.types.Node): | ||||
|             for node in self.tree.nodes: | ||||
|                 if node.bl_node == value: | ||||
|                     self._parent = node | ||||
| 
 | ||||
|         if self._parent: | ||||
|             self.bl_node.parent = self._parent.bl_node | ||||
| 
 | ||||
|     @classmethod | ||||
|     def from_blender_node(cls, bl_node, tree): | ||||
|         """Instanciate an abstract class based of the blender node idname. | ||||
| 
 | ||||
|         Args: | ||||
|             bl_node (bpy.types.Node): Blender Node To create abstraction from. | ||||
|             tree (NodeTree): Node tree object node belongs to. | ||||
| 
 | ||||
|         Returns: | ||||
|             Node: Node abstract according to the blender node type. | ||||
|         """ | ||||
|         if bl_node.bl_idname == "CompositorNodeRLayers": | ||||
|             return RenderLayersNode(bl_node, tree) | ||||
| 
 | ||||
|         elif bl_node.bl_idname == "CompositorNodeValToRGB": | ||||
|             return ColorRampNode(bl_node, tree) | ||||
| 
 | ||||
|         else: | ||||
|             return cls(bl_node, tree) | ||||
| 
 | ||||
|     @classmethod | ||||
|     def from_dict(cls, data, tree): | ||||
|         """Create all nodes from their dict representation. | ||||
| 
 | ||||
|         Args: | ||||
|             data (dict): dict nodes representation. | ||||
|             tree (Tree): blender node tree abstraction. | ||||
| 
 | ||||
|         Returns: | ||||
|             Node: Create abstract node. | ||||
|         """ | ||||
| 
 | ||||
|         new_bl_node = tree.bl_node_tree.nodes.new(type=data["bl_idname"]) | ||||
|         node = cls.from_blender_node(new_bl_node, tree) | ||||
| 
 | ||||
|         node.id = data["id"] | ||||
|         for p in node.parameters: | ||||
|             setattr(node, p, data[p]) | ||||
| 
 | ||||
|             # set attribute on the blender node only if correct type is retrieve | ||||
|             if p not in ("parent", "scene"): | ||||
|                 setattr(node.bl_node, p, getattr(node, p)) | ||||
| 
 | ||||
|         node.inputs = [ | ||||
|             Input.from_dict(ipt_data, node) for ipt_data in data["inputs"].values() | ||||
|         ] | ||||
|         node.outputs = [ | ||||
|             Output.from_dict(opt_data, node) for opt_data in data["outputs"].values() | ||||
|         ] | ||||
|         return node | ||||
| 
 | ||||
|     def dump(self): | ||||
|         """Export currrent Node to its dict representation. | ||||
| 
 | ||||
|         Returns: | ||||
|             dict: Node dict representation. | ||||
|         """ | ||||
| 
 | ||||
|         for prop_id in self.parameters: | ||||
|             if not hasattr(self, prop_id): | ||||
|                 continue | ||||
| 
 | ||||
|             attr_value = getattr(self, prop_id) | ||||
|             if attr_value is None: | ||||
|                 attr_value = None | ||||
| 
 | ||||
|             elif isinstance(attr_value, Node): | ||||
|                 attr_value = attr_value.id | ||||
| 
 | ||||
|             elif isinstance(attr_value, (Color, Vector)): | ||||
|                 attr_value = list(attr_value) | ||||
| 
 | ||||
|             self.data[prop_id] = attr_value | ||||
| 
 | ||||
|         self.data["id"] = self.id | ||||
|         self.data["inputs"] = {ipt.id: ipt.dump() for ipt in self.inputs} | ||||
|         self.data["outputs"] = {opt.id: opt.dump() for opt in self.outputs} | ||||
| 
 | ||||
|         return self.data | ||||
| 
 | ||||
| 
 | ||||
| class RenderLayersNode(Node): | ||||
|     """Blender Render Layers Node abstraction""" | ||||
| 
 | ||||
|     @property | ||||
|     def scene(self): | ||||
|         """Get the name of the scene used by the node. | ||||
| 
 | ||||
|         Returns: | ||||
|             str: scene name. | ||||
|         """ | ||||
|         if self._scene: | ||||
|             return self._scene.name | ||||
| 
 | ||||
|     @scene.setter | ||||
|     def scene(self, value): | ||||
|         """Set the blender scene using the bpy Scene object or its name. | ||||
| 
 | ||||
|         Args: | ||||
|             value (str|bpy.types.Scene): scene name or scene object to set the node. | ||||
|         """ | ||||
|         if isinstance(value, str): | ||||
|             self._scene = bpy.data.scenes[value] | ||||
| 
 | ||||
|         elif isinstance(value, bpy.types.Scene): | ||||
|             self._scene = value | ||||
| 
 | ||||
|         if self._scene: | ||||
|             self.bl_node.scene = self._scene | ||||
| 
 | ||||
| 
 | ||||
| class Link: | ||||
|     """Blender Link abstraction.""" | ||||
| 
 | ||||
|     def __init__(self, bl_link, parent): | ||||
|         self.bl_link = bl_link | ||||
|         self.tree = parent | ||||
|         self.id = hex(id(self.bl_link)) | ||||
| 
 | ||||
|         self.input = self.bl_link.to_socket | ||||
|         self.output = self.bl_link.from_socket | ||||
| 
 | ||||
|         self.data = {} | ||||
| 
 | ||||
|     def dump(self): | ||||
|         self.data["id"] = self.id | ||||
| 
 | ||||
|         return self.data | ||||
							
								
								
									
										71
									
								
								core/_node_tree.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								core/_node_tree.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,71 @@ | ||||
| import json | ||||
| from pathlib import Path | ||||
| 
 | ||||
| from .node import Node, Link | ||||
| 
 | ||||
| 
 | ||||
| class NodeTree: | ||||
|     """Blender node tree abstraction.""" | ||||
| 
 | ||||
|     def __init__(self, bl_node_tree): | ||||
|         self.bl_node_tree = bl_node_tree | ||||
| 
 | ||||
|         self.data = {} | ||||
| 
 | ||||
|         self.links = [Link(lnk, parent=self) for lnk in self.bl_node_tree.links] | ||||
|         self.nodes = [] | ||||
|         for n in self.bl_node_tree.nodes: | ||||
|             self.nodes.append(Node.from_blender_node(n, self)) | ||||
| 
 | ||||
|     def dump(self, select_only=False): | ||||
|         """Convert all blender nodes and links inside the tree into a dictionnary. | ||||
| 
 | ||||
|         Args: | ||||
|             select_only (bool, optional): True to convert only selected nodes. | ||||
|                 Defaults to False. | ||||
| 
 | ||||
|         Returns: | ||||
|             dict: Nodes and links as dict. | ||||
|         """ | ||||
|         self.data["nodes"] = { | ||||
|             n.id: n.dump() | ||||
|             for n in self.nodes | ||||
|             if not select_only or (select_only and n.select) | ||||
|         } | ||||
|         self.data["links"] = [l.id for l in self.links] | ||||
| 
 | ||||
|         return self.data | ||||
| 
 | ||||
|     def load(self, data): | ||||
|         """From a Tree dict representation, create new nodes with their attributes. | ||||
|         Then create a connection dict by comparing link id from inputs and outputs of each nodes. | ||||
|         Use this dict to link nodes between each others. | ||||
| 
 | ||||
|         Args: | ||||
|             data (dict): Tree dict representation to generate nodes and links from. | ||||
|         """ | ||||
| 
 | ||||
|         connections = {} | ||||
| 
 | ||||
|         self.data = data | ||||
| 
 | ||||
|         for node_id, node_data in self.data["nodes"].items(): | ||||
|             new_node = Node.from_dict(node_data, self) | ||||
|             self.nodes.append(new_node) | ||||
| 
 | ||||
|             new_node.bl_node.select = True | ||||
| 
 | ||||
|             for ipt in new_node.inputs: | ||||
|                 if ipt.is_linked: | ||||
|                     connections.setdefault(ipt.link, {})["input"] = ipt.bl_input | ||||
| 
 | ||||
|             for opt in new_node.outputs: | ||||
|                 if opt.is_linked: | ||||
|                     for link in opt.link: | ||||
|                         connections.setdefault(link, {})["output"] = opt.bl_output | ||||
| 
 | ||||
|         for link_id in self.data["links"]: | ||||
|             ipt = connections[link_id]["input"] | ||||
|             opt = connections[link_id]["output"] | ||||
| 
 | ||||
|             self.bl_node_tree.links.new(ipt, opt) | ||||
							
								
								
									
										97
									
								
								core/_sockets.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								core/_sockets.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,97 @@ | ||||
| class Socket: | ||||
|     def __init__(self, bl_socket, tree): | ||||
|         self.tree = tree | ||||
|         self.bl_socket = bl_socket | ||||
|         self.data = {} | ||||
| 
 | ||||
|         self.id = hex(id(bl_socket)) | ||||
|         self.identifier = bl_socket.identifier | ||||
|         self.is_linked = bl_socket.is_linked | ||||
| 
 | ||||
|         self._value = None | ||||
| 
 | ||||
|         if hasattr(bl_socket, "default_value"): | ||||
|             self._value = bl_socket.default_value | ||||
| 
 | ||||
|     @property | ||||
|     def value(self): | ||||
|         if not isinstance(self._value, (str, int, float, bool)): | ||||
|             self._value = [v for v in self._value] | ||||
| 
 | ||||
|         return self._value | ||||
| 
 | ||||
|     @value.setter | ||||
|     def value(self, v): | ||||
|         self.bl_socket.default_value = v | ||||
|         self._value = v | ||||
|         return self._value | ||||
| 
 | ||||
|     def to_dict(self): | ||||
|         self.data["id"] = self.id | ||||
|         self.data["value"] = self.value | ||||
|         self.data["identifier"] = self.identifier | ||||
|         self.data["is_linked"] = self.is_linked | ||||
|         self.data["link"] = self.get_link() | ||||
|         return self.data | ||||
| 
 | ||||
| 
 | ||||
| class Input(Socket): | ||||
|     def __init__(self, bl_input, tree): | ||||
|         super().__init__(bl_input, tree) | ||||
| 
 | ||||
|         self.bl_input = bl_input | ||||
| 
 | ||||
|     @classmethod | ||||
|     def from_dict(cls, data, node): | ||||
|         for bl_ipt in node.bl_node.inputs: | ||||
|             if bl_ipt.identifier != data["identifier"]: | ||||
|                 continue | ||||
| 
 | ||||
|             new_ipt = cls(bl_ipt, node.tree) | ||||
| 
 | ||||
|             for k, v in data.items(): | ||||
|                 setattr(new_ipt, k, v) | ||||
| 
 | ||||
|             return new_ipt | ||||
| 
 | ||||
|     def get_link(self): | ||||
|         if not self.is_linked: | ||||
|             return None | ||||
| 
 | ||||
|         for ipt_link in self.bl_input.links: | ||||
|             for tree_link in self.tree.links: | ||||
|                 if ipt_link == tree_link.bl_link: | ||||
|                     return tree_link.id | ||||
| 
 | ||||
| 
 | ||||
| class Output(Socket): | ||||
|     def __init__(self, bl_output, tree): | ||||
|         super().__init__(bl_output, tree) | ||||
| 
 | ||||
|         self.bl_output = bl_output | ||||
| 
 | ||||
|     @classmethod | ||||
|     def from_dict(cls, data, node): | ||||
|         for bl_opt in node.bl_node.outputs: | ||||
|             if bl_opt.identifier != data["identifier"]: | ||||
|                 continue | ||||
| 
 | ||||
|             new_opt = cls(bl_opt, node.tree) | ||||
| 
 | ||||
|             for k, v in data.items(): | ||||
|                 setattr(new_opt, k, v) | ||||
| 
 | ||||
|             return new_opt | ||||
| 
 | ||||
|     def get_link(self): | ||||
|         links = [] | ||||
| 
 | ||||
|         if not self.is_linked: | ||||
|             return None | ||||
| 
 | ||||
|         for opt_link in self.bl_output.links: | ||||
|             for tree_link in self.tree.links: | ||||
|                 if opt_link == tree_link.bl_link: | ||||
|                     links.append(tree_link.id) | ||||
| 
 | ||||
|         return links | ||||
| @ -1,15 +1,41 @@ | ||||
| from __future__ import annotations | ||||
| 
 | ||||
| import json | ||||
| from copy import copy | ||||
| from os.path import abspath | ||||
| from pprint import pprint | ||||
| 
 | ||||
| import bpy | ||||
| 
 | ||||
| from . import utils | ||||
| from .. import utils | ||||
| 
 | ||||
| 
 | ||||
| format_token = "#FMT:NODE_KIT#" | ||||
| 
 | ||||
| 
 | ||||
| def dump_nkit_format(data: str) -> str: | ||||
|     return format_token + json.dumps(data) | ||||
| 
 | ||||
| 
 | ||||
| def parse_nkit_format(data: str) -> str | None: | ||||
|     if data.startswith(format_token): | ||||
|         print(data[len(format_token):]) | ||||
|         return json.loads(data[len(format_token):]) | ||||
|      | ||||
|     return None | ||||
| 
 | ||||
| 
 | ||||
| def get_default(prop: bpy.types.Property): | ||||
|     """Get the default value of a Blender property""" | ||||
|     if getattr(prop, "is_array", False): | ||||
|         return list(prop.default_array) | ||||
|     elif hasattr(prop, "default"): | ||||
|         return prop.default | ||||
| 
 | ||||
| 
 | ||||
| def get_dumper(bl_object: bpy.types.bpy_struct) -> type[Dumper]: | ||||
|     """Get the closest corresponding dumper for a given Blender object using its MRO""" | ||||
|     """Get the corresponding dumper for a given Blender object, or its closest base type using its MRO""" | ||||
| 
 | ||||
|     for cls in bl_object.__class__.mro(): | ||||
|         dumper_map = DumperRegistry().dumper_map | ||||
|         if cls in dumper_map: | ||||
| @ -18,9 +44,13 @@ def get_dumper(bl_object: bpy.types.bpy_struct) -> type[Dumper]: | ||||
|     # Fallback to base Dumper if no matches are found | ||||
|     return Dumper | ||||
| 
 | ||||
| def get_current_node_tree(data): | ||||
|     if data.get("_new", {}).get("type") == "GeometryNodeTree": | ||||
|         return bpy.context.object.modifiers.active.node_group | ||||
| 
 | ||||
| 
 | ||||
| def dump_nodes(nodes: list[bpy.types.Node]): | ||||
|     """Generic recursive dump, convert nodes into a dict""" | ||||
|     """Generic Recursive Dump, convert any object into a dict""" | ||||
|     Dumper.pointers.clear()  # TODO: Bad global | ||||
| 
 | ||||
|     data = [dump_node(node) for node in nodes] | ||||
| @ -29,15 +59,19 @@ def dump_nodes(nodes: list[bpy.types.Node]): | ||||
|      | ||||
|     return data | ||||
| 
 | ||||
| 
 | ||||
| def dump_node(node: bpy.types.Node): | ||||
|     dumper = get_dumper(node) | ||||
|     return dumper.dump(node) | ||||
|     return dumper.dump(node)  # TODO: Break the recursivity, clear things up | ||||
| 
 | ||||
| 
 | ||||
| def load_nodes(data, node_tree): | ||||
|     """Load/Dump nodes into a specific node tree""" | ||||
| def load_nodes(data, node_tree=None): | ||||
|     """Generic Load to create an object from a dict""" | ||||
| 
 | ||||
|     Dumper.pointers.clear() | ||||
|     # print(Dumper.pointers) | ||||
| 
 | ||||
|     if node_tree is None: | ||||
|         node_tree = get_current_node_tree(data) | ||||
| 
 | ||||
|     dumper = get_dumper(node_tree) | ||||
|     dumper.load(data, node_tree) | ||||
| @ -45,6 +79,13 @@ def load_nodes(data, node_tree): | ||||
|     Dumper.pointers.clear() | ||||
| 
 | ||||
| 
 | ||||
| def set_attribute(bl_object, attr, value): | ||||
|     try: | ||||
|         setattr(bl_object, attr, value) | ||||
|     except Exception as e: | ||||
|         print(e) | ||||
| 
 | ||||
| 
 | ||||
| class Dumper: | ||||
|     pointers = {} | ||||
|     includes = [] | ||||
| @ -103,7 +144,7 @@ class Dumper: | ||||
|                     value = cls.pointers[value] | ||||
| 
 | ||||
|                 elif value is None: | ||||
|                     utils.set_bl_attribute(bl_object, key, value) | ||||
|                     set_attribute(bl_object, key, value) | ||||
| 
 | ||||
|                 else: | ||||
|                     bl_type = prop.fixed_type.bl_rna.type_recast() | ||||
| @ -122,14 +163,14 @@ class Dumper: | ||||
|                     value = attr | ||||
| 
 | ||||
|                 if not prop.is_readonly: | ||||
|                     utils.set_bl_attribute(bl_object, key, value) | ||||
|                     set_attribute(bl_object, key, value) | ||||
| 
 | ||||
|                 # Some coll needs a manual update like curve mapping | ||||
|                 if hasattr(attr, "update"): | ||||
|                     attr.update() | ||||
| 
 | ||||
|             elif not prop.is_readonly: | ||||
|                 utils.set_bl_attribute(bl_object, key, value) | ||||
|                 set_attribute(bl_object, key, value) | ||||
|                 continue | ||||
| 
 | ||||
|         # return bl_object | ||||
| @ -216,7 +257,7 @@ class PropCollection(Dumper): | ||||
|                 params = value["_new"] | ||||
|             else: | ||||
|                 params = { | ||||
|                     k: value.get(k, utils.get_bl_default(v)) | ||||
|                     k: value.get(k, get_default(v)) | ||||
|                     for k, v in new_func.parameters.items()[:-1] | ||||
|                 } | ||||
| 
 | ||||
| @ -516,7 +557,7 @@ class Points(PropCollection): | ||||
|     @classmethod | ||||
|     def load(cls, values, coll): | ||||
|         new_func = coll.bl_rna.functions["new"] | ||||
|         params = {k: utils.get_bl_default(v) + 1.1 for k, v in new_func.parameters.items()[:-1]} | ||||
|         params = {k: get_default(v) + 1.1 for k, v in new_func.parameters.items()[:-1]} | ||||
| 
 | ||||
|         # Match the same number of elements in collection | ||||
|         if len(values) > len(coll): | ||||
							
								
								
									
										17
									
								
								formats.py
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								formats.py
									
									
									
									
									
								
							| @ -1,17 +0,0 @@ | ||||
| import json | ||||
| 
 | ||||
| 
 | ||||
| format_token = "#FMT:NODE_KIT#" | ||||
| 
 | ||||
| 
 | ||||
| def dump_nkit_format(data: str) -> str: | ||||
|     return format_token + json.dumps(data) | ||||
| 
 | ||||
| 
 | ||||
| def parse_nkit_format(data: str) -> str | None: | ||||
|     if data.startswith(format_token): | ||||
|         print(data[len(format_token):]) | ||||
|         return json.loads(data[len(format_token):]) | ||||
|      | ||||
|     return None | ||||
| 
 | ||||
							
								
								
									
										25
									
								
								operators.py
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								operators.py
									
									
									
									
									
								
							| @ -14,10 +14,9 @@ import bpy | ||||
| from bpy.props import BoolProperty, EnumProperty | ||||
| from bpy.types import Operator | ||||
| 
 | ||||
| from .dumper import dump_nodes, load_nodes | ||||
| from .node_utils import remap_node_group_duplicates | ||||
| from .pack_nodes import combine_objects, extract_objects | ||||
| from .formats import dump_nkit_format, parse_nkit_format | ||||
| from .core.dumper import dump_nodes, load_nodes, dump_nkit_format, parse_nkit_format | ||||
| from .core.node_utils import remap_node_group_duplicates | ||||
| from .core.pack_nodes import combine_objects, extract_objects | ||||
| 
 | ||||
| 
 | ||||
| class NODEKIT_OT_copy(Operator): | ||||
| @ -37,6 +36,8 @@ class NODEKIT_OT_copy(Operator): | ||||
|             ), | ||||
|         } | ||||
| 
 | ||||
|         pprint(ntree_data) | ||||
| 
 | ||||
|         context.window_manager.clipboard = dump_nkit_format(ntree_data) | ||||
| 
 | ||||
|         self.report({"INFO"}, f"Copied {len(selected_nodes)} selected nodes to system clipboard") | ||||
| @ -51,7 +52,9 @@ class NODEKIT_OT_copy_tree(Operator): | ||||
| 
 | ||||
|     def execute(self, context): | ||||
|         ntree = context.space_data.edit_tree | ||||
|         ntree_data = dict(ntree) | ||||
|         ntree_data = dump_nodes(ntree) | ||||
| 
 | ||||
|         pprint(ntree_data) | ||||
| 
 | ||||
|         context.window_manager.clipboard = dump_nkit_format(ntree_data) | ||||
| 
 | ||||
| @ -75,8 +78,7 @@ class NODEKIT_OT_paste(Operator): | ||||
| 
 | ||||
| class NODEKIT_OT_remap_node_group_duplicates(Operator): | ||||
|     bl_idname = "node_kit.remap_node_group_duplicates" | ||||
|     bl_label = "Clean Node Groups Duplicates" | ||||
|     bl_description = "Remap Node Groups duplicates to the latest imported version" | ||||
|     bl_label = "Remap Node Groups Duplicates" | ||||
|     bl_options = {"REGISTER", "UNDO"} | ||||
| 
 | ||||
|     selection: EnumProperty( | ||||
| @ -137,8 +139,7 @@ class NODEKIT_OT_remap_node_group_duplicates(Operator): | ||||
| 
 | ||||
| class NODEKIT_OT_update_nodes(Operator): | ||||
|     bl_idname = "node_kit.update_nodes" | ||||
|     bl_label = "Update Nodes from Library" | ||||
|     bl_description = "Overrides node group using the latest version from Asset Library" | ||||
|     bl_label = "Update Nodes" | ||||
|     bl_options = {"REGISTER", "UNDO"} | ||||
| 
 | ||||
|     selection: EnumProperty( | ||||
| @ -248,8 +249,7 @@ class NODEKIT_OT_update_nodes(Operator): | ||||
| 
 | ||||
| class NODEKIT_OT_pack_nodes(Operator): | ||||
|     bl_idname = "node_kit.pack_nodes" | ||||
|     bl_label = "Pack Modifiers as Nodes" | ||||
|     bl_description = "Pack Geometry Nodes modifiers stack as a single node tree" | ||||
|     bl_label = "Pack Nodes" | ||||
|     bl_options = {"REGISTER", "UNDO"} | ||||
| 
 | ||||
|     def execute(self, context): | ||||
| @ -259,8 +259,7 @@ class NODEKIT_OT_pack_nodes(Operator): | ||||
| 
 | ||||
| class NODEKIT_OT_unpack_nodes(Operator): | ||||
|     bl_idname = "node_kit.unpack_nodes" | ||||
|     bl_label = "Unpack Nodes as Modifiers" | ||||
|     bl_description = "Unpack node tree as Geometry Nodes modifiers" | ||||
|     bl_label = "Unpack Nodes" | ||||
|     bl_options = {"REGISTER", "UNDO"} | ||||
| 
 | ||||
|     def execute(self, context): | ||||
|  | ||||
							
								
								
									
										16
									
								
								utils.py
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								utils.py
									
									
									
									
									
								
							| @ -1,22 +1,6 @@ | ||||
| import bpy | ||||
| 
 | ||||
| 
 | ||||
| def all_subclasses(cls): | ||||
|     return set(cls.__subclasses__()).union( | ||||
|         [s for c in cls.__subclasses__() for s in all_subclasses(c)] | ||||
|     ) | ||||
| 
 | ||||
| 
 | ||||
| def get_bl_default(prop: bpy.types.Property): | ||||
|     """Get the default value of a Blender property""" | ||||
|     if getattr(prop, "is_array", False): | ||||
|         return list(prop.default_array) | ||||
|     elif hasattr(prop, "default"): | ||||
|         return prop.default | ||||
| 
 | ||||
| 
 | ||||
| def set_bl_attribute(bl_object, attr, value): | ||||
|     try: | ||||
|         setattr(bl_object, attr, value) | ||||
|     except Exception as e: | ||||
|         print(e) | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user