[Edit Linked Library] Removed no-longer-relevant comment
[blender-addons-contrib.git] / mesh_discombobulator.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 #  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.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18  
19 bl_info = {
20     "name": "Discombobulator",
21     "description": "Its job is to easily add scifi details to a surface to create nice-looking space-ships or futuristic cities.",
22     "author": "Evan J. Rosky (syrux), Chichiri, Jace Priester",
23     "version": (0,2),
24     "blender": (2, 64, 0),
25     "location": "Spacebar > Discombobulate",
26     "warning": 'Beta',
27     'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts',
28     'tracker_url': 'https://projects.blender.org/tracker/index.php?'\
29                    'func=detail&aid=31390',
30     "category": "Mesh"}
31  
32 import bpy
33 import random
34 import mathutils
35 import math
36 from mathutils import *
37
38 doprots = True
39  
40 # Datas in which we will build the new discombobulated mesh
41 nPolygons = []
42 nVerts = []
43 Verts = []
44 Polygons = []
45 dVerts = []
46 dPolygons = []
47 i_prots = [] # index of the top polygons on whow we will generate the doodads
48 i_dood_type = [] # type of doodad (given by index of the doodad obj)
49  
50 bpy.types.Scene.DISC_doodads = []
51  
52 def randnum(a, b):
53     return random.random()*(b-a)+a
54  
55 def randVertex(a, b, c, d, Verts):
56     """Return a vector of a random vertex on a quad-polygon"""
57     i = random.randint(1,2)
58     A, B, C, D = 0, 0, 0, 0
59     if(a==1):
60         A, B, C, D = a, b, c, d
61     else:
62         A, B, C, D = a, d, c, b
63    
64     i = randnum(0.1, 0.9)
65    
66
67     vecAB=Verts[B]-Verts[A]
68     E=Verts[A]+vecAB*i
69    
70     vecDC=Verts[C]-Verts[D]
71     F=Verts[D]+vecDC*i
72    
73     i = randnum(0.1, 0.9)
74     vecEF=F-E
75     
76     O=E+vecEF*i
77     return O
78  
79 ################################ Protusions ###################################
80  
81 def fill_older_datas(verts, polygon):
82     """ Specifically coded to be called by the function addProtusionToPolygon, its sets up a tuple which contains the vertices from the base and the top of the protusions. """
83     temp_vertices = []  
84     temp_vertices.append(verts[polygon[0]].copy())
85     temp_vertices.append(verts[polygon[1]].copy())
86     temp_vertices.append(verts[polygon[2]].copy())
87     temp_vertices.append(verts[polygon[3]].copy())
88     temp_vertices.append(verts[polygon[0]].copy())
89     temp_vertices.append(verts[polygon[1]].copy())
90     temp_vertices.append(verts[polygon[2]].copy())
91     temp_vertices.append(verts[polygon[3]].copy())
92     return temp_vertices
93    
94 def extrude_top(temp_vertices, normal, height):
95     """ This function extrude the polygon composed of the four first members of the tuple temp_vertices along the normal multiplied by the height of the extrusion."""
96     j = 0
97     while j < 3:  
98         temp_vertices[0][j]+=normal[j]*height
99         temp_vertices[1][j]+=normal[j]*height
100         temp_vertices[2][j]+=normal[j]*height
101         temp_vertices[3][j]+=normal[j]*height
102         j+=1
103  
104 def scale_top(temp_vertices, center, normal, height, scale_ratio):
105     """ This function scale the polygon composed of the four first members of the tuple temp_vertices. """
106     vec1 = [0, 0, 0]
107     vec2 = [0, 0, 0]
108     vec3 = [0, 0, 0]
109     vec4 = [0, 0, 0]
110    
111     j = 0
112     while j < 3:
113         center[j]+=normal[j]*height
114         vec1[j] = temp_vertices[0][j] - center[j]
115         vec2[j] = temp_vertices[1][j] - center[j]
116         vec3[j] = temp_vertices[2][j] - center[j]
117         vec4[j] = temp_vertices[3][j] - center[j]
118         temp_vertices[0][j] = center[j] + vec1[j]*(1-scale_ratio)
119         temp_vertices[1][j] = center[j] + vec2[j]*(1-scale_ratio)
120         temp_vertices[2][j] = center[j] + vec3[j]*(1-scale_ratio)
121         temp_vertices[3][j] = center[j] + vec4[j]*(1-scale_ratio)
122         j+=1
123  
124 def add_prot_polygons(temp_vertices):
125     """ Specifically coded to be called by addProtusionToPolygon, this function put the data from the generated protusion at the end the tuples Verts and Polygons, which will later used to generate the final mesh. """
126     global Verts
127     global Polygons
128     global i_prots
129    
130     findex = len(Verts)
131     Verts+=temp_vertices
132    
133     polygontop = [findex+0, findex+1, findex+2, findex+3]
134     polygon1 = [findex+0, findex+1, findex+5, findex+4]
135     polygon2 = [findex+1, findex+2, findex+6, findex+5]
136     polygon3 = [findex+2, findex+3, findex+7, findex+6]
137     polygon4 = [findex+3, findex+0, findex+4, findex+7]
138    
139     Polygons.append(polygontop)
140     i_prots.append(len(Polygons)-1)
141     Polygons.append(polygon1)
142     Polygons.append(polygon2)
143     Polygons.append(polygon3)
144     Polygons.append(polygon4)
145        
146 def addProtusionToPolygon(obpolygon, verts, minHeight, maxHeight, minTaper, maxTaper):
147     """Create a protusion from the polygon "obpolygon" of the original object and use several values sent by the user. It calls in this order the following functions:
148        - fill_older_data;
149        - extrude_top;
150        - scale_top;
151        - add_prot_polygons;
152    """
153     # some useful variables
154     polygon = obpolygon.vertices
155     polygontop = polygon
156     polygon1 = []
157     polygon2 = []
158     polygon3 = []
159     polygon4 = []
160     vertices = []
161     tVerts = list(fill_older_datas(verts, polygon)) # list of temp vertices
162     height = randnum(minHeight, maxHeight) # height of generated protusion
163     scale_ratio = randnum(minTaper, maxTaper)
164    
165     # extrude the top polygon
166     extrude_top(tVerts, obpolygon.normal, height)
167     # Now, we scale, the top polygon along its normal
168     scale_top(tVerts, GetPolyCentroid(obpolygon,verts), obpolygon.normal, height, scale_ratio)
169     # Finally, we add the protusions to the list of polygons
170     add_prot_polygons(tVerts)
171  
172 ################################## Divide a polygon ##################################
173  
174 def divide_one(list_polygons, list_vertices, verts, polygon, findex):
175     """ called by divide_polygon, to generate a polygon from one polygon, maybe I could simplify this process """
176     temp_vertices = []
177     temp_vertices.append(verts[polygon[0]].copy())
178     temp_vertices.append(verts[polygon[1]].copy())
179     temp_vertices.append(verts[polygon[2]].copy())
180     temp_vertices.append(verts[polygon[3]].copy())
181    
182     list_vertices+=temp_vertices
183        
184     list_polygons.append([findex+0, findex+1, findex+2, findex+3])
185  
186 def divide_two(list_polygons, list_vertices, verts, polygon, findex):
187     """ called by divide_polygon, to generate two polygons from one polygon and add them to the list of polygons and vertices which form the discombobulated mesh"""
188     temp_vertices = []
189     temp_vertices.append(verts[polygon[0]].copy())
190     temp_vertices.append(verts[polygon[1]].copy())
191     temp_vertices.append(verts[polygon[2]].copy())
192     temp_vertices.append(verts[polygon[3]].copy())
193     temp_vertices.append((verts[polygon[0]]+verts[polygon[1]])/2)
194     temp_vertices.append((verts[polygon[2]]+verts[polygon[3]])/2)
195        
196     list_vertices+=temp_vertices
197        
198     list_polygons.append([findex+0, findex+4, findex+5, findex+3])
199     list_polygons.append([findex+1, findex+2, findex+5, findex+4])
200
201 def divide_three(list_polygons, list_vertices, verts, polygon, findex, center):
202     """ called by divide_polygon, to generate three polygons from one polygon and add them to the list of polygons and vertices which form the discombobulated mesh"""
203     temp_vertices = []
204     temp_vertices.append(verts[polygon[0]].copy())
205     temp_vertices.append(verts[polygon[1]].copy())
206     temp_vertices.append(verts[polygon[2]].copy())
207     temp_vertices.append(verts[polygon[3]].copy())
208     temp_vertices.append((verts[polygon[0]]+verts[polygon[1]])/2)
209     temp_vertices.append((verts[polygon[2]]+verts[polygon[3]])/2)
210     temp_vertices.append((verts[polygon[1]]+verts[polygon[2]])/2)
211     temp_vertices.append(center.copy())
212        
213     list_vertices+=temp_vertices
214        
215     list_polygons.append([findex+0, findex+4, findex+5, findex+3])
216     list_polygons.append([findex+1, findex+6, findex+7, findex+4])
217     list_polygons.append([findex+6, findex+2, findex+5, findex+7])
218   
219 def divide_four(list_polygons, list_vertices, verts, polygon, findex, center):
220     """ called by divide_polygon, to generate four polygons from one polygon and add them to the list of polygons and vertices which form the discombobulated mesh"""
221     temp_vertices = []
222     temp_vertices.append(verts[polygon[0]].copy())
223     temp_vertices.append(verts[polygon[1]].copy())
224     temp_vertices.append(verts[polygon[2]].copy())
225     temp_vertices.append(verts[polygon[3]].copy())
226     temp_vertices.append((verts[polygon[0]]+verts[polygon[1]])/2)
227     temp_vertices.append((verts[polygon[2]]+verts[polygon[3]])/2)
228     temp_vertices.append((verts[polygon[1]]+verts[polygon[2]])/2)
229     temp_vertices.append(center.copy())
230     temp_vertices.append((verts[polygon[0]]+verts[polygon[3]])/2)
231     temp_vertices.append(center.copy())
232    
233     list_vertices+=temp_vertices
234        
235     list_polygons.append([findex+0, findex+4, findex+7, findex+8])
236     list_polygons.append([findex+1, findex+6, findex+7, findex+4])
237     list_polygons.append([findex+6, findex+2, findex+5, findex+7])
238     list_polygons.append([findex+8, findex+7, findex+5, findex+3])
239    
240 def dividepolygon(obpolygon, verts, number):
241     """Divide the poly into the wanted number of polygons"""
242     global nPolygons
243     global nVerts
244    
245     poly = obpolygon.vertices
246     tVerts = []
247    
248     if(number==1):
249         divide_one(nPolygons, nVerts, verts, poly, len(nVerts))
250     elif(number==2):
251         divide_two(nPolygons, nVerts, verts, poly, len(nVerts))
252     elif(number==3):
253         divide_three(nPolygons, nVerts, verts, poly, len(nVerts), GetPolyCentroid(obpolygon,verts))
254     elif(number==4):
255         divide_four(nPolygons, nVerts, verts, poly, len(nVerts), GetPolyCentroid(obpolygon,verts))
256    
257 ############################### Discombobulate ################################
258
259 def GetPolyCentroid(obpolygon,allvertcoords):
260     centroid=mathutils.Vector((0,0,0))
261     for vindex in obpolygon.vertices:
262         centroid+=mathutils.Vector(allvertcoords[vindex])
263     centroid/=len(obpolygon.vertices)
264     return centroid
265  
266 def division(obpolygons, verts, sf1, sf2, sf3, sf4):
267     """Function to divide each of the selected polygons"""
268     divide = []
269     if(sf1): divide.append(1)
270     if(sf2): divide.append(2)
271     if(sf3): divide.append(3)
272     if(sf4): divide.append(4)
273     for poly in obpolygons:
274         if(poly.select == True and len(poly.vertices)==4):
275             a = random.randint(0, len(divide)-1)
276             dividepolygon(poly, verts, divide[a])
277  
278 def protusion(obverts, obpolygons, minHeight, maxHeight, minTaper, maxTaper):
279     """function to generate the protusions"""
280     verts = []
281     for vertex in obverts:
282         verts.append(vertex.co)
283            
284     for polygon in obpolygons:
285         if(polygon.select == True):
286             if(len(polygon.vertices) == 4):
287                 addProtusionToPolygon(polygon, verts, minHeight, maxHeight, minTaper, maxTaper)
288  
289 def test_v2_near_v1(v1, v2):
290     if(v1.x - 0.1 <= v2.x <= v1.x + 0.1
291         and v1.y - 0.1 <= v2.y <= v1.y + 0.1
292         and v1.z - 0.1 <= v2.z <= v1.z + 0.1):
293         return True
294    
295     return False
296  
297 def angle_between_nor(nor_orig, nor_result):
298     angle = math.acos(nor_orig.dot(nor_result))
299     axis = nor_orig.cross(nor_result).normalized()
300    
301     q = mathutils.Quaternion()
302     q.x = axis.x*math.sin(angle/2)
303     q.y = axis.y*math.sin(angle/2)
304     q.z = axis.z*math.sin(angle/2)
305     q.w = math.cos(angle/2)
306    
307     return q
308  
309 def doodads(object1, mesh1, dmin, dmax):
310     """function to generate the doodads"""
311     global dVerts
312     global dPolygons
313     i = 0
314     # on parcoure cette boucle pour ajouter des doodads a toutes les polygons
315     # english translation: this loops adds doodads to all polygons
316     while(i<len(object1.data.polygons)):
317         if object1.data.polygons[i].select==False:
318             continue
319         doods_nbr = random.randint(dmin, dmax)
320         j = 0
321         while(j<=doods_nbr):
322             origin_dood = randVertex(object1.data.polygons[i].vertices[0], object1.data.polygons[i].vertices[1], object1.data.polygons[i].vertices[2], object1.data.polygons[i].vertices[3], Verts)
323             type_dood = random.randint(0, len(bpy.context.scene.DISC_doodads)-1)
324             polygons_add = []
325             verts_add = []
326            
327             # First we have to apply scaling and rotation to the mesh
328             bpy.ops.object.select_pattern(pattern=bpy.context.scene.DISC_doodads[type_dood],extend=False)
329             bpy.context.scene.objects.active=bpy.data.objects[bpy.context.scene.DISC_doodads[type_dood]]
330             bpy.ops.object.transform_apply(rotation=True, scale=True)
331            
332             for polygon in bpy.data.objects[bpy.context.scene.DISC_doodads[type_dood]].data.polygons:
333                 polygons_add.append(polygon.vertices)
334             for vertex in bpy.data.objects[bpy.context.scene.DISC_doodads[type_dood]].data.vertices:
335                 verts_add.append(vertex.co.copy())
336             normal_original_polygon = object1.data.polygons[i].normal
337            
338             nor_def = mathutils.Vector((0.0, 0.0, 1.0))
339             qr = nor_def.rotation_difference(normal_original_polygon.normalized())
340            
341             case_z = False
342             if(test_v2_near_v1(nor_def, -normal_original_polygon)):
343                 case_z = True
344                 qr = mathutils.Quaternion((0.0, 0.0, 0.0, 0.0))
345             #qr = angle_between_nor(nor_def, normal_original_polygon)
346             for vertex in verts_add:
347                 vertex.rotate(qr)
348                 vertex+=origin_dood
349             findex = len(dVerts)
350             for polygon in polygons_add:
351                 dPolygons.append([polygon[0]+findex, polygon[1]+findex, polygon[2]+findex, polygon[3]+findex])
352                 i_dood_type.append(bpy.data.objects[bpy.context.scene.DISC_doodads[type_dood]].name)
353             for vertex in verts_add:
354                 dVerts.append(vertex)
355             j+=1
356         i+=5
357        
358 def protusions_repeat(object1, mesh1, r_prot):
359
360         for j in i_prots:
361             if j<len(object1.data.polygons):
362                 object1.data.polygons[j].select=True
363             else:
364                 print("Warning: hit end of polygons in object1")
365  
366 # add material to discombobulated mesh
367 def setMatProt(discObj, origObj, sideProtMat, topProtMat):
368     # First we put the materials in their slots
369     bpy.ops.object.select_pattern(pattern = discObj.name,extend=False)
370     bpy.context.scene.objects.active=bpy.data.objects[discObj.name]
371     try:
372         origObj.material_slots[topProtMat]
373         origObj.material_slots[sideProtMat]
374     except:
375         return
376         
377     bpy.ops.object.material_slot_add()
378     bpy.ops.object.material_slot_add()
379     discObj.material_slots[0].material = origObj.material_slots[topProtMat].material
380     discObj.material_slots[1].material = origObj.material_slots[sideProtMat].material
381    
382     # Then we assign materials to protusions
383     for polygon in discObj.data.polygons:
384         if polygon.index in i_prots:
385             polygon.material_index = 0
386         else:
387             polygon.material_index = 1
388  
389 def setMatDood(doodObj):
390     # First we add the materials slots
391     bpy.ops.object.select_pattern(pattern = doodObj.name,extend=False)
392     bpy.context.scene.objects.active=doodObj
393     for name in bpy.context.scene.DISC_doodads:
394         try:
395             bpy.ops.object.material_slot_add()
396             doodObj.material_slots[-1].material = bpy.data.objects[name].material_slots[0].material
397             for polygon in doodObj.data.polygons:
398                 if i_dood_type[polygon.index] == name:
399                     polygon.material_index = len(doodObj.material_slots)-1
400         except:
401             print()
402            
403            
404 def clean_doodads():
405     current_doodads=list(bpy.context.scene.DISC_doodads)
406     
407     for name in current_doodads:
408         if name not in bpy.data.objects:
409             bpy.context.scene.DISC_doodads.remove(name)
410             
411
412 def discombobulate(minHeight, maxHeight, minTaper, maxTaper, sf1, sf2, sf3, sf4, dmin, dmax, r_prot, sideProtMat, topProtMat, isLast):
413     global doprots
414     global nVerts
415     global nPolygons
416     global Verts
417     global Polygons
418     global dVerts
419     global dPolygons
420     global i_prots
421     
422    
423     bpy.ops.object.mode_set(mode="OBJECT")
424     
425     
426     #start by cleaning up doodads that don't exist anymore
427     clean_doodads()
428     
429     
430     # Create the discombobulated mesh
431     mesh = bpy.data.meshes.new("tmp")
432     object = bpy.data.objects.new("tmp", mesh)
433     bpy.context.scene.objects.link(object)
434    
435     # init final verts and polygons tuple
436     nPolygons = []
437     nVerts = []
438     Polygons = []
439     Verts = []
440     dPolygons = []
441     dVerts = []
442    
443     origObj = bpy.context.active_object
444    
445     # There we collect the rotation, translation and scaling datas from the original mesh
446     to_translate = bpy.context.active_object.location
447     to_scale     = bpy.context.active_object.scale
448     to_rotate    = bpy.context.active_object.rotation_euler
449    
450     # First, we collect all the informations we will need from the previous mesh        
451     obverts = bpy.context.active_object.data.vertices
452     obpolygons = bpy.context.active_object.data.polygons
453     verts = []
454     for vertex in obverts:
455         verts.append(vertex.co)
456    
457     division(obpolygons, verts, sf1, sf2, sf3, sf4)
458        
459     # Fill in the discombobulated mesh with the new polygons
460     mesh.from_pydata(nVerts, [], nPolygons)
461     mesh.update(calc_edges = True)
462    
463     # Reload the datas
464     bpy.ops.object.select_all(action="DESELECT")
465     bpy.ops.object.select_pattern(pattern = object.name,extend=False)
466     bpy.context.scene.objects.active=bpy.data.objects[object.name]
467     obverts = bpy.context.active_object.data.vertices
468     obpolygons = bpy.context.active_object.data.polygons
469    
470     protusion(obverts, obpolygons, minHeight, maxHeight, minTaper, maxTaper)
471    
472     # Fill in the discombobulated mesh with the new polygons
473     mesh1 = bpy.data.meshes.new("discombobulated_object")
474     object1 = bpy.data.objects.new("discombobulated_mesh", mesh1)
475     bpy.context.scene.objects.link(object1)
476     mesh1.from_pydata(Verts, [], Polygons)
477     mesh1.update(calc_edges = True)
478    
479    
480     # Set the material's of discombobulated object
481     setMatProt(object1, origObj, sideProtMat, topProtMat)
482    
483     bpy.ops.object.select_pattern(pattern = object1.name,extend=False)
484     bpy.context.scene.objects.active=bpy.data.objects[object1.name]
485     bpy.ops.object.mode_set(mode='EDIT')
486     bpy.ops.mesh.normals_make_consistent(inside=False)
487     bpy.ops.mesh.select_all(action='DESELECT')
488     bpy.ops.object.mode_set(mode='OBJECT')
489    
490     #if(bpy.context.scene.repeatprot):
491     protusions_repeat(object1, mesh1, r_prot)
492    
493     if(len(bpy.context.scene.DISC_doodads) != 0 and bpy.context.scene.dodoodads and isLast):
494         doodads(object1, mesh1, dmin, dmax)
495         mesh2 = bpy.data.meshes.new("dood_mesh")
496         object2 = bpy.data.objects.new("dood_obj", mesh2)
497         bpy.context.scene.objects.link(object2)
498         mesh2.from_pydata(dVerts, [], dPolygons)
499         mesh2.update(calc_edges = True)
500         setMatDood(object2)
501         object2.location        = to_translate
502         object2.rotation_euler  = to_rotate
503         object2.scale           = to_scale
504  
505     bpy.ops.object.select_pattern(pattern = object.name,extend=False)
506     bpy.context.scene.objects.active=bpy.data.objects[object.name]
507     bpy.ops.object.delete()
508     
509     bpy.ops.object.select_pattern(pattern=object1.name,extend=False)
510     bpy.context.scene.objects.active=bpy.data.objects[object1.name]
511     bpy.context.scene.update()
512    
513     # translate, scale and rotate discombobulated results
514     object1.location        = to_translate
515     object1.rotation_euler  = to_rotate
516     object1.scale           = to_scale
517     
518     #set all polys to selected. this allows recursive discombobulating.
519     for poly in mesh1.polygons:
520         poly.select=True
521  
522 ############ Operator to select and deslect an object as a doodad ###############
523  
524 class chooseDoodad(bpy.types.Operator):
525     bl_idname = "object.discombobulate_set_doodad"
526     bl_label = "Discombobulate set doodad object"
527    
528     def execute(self, context):
529         bpy.context.scene.DISC_doodads.append(bpy.context.active_object.name)
530        
531     def invoke(self, context, event):
532         self.execute(context)
533         return {'FINISHED'}
534  
535 class unchooseDoodad(bpy.types.Operator):
536     bl_idname = "object.discombobulate_unset_doodad"
537     bl_label = "Discombobulate unset doodad object"
538    
539     def execute(self, context):
540         for name in bpy.context.scene.DISC_doodads:
541             if name == bpy.context.active_object.name:
542                 bpy.context.scene.DISC_doodads.remove(name)
543                
544     def invoke(self, context, event):
545         self.execute(context)
546         return {'FINISHED'}
547  
548 ################################## Interpolygon ####################################
549  
550 class discombobulator(bpy.types.Operator):
551     bl_idname = "object.discombobulate"
552     bl_label = "Discombobulate"
553     bl_options = {'REGISTER', 'UNDO'}  
554    
555     def execute(self, context):
556         scn = context.scene
557         i=0
558         while i<scn.repeatprot:
559             isLast=False
560             if i==scn.repeatprot-1:
561                 isLast=True
562             discombobulate(scn.minHeight, scn.maxHeight, scn.minTaper, scn.maxTaper, scn.subpolygon1, scn.subpolygon2, scn.subpolygon3, scn.subpolygon4, scn.mindoodads, scn.maxdoodads, scn.repeatprot, scn.sideProtMat, scn.topProtMat, isLast)
563             i+=1
564         return {'FINISHED'}
565
566 class discombob_help(bpy.types.Operator):
567         bl_idname = 'help.discombobulator'
568         bl_label = ''
569
570         def draw(self, context):
571                 layout = self.layout
572                 layout.label('To use:')
573                 layout.label('Works with Quads only not Ngons.')
574                 layout.label('Select a face or faces')
575                 layout.label('Press Discombobulate to create greebles')
576
577
578         
579         def execute(self, context):
580                 return {'FINISHED'}
581
582         def invoke(self, context, event):
583                 return context.window_manager.invoke_popup(self, width = 300)
584                 
585 class VIEW3D_PT_tools_discombobulate(bpy.types.Panel):
586     bl_space_type = 'VIEW_3D'
587     bl_region_type = 'TOOLS'
588     bl_label = "Discombobulator"
589     bl_context = "objectmode"
590     bl_options = {'DEFAULT_CLOSED'}
591         
592     def draw(self, context):
593         layout = self.layout
594         row = layout.row()
595         row = layout.split(0.80)
596         row.operator('object.discombobulate', text = 'Discombobulate', icon = 'PLUGIN')
597         row.operator('help.discombobulator', icon = 'INFO')
598         box = layout.box()
599         box.label("Protusions settings")
600         row = box.row()
601         row.prop(context.scene, 'doprots')
602         row = box.row()
603         row.prop(context.scene, 'minHeight')
604         row = box.row()
605         row.prop(context.scene, 'maxHeight')
606         row = box.row()
607         row.prop(context.scene, 'minTaper')
608         row = box.row()
609         row.prop(context.scene, 'maxTaper')
610         row = box.row()
611         col1 = row.column(align = True)
612         col1.prop(context.scene, "subpolygon1")
613         col2 = row.column(align = True)
614         col2.prop(context.scene, "subpolygon2")
615         col3 = row.column(align = True)
616         col3.prop(context.scene, "subpolygon3")
617         col4 = row.column(align = True)
618         col4.prop(context.scene, "subpolygon4")
619         row = box.row()
620         row.prop(context.scene, "repeatprot")
621         box = layout.box()
622         box.label("Doodads settings")
623         row = box.row()
624         row.prop(context.scene, 'dodoodads')
625         row = box.row()
626         row.prop(context.scene, "mindoodads")
627         row = box.row()
628         row.prop(context.scene, "maxdoodads")
629         row = box.row()
630         row.operator("object.discombobulate_set_doodad", text = "Pick doodad")
631         row = box.row()
632         row.operator("object.discombobulate_unset_doodad", text = "Remove doodad")
633         col = box.column(align = True)
634         for name in bpy.context.scene.DISC_doodads:
635             col.label(text = name)
636         box = layout.box()
637         box.label("Materials settings")
638         row = box.row()
639         row.prop(context.scene, 'topProtMat')
640         row = box.row()
641         row.prop(context.scene, "sideProtMat")
642         row = box.row()
643            
644 # registering and menu integration
645 def register():
646     # Protusions Buttons:
647     bpy.types.Scene.repeatprot = bpy.props.IntProperty(name="Repeat protusions", description="make several layers of protusion", default = 1, min = 1, max = 10)
648     bpy.types.Scene.doprots = bpy.props.BoolProperty(name="Make protusions", description = "Check if we want to add protusions to the mesh", default = True)
649     bpy.types.Scene.polygonschangedpercent = bpy.props.FloatProperty(name="Polygon %", description = "Percentage of changed polygons", default = 1.0)
650     bpy.types.Scene.minHeight = bpy.props.FloatProperty(name="Min height", description="Minimal height of the protusions", default=0.2)
651     bpy.types.Scene.maxHeight = bpy.props.FloatProperty(name="Max height", description="Maximal height of the protusions", default = 0.4)
652     bpy.types.Scene.minTaper = bpy.props.FloatProperty(name="Min taper", description="Minimal height of the protusions", default=0.15, min = 0.0, max = 1.0, subtype = 'PERCENTAGE')
653     bpy.types.Scene.maxTaper = bpy.props.FloatProperty(name="Max taper", description="Maximal height of the protusions", default = 0.35, min = 0.0, max = 1.0, subtype = 'PERCENTAGE')
654     bpy.types.Scene.subpolygon1 = bpy.props.BoolProperty(name="1", default = True)
655     bpy.types.Scene.subpolygon2 = bpy.props.BoolProperty(name="2", default = True)
656     bpy.types.Scene.subpolygon3 = bpy.props.BoolProperty(name="3", default = True)
657     bpy.types.Scene.subpolygon4 = bpy.props.BoolProperty(name="4", default = True)
658    
659     # Doodads buttons:
660     bpy.types.Scene.dodoodads = bpy.props.BoolProperty(name="Make doodads", description = "Check if we want to generate doodads", default = True)
661     bpy.types.Scene.mindoodads = bpy.props.IntProperty(name="Minimum doodads number", description = "Ask for the minimum number of doodads to generate per polygon", default = 1, min = 0, max = 50)
662     bpy.types.Scene.maxdoodads = bpy.props.IntProperty(name="Maximum doodads number", description = "Ask for the maximum number of doodads to generate per polygon", default = 6, min = 1, max = 50)
663     bpy.types.Scene.doodMinScale = bpy.props.FloatProperty(name="Scale min", description="Minimum scaling of doodad", default = 0.5, min = 0.0, max = 1.0, subtype = 'PERCENTAGE')
664     bpy.types.Scene.doodMaxScale = bpy.props.FloatProperty(name="Scale max", description="Maximum scaling of doodad", default = 1.0, min = 0.0, max = 1.0, subtype = 'PERCENTAGE')
665    
666     # Materials buttons:
667     bpy.types.Scene.sideProtMat = bpy.props.IntProperty(name="Side's prot mat", description = "Material of protusion's sides", default = 0, min = 0)
668     bpy.types.Scene.topProtMat = bpy.props.IntProperty(name = "Prot's top mat", description = "Material of protusion's top", default = 0, min = 0)
669    
670     bpy.utils.register_class(discombobulator)
671     bpy.utils.register_class(chooseDoodad)
672     bpy.utils.register_class(unchooseDoodad)
673     bpy.utils.register_class(VIEW3D_PT_tools_discombobulate)
674     bpy.utils.register_class(discombob_help)
675  
676 # unregistering and removing menus
677 def unregister():
678     bpy.utils.unregister_class(discombobulator)
679     bpy.utils.unregister_class(chooseDoodad)
680     bpy.utils.unregister_class(unchooseDoodad)
681     bpy.utils.unregister_class(VIEW3D_PT_tools_discombobulate)
682     bpy.utils.unregister_class(discombob_help)
683  
684 if __name__ == "__main__":
685     register()