mesh_tools/mesh_relax: pass in smooth factor
[blender-addons.git] / node_presets.py
blob2f65dd5467a267c170a630ed851ec48322f73237
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 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
29 "node/node_presets.html",
30 "category": "Node",
33 import os
34 import bpy
35 from bpy.types import (
36 Operator,
37 Menu,
38 AddonPreferences,
41 from bpy.props import (
42 StringProperty,
46 # -----------------------------------------------------------------------------
47 # Node Adding Operator
50 def node_center(context):
51 from mathutils import Vector
52 loc = Vector((0.0, 0.0))
53 node_selected = context.selected_nodes
54 if node_selected:
55 for node in node_selected:
56 loc += node.location
57 loc /= len(node_selected)
58 return loc
61 def node_template_add(context, filepath, node_group, ungroup, report):
62 """ Main function
63 """
65 space = context.space_data
66 node_tree = space.node_tree
67 node_active = context.active_node
68 node_selected = context.selected_nodes
70 if node_tree is None:
71 report({'ERROR'}, "No node tree available")
72 return
74 with bpy.data.libraries.load(filepath, link=False) as (data_from, data_to):
75 assert(node_group in data_from.node_groups)
76 data_to.node_groups = [node_group]
77 node_group = data_to.node_groups[0]
79 # add node!
80 center = node_center(context)
82 for node in node_tree.nodes:
83 node.select = False
85 node_type_string = {
86 "ShaderNodeTree": "ShaderNodeGroup",
87 "CompositorNodeTree": "CompositorNodeGroup",
88 "TextureNodeTree": "TextureNodeGroup",
89 }[type(node_tree).__name__]
91 node = node_tree.nodes.new(type=node_type_string)
92 node.node_tree = node_group
94 is_fail = (node.node_tree is None)
95 if is_fail:
96 report({'WARNING'}, "Incompatible node type")
98 node.select = True
99 node_tree.nodes.active = node
100 node.location = center
102 if is_fail:
103 node_tree.nodes.remove(node)
104 else:
105 if ungroup:
106 bpy.ops.node.group_ungroup()
108 # node_group.user_clear()
109 # bpy.data.node_groups.remove(node_group)
112 # -----------------------------------------------------------------------------
113 # Node Template Prefs
115 def node_search_path(context):
116 preferences = context.preferences
117 addon_prefs = preferences.addons[__name__].preferences
118 dirpath = addon_prefs.search_path
119 return dirpath
122 class NodeTemplatePrefs(AddonPreferences):
123 bl_idname = __name__
125 search_path: StringProperty(
126 name="Directory of blend files with node-groups",
127 subtype='DIR_PATH',
130 def draw(self, context):
131 layout = self.layout
132 layout.prop(self, "search_path")
135 class NODE_OT_template_add(Operator):
136 """Add a node template"""
137 bl_idname = "node.template_add"
138 bl_label = "Add node group template"
139 bl_description = "Add node group template"
140 bl_options = {'REGISTER', 'UNDO'}
142 filepath: StringProperty(
143 subtype='FILE_PATH',
145 group_name: StringProperty()
147 def execute(self, context):
148 node_template_add(context, self.filepath, self.group_name, True, self.report)
150 return {'FINISHED'}
152 def invoke(self, context, event):
153 node_template_add(context, self.filepath, self.group_name, event.shift, self.report)
155 return {'FINISHED'}
158 # -----------------------------------------------------------------------------
159 # Node menu list
161 def node_template_cache(context, *, reload=False):
162 dirpath = node_search_path(context)
164 if node_template_cache._node_cache_path != dirpath:
165 reload = True
167 node_cache = node_template_cache._node_cache
168 if reload:
169 node_cache = []
170 if node_cache:
171 return node_cache
173 for fn in os.listdir(dirpath):
174 if fn.endswith(".blend"):
175 filepath = os.path.join(dirpath, fn)
176 with bpy.data.libraries.load(filepath) as (data_from, data_to):
177 for group_name in data_from.node_groups:
178 if not group_name.startswith("_"):
179 node_cache.append((filepath, group_name))
181 node_template_cache._node_cache = node_cache
182 node_template_cache._node_cache_path = dirpath
184 return node_cache
187 node_template_cache._node_cache = []
188 node_template_cache._node_cache_path = ""
191 class NODE_MT_template_add(Menu):
192 bl_label = "Node Template"
194 def draw(self, context):
195 layout = self.layout
197 dirpath = node_search_path(context)
198 if dirpath == "":
199 layout.label(text="Set search dir in the addon-prefs")
200 return
202 try:
203 node_items = node_template_cache(context)
204 except Exception as ex:
205 node_items = ()
206 layout.label(text=repr(ex), icon='ERROR')
208 for filepath, group_name in node_items:
209 props = layout.operator(
210 NODE_OT_template_add.bl_idname,
211 text=group_name,
213 props.filepath = filepath
214 props.group_name = group_name
217 def add_node_button(self, context):
218 self.layout.menu(
219 NODE_MT_template_add.__name__,
220 text="Template",
221 icon='PLUGIN',
225 classes = (
226 NODE_OT_template_add,
227 NODE_MT_template_add,
228 NodeTemplatePrefs
232 def register():
233 for cls in classes:
234 bpy.utils.register_class(cls)
236 bpy.types.NODE_MT_add.append(add_node_button)
239 def unregister():
240 for cls in classes:
241 bpy.utils.unregister_class(cls)
243 bpy.types.NODE_MT_add.remove(add_node_button)
246 if __name__ == "__main__":
247 register()