Update for 2.8
[blender-addons.git] / mesh_extra_tools / mesh_mextrude_plus.py
blobde5f561b7854bf000bfc71306ba6e3e42ec97faa
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # Repeats extrusion + rotation + scale for one or more faces
20 # Original code by liero
21 # Update by Jimmy Hazevoet 03/2017 for Blender 2.79
22 # normal rotation, probability, scaled offset, object coords, initial and per step noise
25 bl_info = {
26 "name": "MExtrude Plus1",
27 "author": "liero, Jimmy Hazevoet",
28 "version": (1, 3, 0),
29 "blender": (2, 77, 0),
30 "location": "View3D > Tool Shelf",
31 "description": "Repeat extrusions from faces to create organic shapes",
32 "warning": "",
33 "wiki_url": "",
34 "category": "Mesh"}
37 import bpy
38 import bmesh
39 import random
40 from bpy.types import Operator
41 from random import gauss
42 from math import radians
43 from mathutils import (
44 Euler, Vector,
46 from bpy.props import (
47 FloatProperty,
48 IntProperty,
49 BoolProperty,
53 def gloc(self, r):
54 return Vector((self.offx, self.offy, self.offz))
57 def vloc(self, r):
58 random.seed(self.ran + r)
59 return self.off * (1 + gauss(0, self.var1 / 3))
62 def nrot(self, n):
63 return Euler((radians(self.nrotx) * n[0],
64 radians(self.nroty) * n[1],
65 radians(self.nrotz) * n[2]), 'XYZ')
68 def vrot(self, r):
69 random.seed(self.ran + r)
70 return Euler((radians(self.rotx) + gauss(0, self.var2 / 3),
71 radians(self.roty) + gauss(0, self.var2 / 3),
72 radians(self.rotz) + gauss(0, self.var2 / 3)), 'XYZ')
75 def vsca(self, r):
76 random.seed(self.ran + r)
77 return self.sca * (1 + gauss(0, self.var3 / 3))
80 class MExtrude(Operator):
81 bl_idname = "object.mextrude"
82 bl_label = "Multi Extrude"
83 bl_description = ("Extrude selected Faces with Rotation,\n"
84 "Scaling, Variation, Randomization")
85 bl_options = {"REGISTER", "UNDO", "PRESET"}
87 off = FloatProperty(
88 name="Offset",
89 soft_min=0.001, soft_max=10,
90 min=-100, max=100,
91 default=1.0,
92 description="Translation"
94 offx = FloatProperty(
95 name="Loc X",
96 soft_min=-10.0, soft_max=10.0,
97 min=-100.0, max=100.0,
98 default=0.0,
99 description="Global Translation X"
101 offy = FloatProperty(
102 name="Loc Y",
103 soft_min=-10.0, soft_max=10.0,
104 min=-100.0, max=100.0,
105 default=0.0,
106 description="Global Translation Y"
108 offz = FloatProperty(
109 name="Loc Z",
110 soft_min=-10.0, soft_max=10.0,
111 min=-100.0, max=100.0,
112 default=0.0,
113 description="Global Translation Z"
115 rotx = FloatProperty(
116 name="Rot X",
117 min=-85, max=85,
118 soft_min=-30, soft_max=30,
119 default=0,
120 description="X Rotation"
122 roty = FloatProperty(
123 name="Rot Y",
124 min=-85, max=85,
125 soft_min=-30,
126 soft_max=30,
127 default=0,
128 description="Y Rotation"
130 rotz = FloatProperty(
131 name="Rot Z",
132 min=-85, max=85,
133 soft_min=-30, soft_max=30,
134 default=-0,
135 description="Z Rotation"
137 nrotx = FloatProperty(
138 name="N Rot X",
139 min=-85, max=85,
140 soft_min=-30, soft_max=30,
141 default=0,
142 description="Normal X Rotation"
144 nroty = FloatProperty(
145 name="N Rot Y",
146 min=-85, max=85,
147 soft_min=-30, soft_max=30,
148 default=0,
149 description="Normal Y Rotation"
151 nrotz = FloatProperty(
152 name="N Rot Z",
153 min=-85, max=85,
154 soft_min=-30, soft_max=30,
155 default=-0,
156 description="Normal Z Rotation"
158 sca = FloatProperty(
159 name="Scale",
160 min=0.01, max=10,
161 soft_min=0.5, soft_max=1.5,
162 default=1.0,
163 description="Scaling of the selected faces after extrusion"
165 var1 = FloatProperty(
166 name="Offset Var", min=-10, max=10,
167 soft_min=-1, soft_max=1,
168 default=0,
169 description="Offset variation"
171 var2 = FloatProperty(
172 name="Rotation Var",
173 min=-10, max=10,
174 soft_min=-1, soft_max=1,
175 default=0,
176 description="Rotation variation"
178 var3 = FloatProperty(
179 name="Scale Noise",
180 min=-10, max=10,
181 soft_min=-1, soft_max=1,
182 default=0,
183 description="Scaling noise"
185 var4 = IntProperty(
186 name="Probability",
187 min=0, max=100,
188 default=100,
189 description="Probability, chance of extruding a face"
191 num = IntProperty(
192 name="Repeat",
193 min=1, max=500,
194 soft_max=100,
195 default=5,
196 description="Repetitions"
198 ran = IntProperty(
199 name="Seed",
200 min=-9999, max=9999,
201 default=0,
202 description="Seed to feed random values"
204 opt1 = BoolProperty(
205 name="Polygon coordinates",
206 default=True,
207 description="Polygon coordinates, Object coordinates"
209 opt2 = BoolProperty(
210 name="Proportional offset",
211 default=False,
212 description="Scale * Offset"
214 opt3 = BoolProperty(
215 name="Per step rotation noise",
216 default=False,
217 description="Per step rotation noise, Initial rotation noise"
219 opt4 = BoolProperty(
220 name="Per step scale noise",
221 default=False,
222 description="Per step scale noise, Initial scale noise"
225 @classmethod
226 def poll(cls, context):
227 obj = context.object
228 return (obj and obj.type == 'MESH')
230 def draw(self, context):
231 layout = self.layout
232 col = layout.column(align=True)
233 col.label(text="Transformations:")
234 col.prop(self, "off", slider=True)
235 col.prop(self, "offx", slider=True)
236 col.prop(self, "offy", slider=True)
237 col.prop(self, "offz", slider=True)
239 col = layout.column(align=True)
240 col.prop(self, "rotx", slider=True)
241 col.prop(self, "roty", slider=True)
242 col.prop(self, "rotz", slider=True)
243 col.prop(self, "nrotx", slider=True)
244 col.prop(self, "nroty", slider=True)
245 col.prop(self, "nrotz", slider=True)
246 col = layout.column(align=True)
247 col.prop(self, "sca", slider=True)
249 col = layout.column(align=True)
250 col.label(text="Variation settings:")
251 col.prop(self, "var1", slider=True)
252 col.prop(self, "var2", slider=True)
253 col.prop(self, "var3", slider=True)
254 col.prop(self, "var4", slider=True)
255 col.prop(self, "ran")
256 col = layout.column(align=False)
257 col.prop(self, 'num')
259 col = layout.column(align=True)
260 col.label(text="Options:")
261 col.prop(self, "opt1")
262 col.prop(self, "opt2")
263 col.prop(self, "opt3")
264 col.prop(self, "opt4")
266 def execute(self, context):
267 obj = bpy.context.object
268 om = obj.mode
269 bpy.context.tool_settings.mesh_select_mode = [False, False, True]
270 origin = Vector([0.0, 0.0, 0.0])
272 # bmesh operations
273 bpy.ops.object.mode_set()
274 bm = bmesh.new()
275 bm.from_mesh(obj.data)
276 sel = [f for f in bm.faces if f.select]
278 after = []
280 # faces loop
281 for i, of in enumerate(sel):
282 nro = nrot(self, of.normal)
283 off = vloc(self, i)
284 loc = gloc(self, i)
285 of.normal_update()
287 # initial rotation noise
288 if self.opt3 is False:
289 rot = vrot(self, i)
290 # initial scale noise
291 if self.opt4 is False:
292 s = vsca(self, i)
294 # extrusion loop
295 for r in range(self.num):
296 # random probability % for extrusions
297 if self.var4 > int(random.random() * 100):
298 nf = of.copy()
299 nf.normal_update()
300 no = nf.normal.copy()
302 # face/obj coördinates
303 if self.opt1 is True:
304 ce = nf.calc_center_bounds()
305 else:
306 ce = origin
308 # per step rotation noise
309 if self.opt3 is True:
310 rot = vrot(self, i + r)
311 # per step scale noise
312 if self.opt4 is True:
313 s = vsca(self, i + r)
315 # proportional, scale * offset
316 if self.opt2 is True:
317 off = s * off
319 for v in nf.verts:
320 v.co -= ce
321 v.co.rotate(nro)
322 v.co.rotate(rot)
323 v.co += ce + loc + no * off
324 v.co = v.co.lerp(ce, 1 - s)
326 # extrude code from TrumanBlending
327 for a, b in zip(of.loops, nf.loops):
328 sf = bm.faces.new((a.vert, a.link_loop_next.vert,
329 b.link_loop_next.vert, b.vert))
330 sf.normal_update()
331 bm.faces.remove(of)
332 of = nf
334 after.append(of)
336 for v in bm.verts:
337 v.select = False
338 for e in bm.edges:
339 e.select = False
341 for f in after:
342 if f not in sel:
343 f.select = True
344 else:
345 f.select = False
347 bm.to_mesh(obj.data)
348 obj.data.update()
350 # restore user settings
351 bpy.ops.object.mode_set(mode=om)
353 if not len(sel):
354 self.report({"WARNING"},
355 "No suitable Face selection found. Operation cancelled")
356 return {'CANCELLED'}
358 return {'FINISHED'}
361 def register():
362 bpy.utils.register_module(__name__)
365 def unregister():
366 bpy.utils.unregister_module(__name__)
369 if __name__ == '__main__':
370 register()