addons-contrib: objects.link/unlink syntax update
[blender-addons-contrib.git] / object_creaprim.py
1 # ###### BEGIN GPL LICENSE BLOCK ######
2 #
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; either version 2
6 # of the License, or (at your option) any later version.
7 #
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.
12 #
13 # If you have Internet access, you can find the license text at
14 # http://www.gnu.org/licenses/gpl.txt,
15 # if not, write to the Free Software Foundation,
16 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #
18 # ###### END GPL LICENCE BLOCK ######
19
20
21 """
22 CreaPrim does what it says. I takes the active object and turns it into an
23 Add Mesh addon. When you enable this, your custom object will be added to the
24 Add->Mesh menu.
25
26
27 Documentation
28
29 Go to User Preferences->Addons and enable the CreaPrim addon in the Object
30 section. The addon will show up in the 3dview properties panel.
31
32 First select your object or objects.
33 The name (in panel) will be set to the active object name.
34 Select "Apply transform" if you want transforms to be applied to the selected
35 objects. Modifiers will taken into account. You can always change this.
36 Just hit the button and the selected objects will be saved in your addons folder
37 as an Add Mesh addon with the name "add_mesh_XXXX.py" with XXXX being your
38 object name.  The addon will show up in User Preferences->Addons in the
39 Add Mesh section. Enable this addon et voila, your new custom primitive will
40 now show up in the Add Mesh menu.
41
42 REMARK - dont need to be admin anymore - saves to user scripts dir
43
44 ALSO - dont forget to Apply rotation and scale to have your object
45 show up correctly
46 """
47
48 bl_info = {
49     "name": "CreaPrim",
50     "author": "Gert De Roost",
51     "version": (0, 3, 11),
52     "blender": (2, 64, 0),
53     "location": "View3D > Object Tools",
54     "description": "Create primitive addon",
55     "warning": "",
56     "wiki_url": "",
57     "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/",
58     "category": "Object"}
59
60
61 import bpy
62 import bmesh
63 import os
64
65
66 started = 0
67 oldname = ""
68
69
70 def test_data(obj):
71     me = obj.data
72     is_faces = bool(len(me.polygons) > 0)
73     return is_faces
74
75
76 class CreaPrim(bpy.types.Operator):
77     bl_idname = "object.creaprim"
78     bl_label = "CreaPrim"
79     bl_description = "Create a primitive add-on"
80     bl_options = {"REGISTER"}
81
82     @classmethod
83     def poll(cls, context):
84         obj = context.active_object
85         return (obj and obj.type == 'MESH' and context.mode == 'OBJECT')
86
87     def invoke(self, context, event):
88
89         global oldname, groupname, message
90
91         scn = bpy.context.scene
92
93         objlist = []
94         for selobj in bpy.context.scene.objects:
95             if selobj.select and test_data(selobj) is True:
96                 objlist.append(selobj)
97
98         if len(objlist) == 0:
99             self.report({'WARNING'},
100                         "Please select Mesh objects containing Polygons. Operation Cancelled")
101             return {"CANCELLED"}
102
103         try:
104             direc = bpy.utils.script_paths()[1]
105             scriptdir = 1
106         except:
107             direc = bpy.utils.script_paths()[0]
108             scriptdir = 0
109
110         if len(objlist) > 1:
111             groupname = scn.Creaprim_Name
112             groupname = groupname.replace(".", "")
113             addondir = direc + os.sep + "addons" + os.sep + "add_mesh_" + groupname + os.sep
114             if not os.path.exists(addondir):
115                 os.makedirs(addondir)
116         else:
117             groupname = scn.Creaprim_Name
118             print(bpy.utils.script_paths())
119             addondir = direc + os.sep + "addons" + os.sep
120             print(addondir)
121             if not os.path.exists(addondir):
122                 os.makedirs(addondir)
123
124         actobj = bpy.context.active_object
125         txtlist = []
126         namelist = []
127         for selobj in objlist:
128             if len(objlist) == 1:
129                 objname = scn.Creaprim_Name
130                 objname = objname.replace(" ", "_")
131             else:
132                 objname = selobj.name
133                 objname = objname.replace(".", "")
134                 objname = objname.replace(" ", "_")
135                 namelist.append(objname)
136             mesh = selobj.to_mesh(scn, True, "PREVIEW")
137             oldname = selobj.name
138             scn.objects.active = selobj
139
140             if scn.Creaprim_Apply:
141                 bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
142             txt = do_creaprim(self, mesh, objname, addondir)
143
144             if txt == 0:
145                 return {'CANCELLED'}
146
147             txtlist.append(txt)
148
149         oldname = actobj.name
150         scn.objects.active = actobj
151
152         if len(txtlist) > 1:
153             makeinit(txtlist, namelist, groupname, addondir)
154             bpy.ops.wm.addon_enable(module="add_mesh_" + groupname)
155         else:
156             bpy.ops.wm.addon_enable(module="add_mesh_" + str.lower(objname))
157
158         if scriptdir == 1:
159             message = "Add Mesh addon " + groupname + " saved to user scripts directory."
160         else:
161             message = "Add Mesh addon " + groupname + " saved to main scripts directory."
162         bpy.ops.creaprim.message('INVOKE_DEFAULT')
163
164         return {'FINISHED'}
165
166
167 class MessageOperator(bpy.types.Operator):
168     bl_idname = "creaprim.message"
169     bl_label = "Saved"
170
171     def invoke(self, context, event):
172         wm = context.window_manager
173         return wm.invoke_popup(self, width=500, height=20)
174
175     def draw(self, context):
176
177         global groupname
178
179         layout = self.layout
180         row = layout.row()
181         row.label(text='', icon="PLUGIN")
182         row.label(message)
183
184
185 def panel_func(self, context):
186
187     global started
188
189     scn = bpy.context.scene
190
191     self.layout.label(text="CreaPrim:")
192     self.layout.operator("object.creaprim", text="Create primitive", icon='PLUGIN')
193     self.layout.prop(scn, "Creaprim_Name")
194     self.layout.prop(scn, "Creaprim_Apply")
195
196
197 def register():
198     bpy.utils.register_module(__name__)
199     bpy.types.Scene.Creaprim_Name = bpy.props.StringProperty(
200             name="Name",
201             description="Name for the primitive",
202             maxlen=1024
203             )
204     bpy.types.Scene.Creaprim_Apply = bpy.props.BoolProperty(
205             name="Apply transform",
206             description="Apply transform to selected objects",
207             default=False
208             )
209     bpy.types.VIEW3D_PT_tools_object.append(panel_func)
210     bpy.app.handlers.scene_update_post.append(setname)
211
212
213 def unregister():
214     bpy.utils.unregister_module(__name__)
215     bpy.types.VIEW3D_PT_tools_object.remove(panel_func)
216     bpy.app.handlers.scene_update_post.remove(setname)
217     del bpy.types.Scene.Creaprim_Name
218     del bpy.types.Scene.Creaprim_Apply
219
220
221 if __name__ == "__main__":
222     register()
223
224
225 def do_creaprim(self, mesh, objname, addondir):
226
227     global message
228
229     objname = objname.replace(".", "")
230     objname = objname.replace(" ", "_")
231     bm = bmesh.new()
232     bm.from_mesh(mesh)
233
234     try:
235         txt = bpy.data.texts[str.lower("add_mesh_" + objname) + ".py"]
236         txt.clear()
237     except:
238         txt = bpy.data.texts.new("add_mesh_" + str.lower(objname) + ".py")
239
240     strlist = []
241     strlist.append("# Script autogenerated by object_creaprim.py")
242     strlist.append("\n")
243     strlist.append("bl_info = {\n")
244     strlist.append("    \"name\": \"" + objname + "\",\n")
245     strlist.append("    \"author\": \"Gert De Roost\",\n")
246     strlist.append("    \"version\": (1, 0, 0),\n")
247     strlist.append("    \"blender\": (2, 65, 0),\n")
248     strlist.append("    \"location\": \"Add > Mesh\",\n")
249     strlist.append("    \"description\": \"Create " + objname + " primitive\",\n")
250     strlist.append("    \"warning\": \"\",\n")
251     strlist.append("    \"wiki_url\": \"\",\n")
252     strlist.append("    \"tracker_url\": \"\",\n")
253     strlist.append("    \"category\": \"Add Mesh\"}\n")
254     strlist.append("\n")
255     strlist.append("\n")
256     strlist.append("import bpy\n")
257     strlist.append("import bmesh\n")
258     strlist.append("import math\n")
259     strlist.append("from mathutils import *\n")
260     strlist.append("\n")
261     strlist.append("\n")
262     strlist.append("class " + objname + "(bpy.types.Operator):\n")
263     strlist.append("    bl_idname = \"mesh." + str.lower(objname) + "\"\n")
264     strlist.append("    bl_label = \"" + objname + "\"\n")
265     strlist.append("    bl_options = {\'REGISTER\', \'UNDO\'}\n")
266     strlist.append("    bl_description = \"add " + objname + " primitive\"\n")
267     strlist.append("\n")
268     strlist.append("    def invoke(self, context, event):\n")
269     strlist.append("\n")
270     strlist.append("        mesh = bpy.data.meshes.new(name=\"" + objname + "\")\n")
271     strlist.append("        obj = bpy.data.objects.new(name=\"" + objname + "\", object_data=mesh)\n")
272     strlist.append("        collection = bpy.context.collection\n")
273     strlist.append("        scene = bpy.context.scene\n")
274     strlist.append("        collection.objects.link(obj)\n")
275     strlist.append("        obj.location = scene.cursor_location\n")
276     strlist.append("        bm = bmesh.new()\n")
277     strlist.append("        bm.from_mesh(mesh)\n")
278     strlist.append("\n")
279     strlist.append("        idxlist = []\n")
280     posn = 0
281     strlist.append("        vertlist = [")
282     for v in bm.verts:
283         if posn > 0:
284             strlist.append(", ")
285         posn += 1
286         strlist.append(str(v.co[:]))
287     strlist.append("]\n")
288     strlist.append("        for co in vertlist:\n")
289     strlist.append("            v = bm.verts.new(co)\n")
290     strlist.append("            bm.verts.index_update()\n")
291     strlist.append("            idxlist.append(v.index)\n")
292     posn = 0
293     strlist.append("        edgelist = [")
294     for e in bm.edges:
295         if posn > 0:
296             strlist.append(", ")
297         posn += 1
298         strlist.append("[" + str(e.verts[0].index) + ", " + str(e.verts[1].index) + "]")
299     strlist.append("]\n")
300     strlist.append("        for verts in edgelist:\n")
301     strlist.append("            try:\n")
302     strlist.append("                bm.edges.new((bm.verts[verts[0]], bm.verts[verts[1]]))\n")
303     strlist.append("            except:\n")
304     strlist.append("                pass\n")
305     posn1 = 0
306     strlist.append("        facelist = [(")
307     for f in bm.faces:
308         if posn1 > 0:
309             strlist.append(", (")
310         posn1 += 1
311         posn2 = 0
312         for v in f.verts:
313             if posn2 > 0:
314                 strlist.append(", ")
315             strlist.append(str(v.index))
316             posn2 += 1
317         strlist.append(")")
318     strlist.append("]\n")
319     strlist.append("        bm.verts.ensure_lookup_table()\n")
320     strlist.append("        for verts in facelist:\n")
321     strlist.append("            vlist = []\n")
322     strlist.append("            for idx in verts:\n")
323     strlist.append("                vlist.append(bm.verts[idxlist[idx]])\n")
324     strlist.append("            try:\n")
325     strlist.append("                bm.faces.new(vlist)\n")
326     strlist.append("            except:\n")
327     strlist.append("                pass\n")
328     strlist.append("\n")
329     strlist.append("        bm.to_mesh(mesh)\n")
330     strlist.append("        mesh.update()\n")
331     strlist.append("        bm.free()\n")
332     strlist.append("        obj.rotation_quaternion = (Matrix.Rotation(math.radians(90), 3, \'X\').to_quaternion())\n")
333     strlist.append("\n")
334     strlist.append("        return {\'FINISHED\'}\n")
335
336     strlist.append("\n")
337     strlist.append("\n")
338     strlist.append("def menu_item(self, context):\n")
339     strlist.append("    self.layout.operator(" + objname + ".bl_idname, text=\"" + objname + "\", icon=\"PLUGIN\")\n")
340     strlist.append("\n")
341     strlist.append("\n")
342     strlist.append("def register():\n")
343     strlist.append("    bpy.utils.register_module(__name__)\n")
344     strlist.append("    bpy.types.VIEW3D_MT_mesh_add.append(menu_item)\n")
345     strlist.append("\n")
346     strlist.append("\n")
347     strlist.append("def unregister():\n")
348     strlist.append("    bpy.utils.unregister_module(__name__)\n")
349     strlist.append("    bpy.types.VIEW3D_MT_mesh_add.remove(menu_item)\n")
350     strlist.append("\n")
351     strlist.append("\n")
352     strlist.append("if __name__ == \"__main__\":\n")
353     strlist.append("    register()\n")
354     endstring = ''.join(strlist)
355     txt.write(endstring)
356
357     try:
358         fileobj = open(addondir + "add_mesh_" + str.lower(objname) + ".py", "w")
359     except:
360         message = "Permission problem - cant write file - run Blender as Administrator!"
361         bpy.ops.creaprim.message('INVOKE_DEFAULT')
362         return 0
363
364     fileobj.write(endstring)
365     fileobj.close()
366
367     bm.free()
368
369     return txt
370
371
372 def makeinit(txtlist, namelist, groupname, addondir):
373
374     global message
375
376     try:
377         txt = bpy.data.texts["__init__.py"]
378         txt.clear()
379     except:
380         txt = bpy.data.texts.new("__init__.py")
381
382     strlist = []
383     strlist.append("# Script autogenerated by object_creaprim.py")
384     strlist.append("\n")
385     strlist.append("bl_info = {\n")
386     strlist.append("    \"name\": \"" + groupname + "\",\n")
387     strlist.append("    \"author\": \"Gert De Roost\",\n")
388     strlist.append("    \"version\": (1, 0, 0),\n")
389     strlist.append("    \"blender\": (2, 65, 0),\n")
390     strlist.append("    \"location\": \"Add > Mesh\",\n")
391     strlist.append("    \"description\": \"Create " + groupname + " primitive group\",\n")
392     strlist.append("    \"warning\": \"\",\n")
393     strlist.append("    \"wiki_url\": \"\",\n")
394     strlist.append("    \"tracker_url\": \"\",\n")
395     strlist.append("    \"category\": \"Add Mesh\"}\n")
396     strlist.append("\n")
397     strlist.append("\n")
398     strlist.append("if \"bpy\" in locals():\n")
399     strlist.append("    import importlib\n")
400     addonlist = []
401     for txt in txtlist:
402         name = txt.name.replace(".py", "")
403         addonlist.append(name)
404     for name in addonlist:
405         strlist.append("    importlib.reload(" + name + ")\n")
406     strlist.append("else:\n")
407     for name in addonlist:
408         strlist.append("    from . import " + name + "\n")
409     strlist.append("\n")
410     strlist.append("\n")
411     strlist.append("import bpy\n")
412     strlist.append("\n")
413     strlist.append("\n")
414     strlist.append("class VIEW3D_MT_mesh_" + str.lower(groupname) + "_add(bpy.types.Menu):\n")
415     strlist.append("    bl_idname = \"VIEW3D_MT_mesh_" + str.lower(groupname) + "_add\"\n")
416     strlist.append("    bl_label = \"" + groupname + "\"\n")
417     strlist.append("\n")
418     strlist.append("    def draw(self, context):\n")
419     strlist.append("        layout = self.layout\n")
420
421     for name in namelist:
422         strlist.append("        layout.operator(\"mesh." + str.lower(name) + "\", text=\"" + name + "\")\n")
423     strlist.append("\n")
424     strlist.append("\n")
425     strlist.append("def menu_item(self, context):\n")
426     strlist.append("    self.layout.menu(\"VIEW3D_MT_mesh_" + str.lower(groupname) + "_add\", icon=\"PLUGIN\")\n")
427     strlist.append("\n")
428     strlist.append("\n")
429     strlist.append("def register():\n")
430     strlist.append("    bpy.utils.register_module(__name__)\n")
431     strlist.append("    bpy.types.VIEW3D_MT_mesh_add.append(menu_item)\n")
432     strlist.append("\n")
433     strlist.append("\n")
434     strlist.append("def unregister():\n")
435     strlist.append("    bpy.utils.unregister_module(__name__)\n")
436     strlist.append("    bpy.types.VIEW3D_MT_mesh_add.remove(menu_item)\n")
437     strlist.append("\n")
438     strlist.append("\n")
439     strlist.append("if __name__ == \"__main__\":\n")
440     strlist.append("    register()\n")
441     endstring = ''.join(strlist)
442     txt.write(endstring)
443
444     try:
445         fileobj = open(addondir + "__init__.py", "w")
446     except:
447         message = "Permission problem - cant write file - run Blender as Administrator!"
448         bpy.ops.creaprim.message('INVOKE_DEFAULT')
449         return 0
450
451     fileobj.write(endstring)
452     fileobj.close()
453
454
455 def setname(dummy):
456
457     scn = bpy.context.scene
458     oldname = scn.Creaprim_Name
459
460     if bpy.context.active_object and bpy.context.active_object.name != oldname:
461
462         scn.Creaprim_Name = bpy.context.active_object.name