Cleanup: quiet strict name warnings for addons a..h.
[blender-addons.git] / add_mesh_extra_objects / mesh_discombobulator.py
blobb677fc6cc5de5a38db64bd4e7a3db5e01f9da525
1 # GPL # Original Authors: Evan J. Rosky (syrux), Chichiri, Jace Priester #
3 import bpy
4 import random
5 import math
6 from bpy.types import (
7 Operator,
8 Menu,
10 from mathutils import (
11 Vector,
12 Quaternion,
15 # ################### Globals #################### #
17 doprots = True
19 # Datas in which we will build the new discombobulated mesh
20 nPolygons = []
21 nVerts = []
22 Verts = []
23 Polygons = []
24 dVerts = []
25 dPolygons = []
26 i_prots = [] # index of the top polygons on which we'll generate the doodads
27 i_dood_type = [] # type of doodad (given by index of the doodad obj)
30 # ############### Utility Functions ############### #
32 def randnum(a, b):
33 return random.random() * (b - a) + a
36 def randVertex(a, b, c, d, Verts):
37 """Return a vector of a random vertex on a quad-polygon"""
38 i = random.randint(1, 2)
39 A, B, C, D = 0, 0, 0, 0
40 if(a == 1):
41 A, B, C, D = a, b, c, d
42 else:
43 A, B, C, D = a, d, c, b
45 i = randnum(0.1, 0.9)
47 vecAB = Verts[B] - Verts[A]
48 E = Verts[A] + vecAB * i
50 vecDC = Verts[C] - Verts[D]
51 F = Verts[D] + vecDC * i
53 i = randnum(0.1, 0.9)
54 vecEF = F - E
56 O = E + vecEF * i
57 return O
60 # ################## Protusions #################### #
62 def fill_older_datas(verts, polygon):
63 """ Specifically coded to be called by the function addProtusionToPolygon,
64 its sets up a tuple which contains the vertices from the base and the top of the protusions.
65 """
66 temp_vertices = []
67 temp_vertices.append(verts[polygon[0]].copy())
68 temp_vertices.append(verts[polygon[1]].copy())
69 temp_vertices.append(verts[polygon[2]].copy())
70 temp_vertices.append(verts[polygon[3]].copy())
71 temp_vertices.append(verts[polygon[0]].copy())
72 temp_vertices.append(verts[polygon[1]].copy())
73 temp_vertices.append(verts[polygon[2]].copy())
74 temp_vertices.append(verts[polygon[3]].copy())
75 return temp_vertices
78 def extrude_top(temp_vertices, normal, height):
79 """ This function extrude the polygon composed of the four first members of the tuple
80 temp_vertices along the normal multiplied by the height of the extrusion.
81 """
82 j = 0
83 while j < 3:
84 temp_vertices[0][j] += normal[j] * height
85 temp_vertices[1][j] += normal[j] * height
86 temp_vertices[2][j] += normal[j] * height
87 temp_vertices[3][j] += normal[j] * height
88 j += 1
91 def scale_top(temp_vertices, center, normal, height, scale_ratio):
92 """ This function scale the polygon composed of the four first members of the tuple temp_vertices. """
93 vec1 = [0, 0, 0]
94 vec2 = [0, 0, 0]
95 vec3 = [0, 0, 0]
96 vec4 = [0, 0, 0]
98 j = 0
99 while j < 3:
100 center[j] += normal[j] * height
101 vec1[j] = temp_vertices[0][j] - center[j]
102 vec2[j] = temp_vertices[1][j] - center[j]
103 vec3[j] = temp_vertices[2][j] - center[j]
104 vec4[j] = temp_vertices[3][j] - center[j]
105 temp_vertices[0][j] = center[j] + vec1[j] * (1 - scale_ratio)
106 temp_vertices[1][j] = center[j] + vec2[j] * (1 - scale_ratio)
107 temp_vertices[2][j] = center[j] + vec3[j] * (1 - scale_ratio)
108 temp_vertices[3][j] = center[j] + vec4[j] * (1 - scale_ratio)
109 j += 1
112 def add_prot_polygons(temp_vertices):
113 """ Specifically coded to be called by addProtusionToPolygon, this function
114 put the data from the generated protusion at the end the tuples Verts and Polygons,
115 which will later used to generate the final mesh.
117 global Verts
118 global Polygons
119 global i_prots
121 findex = len(Verts)
122 Verts += temp_vertices
124 polygontop = [findex + 0, findex + 1, findex + 2, findex + 3]
125 polygon1 = [findex + 0, findex + 1, findex + 5, findex + 4]
126 polygon2 = [findex + 1, findex + 2, findex + 6, findex + 5]
127 polygon3 = [findex + 2, findex + 3, findex + 7, findex + 6]
128 polygon4 = [findex + 3, findex + 0, findex + 4, findex + 7]
130 Polygons.append(polygontop)
131 i_prots.append(len(Polygons) - 1)
132 Polygons.append(polygon1)
133 Polygons.append(polygon2)
134 Polygons.append(polygon3)
135 Polygons.append(polygon4)
138 def addProtusionToPolygon(obpolygon, verts, minHeight, maxHeight, minTaper, maxTaper):
139 """Create a protusion from the polygon "obpolygon" of the original object and use
140 several values sent by the user. It calls in this order the following functions:
141 - fill_older_data;
142 - extrude_top;
143 - scale_top;
144 - add_prot_polygons;
146 # some useful variables
147 polygon = obpolygon.vertices
149 tVerts = list(fill_older_datas(verts, polygon)) # list of temp vertices
150 height = randnum(minHeight, maxHeight) # height of generated protusion
151 scale_ratio = randnum(minTaper, maxTaper)
153 # extrude the top polygon
154 extrude_top(tVerts, obpolygon.normal, height)
155 # Now, we scale, the top polygon along its normal
156 scale_top(tVerts, GetPolyCentroid(obpolygon, verts), obpolygon.normal, height, scale_ratio)
157 # Finally, we add the protusions to the list of polygons
158 add_prot_polygons(tVerts)
161 # ################# Divide a polygon ############### #
163 def divide_one(list_polygons, list_vertices, verts, polygon, findex):
164 """ called by divide_polygon, to generate a polygon from one polygon, maybe I could simplify this process """
165 temp_vertices = []
166 temp_vertices.append(verts[polygon[0]].copy())
167 temp_vertices.append(verts[polygon[1]].copy())
168 temp_vertices.append(verts[polygon[2]].copy())
169 temp_vertices.append(verts[polygon[3]].copy())
171 list_vertices += temp_vertices
173 list_polygons.append([findex + 0, findex + 1, findex + 2, findex + 3])
176 def divide_two(list_polygons, list_vertices, verts, polygon, findex):
177 """ called by divide_polygon, to generate two polygons from one polygon and
178 add them to the list of polygons and vertices which form the discombobulated mesh
180 temp_vertices = []
181 temp_vertices.append(verts[polygon[0]].copy())
182 temp_vertices.append(verts[polygon[1]].copy())
183 temp_vertices.append(verts[polygon[2]].copy())
184 temp_vertices.append(verts[polygon[3]].copy())
185 temp_vertices.append((verts[polygon[0]] + verts[polygon[1]]) / 2)
186 temp_vertices.append((verts[polygon[2]] + verts[polygon[3]]) / 2)
188 list_vertices += temp_vertices
190 list_polygons.append([findex + 0, findex + 4, findex + 5, findex + 3])
191 list_polygons.append([findex + 1, findex + 2, findex + 5, findex + 4])
194 def divide_three(list_polygons, list_vertices, verts, polygon, findex, center):
195 """ called by divide_polygon, to generate three polygons from one polygon and
196 add them to the list of polygons and vertices which form the discombobulated mesh
198 temp_vertices = []
199 temp_vertices.append(verts[polygon[0]].copy())
200 temp_vertices.append(verts[polygon[1]].copy())
201 temp_vertices.append(verts[polygon[2]].copy())
202 temp_vertices.append(verts[polygon[3]].copy())
203 temp_vertices.append((verts[polygon[0]] + verts[polygon[1]]) / 2)
204 temp_vertices.append((verts[polygon[2]] + verts[polygon[3]]) / 2)
205 temp_vertices.append((verts[polygon[1]] + verts[polygon[2]]) / 2)
206 temp_vertices.append(center.copy())
208 list_vertices += temp_vertices
210 list_polygons.append([findex + 0, findex + 4, findex + 5, findex + 3])
211 list_polygons.append([findex + 1, findex + 6, findex + 7, findex + 4])
212 list_polygons.append([findex + 6, findex + 2, findex + 5, findex + 7])
215 def divide_four(list_polygons, list_vertices, verts, polygon, findex, center):
216 """ called by divide_polygon, to generate four polygons from one polygon and
217 add them to the list of polygons and vertices which form the discombobulated mesh
219 temp_vertices = []
220 temp_vertices.append(verts[polygon[0]].copy())
221 temp_vertices.append(verts[polygon[1]].copy())
222 temp_vertices.append(verts[polygon[2]].copy())
223 temp_vertices.append(verts[polygon[3]].copy())
224 temp_vertices.append((verts[polygon[0]] + verts[polygon[1]]) / 2)
225 temp_vertices.append((verts[polygon[2]] + verts[polygon[3]]) / 2)
226 temp_vertices.append((verts[polygon[1]] + verts[polygon[2]]) / 2)
227 temp_vertices.append(center.copy())
228 temp_vertices.append((verts[polygon[0]] + verts[polygon[3]]) / 2)
229 temp_vertices.append(center.copy())
231 list_vertices += temp_vertices
233 list_polygons.append([findex + 0, findex + 4, findex + 7, findex + 8])
234 list_polygons.append([findex + 1, findex + 6, findex + 7, findex + 4])
235 list_polygons.append([findex + 6, findex + 2, findex + 5, findex + 7])
236 list_polygons.append([findex + 8, findex + 7, findex + 5, findex + 3])
239 def dividepolygon(obpolygon, verts, number):
240 """Divide the poly into the wanted number of polygons"""
241 global nPolygons
242 global nVerts
244 poly = obpolygon.vertices
246 if(number == 1):
247 divide_one(nPolygons, nVerts, verts, poly, len(nVerts))
248 elif(number == 2):
249 divide_two(nPolygons, nVerts, verts, poly, len(nVerts))
250 elif(number == 3):
251 divide_three(nPolygons, nVerts, verts, poly, len(nVerts), GetPolyCentroid(obpolygon, verts))
252 elif(number == 4):
253 divide_four(nPolygons, nVerts, verts, poly, len(nVerts), GetPolyCentroid(obpolygon, verts))
256 # ################## Discombobulate ################ #
258 def GetPolyCentroid(obpolygon, allvertcoords):
259 centroid = Vector((0, 0, 0))
260 for vindex in obpolygon.vertices:
261 centroid += Vector(allvertcoords[vindex])
262 centroid /= len(obpolygon.vertices)
263 return centroid
266 def division(obpolygons, verts, sf1, sf2, sf3, sf4):
267 """Function to divide each of the selected polygons"""
268 divide = []
269 if (sf1):
270 divide.append(1)
271 if (sf2):
272 divide.append(2)
273 if (sf3):
274 divide.append(3)
275 if (sf4):
276 divide.append(4)
278 for poly in obpolygons:
279 if(poly.select is True and len(poly.vertices) == 4):
280 a = random.randint(0, len(divide) - 1)
281 dividepolygon(poly, verts, divide[a])
284 def protusion(obverts, obpolygons, minHeight, maxHeight, minTaper, maxTaper):
285 """function to generate the protusions"""
286 verts = []
287 for vertex in obverts:
288 verts.append(vertex.co)
290 for polygon in obpolygons:
291 if(polygon.select is True):
292 if(len(polygon.vertices) == 4):
293 addProtusionToPolygon(polygon, verts, minHeight, maxHeight, minTaper, maxTaper)
296 def test_v2_near_v1(v1, v2):
297 if (v1.x - 0.1 <= v2.x <= v1.x + 0.1 and
298 v1.y - 0.1 <= v2.y <= v1.y + 0.1 and
299 v1.z - 0.1 <= v2.z <= v1.z + 0.1):
300 return True
302 return False
305 def angle_between_nor(nor_orig, nor_result):
306 angle = math.acos(nor_orig.dot(nor_result))
307 axis = nor_orig.cross(nor_result).normalized()
309 q = Quaternion()
310 q.x = axis.x * math.sin(angle / 2)
311 q.y = axis.y * math.sin(angle / 2)
312 q.z = axis.z * math.sin(angle / 2)
313 q.w = math.cos(angle / 2)
315 return q
318 def doodads(object1, mesh1, dmin, dmax):
319 """function to generate the doodads"""
320 global dVerts
321 global dPolygons
322 i = 0
323 # on parcoure cette boucle pour ajouter des doodads a toutes les polygons
324 # english translation: this loops adds doodads to all polygons
325 while(i < len(object1.data.polygons)):
326 if object1.data.polygons[i].select is False:
327 continue
329 doods_nbr = random.randint(dmin, dmax)
330 j = 0
332 while(j <= doods_nbr):
333 origin_dood = randVertex(object1.data.polygons[i].vertices[0], object1.data.polygons[i].vertices[1],
334 object1.data.polygons[i].vertices[2], object1.data.polygons[i].vertices[3], Verts)
335 type_dood = random.randint(0, len(bpy.context.scene.discomb.DISC_doodads) - 1)
336 polygons_add = []
337 verts_add = []
339 # First we have to apply scaling and rotation to the mesh
340 bpy.ops.object.select_pattern(pattern=bpy.context.scene.discomb.DISC_doodads[type_dood], extend=False)
341 bpy.context.scene.objects.active = bpy.data.objects[bpy.context.scene.discomb.DISC_doodads[type_dood]]
342 bpy.ops.object.transform_apply(rotation=True, scale=True)
344 for polygon in bpy.data.objects[bpy.context.scene.discomb.DISC_doodads[type_dood]].data.polygons:
345 polygons_add.append(polygon.vertices)
346 for vertex in bpy.data.objects[bpy.context.scene.discomb.DISC_doodads[type_dood]].data.vertices:
347 verts_add.append(vertex.co.copy())
348 normal_original_polygon = object1.data.polygons[i].normal
350 nor_def = Vector((0.0, 0.0, 1.0))
351 qr = nor_def.rotation_difference(normal_original_polygon.normalized())
353 if(test_v2_near_v1(nor_def, -normal_original_polygon)):
354 qr = Quaternion((0.0, 0.0, 0.0, 0.0))
356 # qr = angle_between_nor(nor_def, normal_original_polygon)
357 for vertex in verts_add:
358 vertex.rotate(qr)
359 vertex += origin_dood
360 findex = len(dVerts)
362 for polygon in polygons_add:
363 dPolygons.append([polygon[0] + findex, polygon[1] + findex, polygon[2] + findex, polygon[3] + findex])
364 i_dood_type.append(bpy.data.objects[bpy.context.scene.discomb.DISC_doodads[type_dood]].name)
366 for vertex in verts_add:
367 dVerts.append(vertex)
368 j += 1
369 i += 5
372 def protusions_repeat(object1, mesh1, r_prot):
374 for j in i_prots:
375 if j < len(object1.data.polygons):
376 object1.data.polygons[j].select = True
377 else:
378 print("Warning: hit end of polygons in object1")
381 # add material to discombobulated mesh
382 def setMatProt(discObj, origObj, sideProtMat, topProtMat):
383 # First we put the materials in their slots
384 bpy.ops.object.select_pattern(pattern=discObj.name, extend=False)
385 bpy.context.scene.objects.active = bpy.data.objects[discObj.name]
386 try:
387 origObj.material_slots[topProtMat]
388 origObj.material_slots[sideProtMat]
389 except:
390 return
392 bpy.ops.object.material_slot_add()
393 bpy.ops.object.material_slot_add()
394 discObj.material_slots[0].material = origObj.material_slots[topProtMat].material
395 discObj.material_slots[1].material = origObj.material_slots[sideProtMat].material
397 # Then we assign materials to protusions
398 for polygon in discObj.data.polygons:
399 if polygon.index in i_prots:
400 polygon.material_index = 0
401 else:
402 polygon.material_index = 1
405 def setMatDood(doodObj):
406 # First we add the materials slots
407 bpy.ops.object.select_pattern(pattern=doodObj.name, extend=False)
408 bpy.context.scene.objects.active = doodObj
409 for name in bpy.context.scene.discomb.DISC_doodads:
410 try:
411 bpy.ops.object.material_slot_add()
412 doodObj.material_slots[-1].material = bpy.data.objects[name].material_slots[0].material
413 for polygon in doodObj.data.polygons:
414 if i_dood_type[polygon.index] == name:
415 polygon.material_index = len(doodObj.material_slots) - 1
416 except:
417 print()
420 def clean_doodads():
421 current_doodads = list(bpy.context.scene.discomb.DISC_doodads)
423 for name in current_doodads:
424 if name not in bpy.data.objects:
425 bpy.context.scene.discomb.DISC_doodads.remove(name)
428 def discombobulate(minHeight, maxHeight, minTaper, maxTaper, sf1, sf2, sf3, sf4,
429 dmin, dmax, r_prot, sideProtMat, topProtMat, isLast):
430 global doprots
431 global nVerts
432 global nPolygons
433 global Verts
434 global Polygons
435 global dVerts
436 global dPolygons
437 global i_prots
439 bpy.ops.object.mode_set(mode="OBJECT")
441 # start by cleaning up doodads that don't exist anymore
442 clean_doodads()
444 # Create the discombobulated mesh
445 mesh = bpy.data.meshes.new("tmp")
446 object = bpy.data.objects.new("tmp", mesh)
447 bpy.context.scene.objects.link(object)
449 # init final verts and polygons tuple
450 nPolygons = []
451 nVerts = []
452 Polygons = []
453 Verts = []
454 dPolygons = []
455 dVerts = []
457 origObj = bpy.context.active_object
459 # There we collect the rotation, translation and scaling datas from the original mesh
460 to_translate = bpy.context.active_object.location
461 to_scale = bpy.context.active_object.scale
462 to_rotate = bpy.context.active_object.rotation_euler
464 # First, we collect all the informations we will need from the previous mesh
465 obverts = bpy.context.active_object.data.vertices
466 obpolygons = bpy.context.active_object.data.polygons
467 verts = []
468 for vertex in obverts:
469 verts.append(vertex.co)
471 division(obpolygons, verts, sf1, sf2, sf3, sf4)
473 # Fill in the discombobulated mesh with the new polygons
474 mesh.from_pydata(nVerts, [], nPolygons)
475 mesh.update(calc_edges=True)
477 # Reload the datas
478 bpy.ops.object.select_all(action="DESELECT")
479 bpy.ops.object.select_pattern(pattern=object.name, extend=False)
480 bpy.context.scene.objects.active = bpy.data.objects[object.name]
481 obverts = bpy.context.active_object.data.vertices
482 obpolygons = bpy.context.active_object.data.polygons
484 protusion(obverts, obpolygons, minHeight, maxHeight, minTaper, maxTaper)
486 # Fill in the discombobulated mesh with the new polygons
487 mesh1 = bpy.data.meshes.new("discombobulated_object")
488 object1 = bpy.data.objects.new("discombobulated_mesh", mesh1)
489 bpy.context.scene.objects.link(object1)
490 mesh1.from_pydata(Verts, [], Polygons)
491 mesh1.update(calc_edges=True)
493 # Set the material's of discombobulated object
494 setMatProt(object1, origObj, sideProtMat, topProtMat)
496 bpy.ops.object.select_pattern(pattern=object1.name, extend=False)
497 bpy.context.scene.objects.active = bpy.data.objects[object1.name]
498 bpy.ops.object.mode_set(mode='EDIT')
499 bpy.ops.mesh.normals_make_consistent(inside=False)
500 bpy.ops.mesh.select_all(action='DESELECT')
501 bpy.ops.object.mode_set(mode='OBJECT')
503 # if(bpy.context.scene.repeatprot):
504 protusions_repeat(object1, mesh1, r_prot)
506 if(len(bpy.context.scene.discomb.DISC_doodads) != 0 and bpy.context.scene.discomb.dodoodads and isLast):
507 doodads(object1, mesh1, dmin, dmax)
508 mesh2 = bpy.data.meshes.new("dood_mesh")
509 object2 = bpy.data.objects.new("dood_obj", mesh2)
510 bpy.context.scene.objects.link(object2)
511 mesh2.from_pydata(dVerts, [], dPolygons)
512 mesh2.update(calc_edges=True)
513 setMatDood(object2)
514 object2.location = to_translate
515 object2.rotation_euler = to_rotate
516 object2.scale = to_scale
518 bpy.ops.object.select_pattern(pattern=object.name, extend=False)
519 bpy.context.scene.objects.active = bpy.data.objects[object.name]
520 bpy.ops.object.delete()
522 bpy.ops.object.select_pattern(pattern=object1.name, extend=False)
523 bpy.context.scene.objects.active = bpy.data.objects[object1.name]
524 bpy.context.scene.update()
526 # translate, scale and rotate discombobulated results
527 object1.location = to_translate
528 object1.rotation_euler = to_rotate
529 object1.scale = to_scale
531 # set all polys to selected. this allows recursive discombobulating.
532 for poly in mesh1.polygons:
533 poly.select = True
536 # ### Operators for selecting and deselecting an object as a doodad ### #
538 class chooseDoodad(Operator):
539 bl_idname = "object.discombobulate_set_doodad"
540 bl_label = "Discombobulate set doodad object"
541 bl_description = ("Save the Active Object as Doodad \n"
542 "Object has to be quads only")
543 bl_options = {'REGISTER'}
545 @classmethod
546 def poll(cls, context):
547 obj = bpy.context.active_object
548 if (obj is not None and obj.type == "MESH"):
549 mesh = obj.data
551 for polygon in mesh.polygons:
552 is_ok = len(polygon.vertices)
553 if is_ok != 4:
554 return False
555 return True
557 return False
559 def execute(self, context):
560 obj_name = bpy.context.active_object.name
561 msg = "Object with this name already saved"
563 if obj_name not in bpy.context.scene.discomb.DISC_doodads:
564 bpy.context.scene.discomb.DISC_doodads.append(obj_name)
565 msg = "Saved Doodad object: {}".format(obj_name)
567 self.report({'INFO'}, message=msg)
569 def invoke(self, context, event):
570 self.execute(context)
571 return {'FINISHED'}
574 class unchooseDoodad(Operator):
575 bl_idname = "object.discombobulate_unset_doodad"
576 bl_label = "Discombobulate unset doodad object"
577 bl_description = "Remove the saved Doodad Object(s)"
578 bl_options = {'REGISTER'}
580 remove_all = bpy.props.BoolProperty(
581 name="Remove all Doodads",
582 default=False,
585 def execute(self, context):
586 msg = ("No doodads to remove")
587 doodadery = bpy.context.scene.discomb.DISC_doodads
588 if len(doodadery) > 0:
589 if not self.remove_all:
590 name = bpy.context.active_object.name
591 if name in doodadery:
592 bpy.context.scene.discomb.DISC_doodads.remove(name)
593 msg = ("Removed Doodad object: {}".format(name))
594 else:
595 bpy.context.scene.discomb.DISC_doodads[:] = []
596 msg = "Removed all Doodads"
597 else:
598 msg = "No Doodads to Remove"
600 self.report({'INFO'}, message=msg)
602 def invoke(self, context, event):
603 self.execute(context)
604 return {'FINISHED'}
607 # ################## Interpolygon ################## #
609 class discombobulator(Operator):
610 bl_idname = "object.discombobulate"
611 bl_label = "Discombobulate"
612 bl_description = "Apply"
613 bl_options = {'REGISTER', 'UNDO'}
615 def execute(self, context):
616 scn = context.scene.discomb
617 i = 0
618 while i < bpy.context.scene.discomb.repeatprot:
619 isLast = False
620 if i == scn.repeatprot - 1:
621 isLast = True
622 discombobulate(scn.minHeight, scn.maxHeight, scn.minTaper, scn.maxTaper, scn.subpolygon1,
623 scn.subpolygon2, scn.subpolygon3, scn.subpolygon4, scn.mindoodads, scn.maxdoodads,
624 scn.repeatprot, scn.sideProtMat, scn.topProtMat, isLast)
625 i += 1
626 return {'FINISHED'}
629 class discombobulator_dodads_list(Menu):
630 bl_idname = "OBJECT_MT_discombobulator_dodad_list"
631 bl_label = "List of saved Doodads"
632 bl_description = "List of the saved Doodad Object Names"
633 bl_options = {'REGISTER'}
635 def draw(self, context):
636 layout = self.layout
638 doodle = len(bpy.context.scene.discomb.DISC_doodads)
639 layout.label("Saved doodads : {}".format(doodle))
640 layout.separator()
641 if doodle > 0:
642 for name in bpy.context.scene.discomb.DISC_doodads:
643 layout.label(text=name)
646 class discombob_help(Menu):
647 bl_idname = "HELP_MT_discombobulator"
648 bl_label = "Usage Information"
649 bl_description = "Help"
650 bl_options = {'REGISTER'}
652 def draw(self, context):
653 layout = self.layout
654 layout.label(text="Usage Information:", icon="INFO")
655 layout.separator()
656 layout.label(text="Quads only, not Triangles or Ngons", icon="ERROR")
657 layout.label("Works only with Mesh object that have faces")
658 layout.separator()
659 layout.label("Select a face or faces")
660 layout.label("Press Discombobulate to create greebles")
661 layout.label("In object mode, still needs a selection in Edit Mode")
662 layout.separator()
663 layout.label("Doodads - additional objects layered on the mesh surface")
664 layout.label("(Similar to dupliverts - but as one separate object)")
665 layout.separator()
666 layout.label(text="Limitations:", icon="MOD_EXPLODE")
667 layout.label("Be careful with the repeat protusions setting")
668 layout.label("(Runs reqursively)")
669 layout.label("If possible, avoid using on a high polycount base mesh")
670 layout.label("(It can run out of memory and take a long time to compute)")
673 class VIEW3D_OT_tools_discombobulate(Operator):
674 bl_idname = "discombobulate.ops"
675 bl_label = "Discombobulator"
676 bl_description = ("Easily add sci-fi details to a surface \n"
677 "Needs an existing active Mesh with Faces")
678 bl_options = {'REGISTER'}
680 executing = False
682 @classmethod
683 def poll(cls, context):
684 return (context.active_object is not None and
685 context.active_object.type == "MESH")
687 def draw(self, context):
688 layout = self.layout
690 row = layout.row()
691 row.menu('HELP_MT_discombobulator', icon='INFO')
692 box = layout.box()
693 box.label("Protusions settings")
694 row = box.row()
695 row.prop(context.scene.discomb, 'doprots')
696 row = box.row()
697 row.prop(context.scene.discomb, 'minHeight')
698 row = box.row()
699 row.prop(context.scene.discomb, 'maxHeight')
700 row = box.row()
701 row.prop(context.scene.discomb, 'minTaper')
702 row = box.row()
703 row.prop(context.scene.discomb, 'maxTaper')
704 row = box.row()
705 col1 = row.column(align=True)
706 col1.prop(context.scene.discomb, "subpolygon1")
707 col2 = row.column(align=True)
708 col2.prop(context.scene.discomb, "subpolygon2")
709 col3 = row.column(align=True)
710 col3.prop(context.scene.discomb, "subpolygon3")
711 col4 = row.column(align=True)
712 col4.prop(context.scene.discomb, "subpolygon4")
713 row = box.row()
714 row.prop(context.scene.discomb, "repeatprot")
715 box = layout.box()
716 box.label("Doodads settings")
717 row = box.row()
718 is_doodad = context.scene.discomb.dodoodads
719 row.prop(context.scene.discomb, 'dodoodads')
721 row = box.row()
722 row.enabled = is_doodad
723 row.prop(context.scene.discomb, "mindoodads")
724 row = box.row()
725 row.enabled = is_doodad
726 row.prop(context.scene.discomb, "maxdoodads")
727 row = box.row()
728 row.enabled = is_doodad
729 row.operator("object.discombobulate_set_doodad", text="Pick doodad")
731 row = box.row()
732 splits = row.split(0.5)
733 splits.enabled = is_doodad
734 splits.operator("object.discombobulate_unset_doodad",
735 text="Remove active doodad").remove_all = False
736 splits.operator("object.discombobulate_unset_doodad",
737 text="Remove all doodads").remove_all = True
739 col = box.column(align=True)
740 doodle = len(bpy.context.scene.discomb.DISC_doodads)
742 col.enabled = (True if doodle > 0 else False)
743 col.menu("OBJECT_MT_discombobulator_dodad_list",
744 text="List of saved Doodads ({})".format(doodle))
746 box = layout.box()
747 box.label("Materials settings")
748 row = box.row()
749 row.prop(context.scene.discomb, 'topProtMat')
750 row = box.row()
751 row.prop(context.scene.discomb, "sideProtMat")
753 def invoke(self, context, event):
754 return context.window_manager.invoke_props_dialog(self, width=300)
756 def check(self, context):
757 return not self.executing
759 def execute(self, context):
760 self.executing = True
761 bpy.ops.object.discombobulate('INVOKE_DEFAULT')
763 return {'FINISHED'}