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