Pose Library: update for rename of asset_library to asset_library_ref
[blender-addons.git] / node_presets.py
blobd6a708981a13a8edccc6257221ccea09f2908287
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; version 2
6 # of the License.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 "name": "Node Presets",
21 "description": "Useful and time-saving tools for node group workflow",
22 "author": "Campbell Barton",
23 "version": (1, 1),
24 "blender": (2, 80, 0),
25 "location": "Node Editors > Add > Template",
26 "description": "Add node groups directly to the node editors",
27 "warning": "",
28 "doc_url": "{BLENDER_MANUAL_URL}/addons/node/node_presets.html",
29 "category": "Node",
32 import os
33 import bpy
34 from bpy.types import (
35 Operator,
36 Menu,
37 AddonPreferences,
40 from bpy.props import (
41 StringProperty,
45 # -----------------------------------------------------------------------------
46 # Node Adding Operator
49 def node_center(context):
50 from mathutils import Vector
51 loc = Vector((0.0, 0.0))
52 node_selected = context.selected_nodes
53 if node_selected:
54 for node in node_selected:
55 loc += node.location
56 loc /= len(node_selected)
57 return loc
60 def node_template_add(context, filepath, node_group, ungroup, report):
61 """ Main function
62 """
64 space = context.space_data
65 node_tree = space.node_tree
66 node_active = context.active_node
67 node_selected = context.selected_nodes
69 if node_tree is None:
70 report({'ERROR'}, "No node tree available")
71 return
73 with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
74 assert(node_group in data_from.node_groups)
75 data_to.node_groups = [node_group]
76 node_group = data_to.node_groups[0]
78 # add node!
79 center = node_center(context)
81 for node in node_tree.nodes:
82 node.select = False
84 node_type_string = {
85 "ShaderNodeTree": "ShaderNodeGroup",
86 "CompositorNodeTree": "CompositorNodeGroup",
87 "TextureNodeTree": "TextureNodeGroup",
88 }[type(node_tree).__name__]
90 node = node_tree.nodes.new(type=node_type_string)
91 node.node_tree = node_group
93 is_fail = (node.node_tree is None)
94 if is_fail:
95 report({'WARNING'}, "Incompatible node type")
97 node.select = True
98 node_tree.nodes.active = node
99 node.location = center
101 if is_fail:
102 node_tree.nodes.remove(node)
103 else:
104 if ungroup:
105 bpy.ops.node.group_ungroup()
107 # node_group.user_clear()
108 # bpy.data.node_groups.remove(node_group)
111 # -----------------------------------------------------------------------------
112 # Node Template Prefs
114 def node_search_path(context):
115 preferences = context.preferences
116 addon_prefs = preferences.addons[__name__].preferences
117 dirpath = addon_prefs.search_path
118 return dirpath
121 class NodeTemplatePrefs(AddonPreferences):
122 bl_idname = __name__
124 search_path: StringProperty(
125 name="Directory of blend files with node-groups",
126 subtype='DIR_PATH',
129 def draw(self, context):
130 layout = self.layout
131 layout.prop(self, "search_path")
134 class NODE_OT_template_add(Operator):
135 """Add a node template"""
136 bl_idname = "node.template_add"
137 bl_label = "Add node group template"
138 bl_description = "Add node group template"
139 bl_options = {'REGISTER', 'UNDO'}
141 filepath: StringProperty(
142 subtype='FILE_PATH',
144 group_name: StringProperty()
146 def execute(self, context):
147 node_template_add(context, self.filepath, self.group_name, True, self.report)
149 return {'FINISHED'}
151 def invoke(self, context, event):
152 node_template_add(context, self.filepath, self.group_name, event.shift, self.report)
154 return {'FINISHED'}
157 # -----------------------------------------------------------------------------
158 # Node menu list
160 def node_template_cache(context, *, reload=False):
161 dirpath = node_search_path(context)
163 if node_template_cache._node_cache_path != dirpath:
164 reload = True
166 node_cache = node_template_cache._node_cache
167 if reload:
168 node_cache = []
169 if node_cache:
170 return node_cache
172 for fn in os.listdir(dirpath):
173 if fn.endswith(".blend"):
174 filepath = os.path.join(dirpath, fn)
175 with bpy.data.libraries.load(filepath) as (data_from, data_to):
176 for group_name in data_from.node_groups:
177 if not group_name.startswith("_"):
178 node_cache.append((filepath, group_name))
180 node_template_cache._node_cache = node_cache
181 node_template_cache._node_cache_path = dirpath
183 return node_cache
186 node_template_cache._node_cache = []
187 node_template_cache._node_cache_path = ""
190 class NODE_MT_template_add(Menu):
191 bl_label = "Node Template"
193 def draw(self, context):
194 layout = self.layout
196 dirpath = node_search_path(context)
197 if dirpath == "":
198 layout.label(text="Set search dir in the addon-prefs")
199 return
201 try:
202 node_items = node_template_cache(context)
203 except Exception as ex:
204 node_items = ()
205 layout.label(text=repr(ex), icon='ERROR')
207 for filepath, group_name in node_items:
208 props = layout.operator(
209 NODE_OT_template_add.bl_idname,
210 text=group_name,
212 props.filepath = filepath
213 props.group_name = group_name
216 def add_node_button(self, context):
217 self.layout.menu(
218 NODE_MT_template_add.__name__,
219 text="Template",
220 icon='PLUGIN',
224 classes = (
225 NODE_OT_template_add,
226 NODE_MT_template_add,
227 NodeTemplatePrefs
231 def register():
232 for cls in classes:
233 bpy.utils.register_class(cls)
235 bpy.types.NODE_MT_add.append(add_node_button)
238 def unregister():
239 for cls in classes:
240 bpy.utils.unregister_class(cls)
242 bpy.types.NODE_MT_add.remove(add_node_button)
245 if __name__ == "__main__":
246 register()