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
26 "name": "MExtrude Plus1",
27 "author": "liero, Jimmy Hazevoet",
29 "blender": (2, 77, 0),
30 "location": "View3D > Tool Shelf",
31 "description": "Repeat extrusions from faces to create organic shapes",
40 from bpy
.types
import Operator
41 from random
import gauss
42 from math
import radians
43 from mathutils
import (
46 from bpy
.props
import (
54 return Vector((self
.offx
, self
.offy
, self
.offz
))
58 random
.seed(self
.ran
+ r
)
59 return self
.off
* (1 + gauss(0, self
.var1
/ 3))
63 return Euler((radians(self
.nrotx
) * n
[0],
64 radians(self
.nroty
) * n
[1],
65 radians(self
.nrotz
) * n
[2]), 'XYZ')
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')
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"}
89 soft_min
=0.001, soft_max
=10,
92 description
="Translation"
96 soft_min
=-10.0, soft_max
=10.0,
97 min=-100.0, max=100.0,
99 description
="Global Translation X"
101 offy
= FloatProperty(
103 soft_min
=-10.0, soft_max
=10.0,
104 min=-100.0, max=100.0,
106 description
="Global Translation Y"
108 offz
= FloatProperty(
110 soft_min
=-10.0, soft_max
=10.0,
111 min=-100.0, max=100.0,
113 description
="Global Translation Z"
115 rotx
= FloatProperty(
118 soft_min
=-30, soft_max
=30,
120 description
="X Rotation"
122 roty
= FloatProperty(
128 description
="Y Rotation"
130 rotz
= FloatProperty(
133 soft_min
=-30, soft_max
=30,
135 description
="Z Rotation"
137 nrotx
= FloatProperty(
140 soft_min
=-30, soft_max
=30,
142 description
="Normal X Rotation"
144 nroty
= FloatProperty(
147 soft_min
=-30, soft_max
=30,
149 description
="Normal Y Rotation"
151 nrotz
= FloatProperty(
154 soft_min
=-30, soft_max
=30,
156 description
="Normal Z Rotation"
161 soft_min
=0.5, soft_max
=1.5,
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,
169 description
="Offset variation"
171 var2
= FloatProperty(
174 soft_min
=-1, soft_max
=1,
176 description
="Rotation variation"
178 var3
= FloatProperty(
181 soft_min
=-1, soft_max
=1,
183 description
="Scaling noise"
189 description
="Probability, chance of extruding a face"
196 description
="Repetitions"
202 description
="Seed to feed random values"
205 name
="Polygon coordinates",
207 description
="Polygon coordinates, Object coordinates"
210 name
="Proportional offset",
212 description
="Scale * Offset"
215 name
="Per step rotation noise",
217 description
="Per step rotation noise, Initial rotation noise"
220 name
="Per step scale noise",
222 description
="Per step scale noise, Initial scale noise"
226 def poll(cls
, context
):
228 return (obj
and obj
.type == 'MESH')
230 def draw(self
, context
):
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
269 bpy
.context
.tool_settings
.mesh_select_mode
= [False, False, True]
270 origin
= Vector([0.0, 0.0, 0.0])
273 bpy
.ops
.object.mode_set()
275 bm
.from_mesh(obj
.data
)
276 sel
= [f
for f
in bm
.faces
if f
.select
]
281 for i
, of
in enumerate(sel
):
282 nro
= nrot(self
, of
.normal
)
287 # initial rotation noise
288 if self
.opt3
is False:
290 # initial scale noise
291 if self
.opt4
is False:
295 for r
in range(self
.num
):
296 # random probability % for extrusions
297 if self
.var4
> int(random
.random() * 100):
300 no
= nf
.normal
.copy()
302 # face/obj coördinates
303 if self
.opt1
is True:
304 ce
= nf
.calc_center_bounds()
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:
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
))
350 # restore user settings
351 bpy
.ops
.object.mode_set(mode
=om
)
354 self
.report({"WARNING"},
355 "No suitable Face selection found. Operation cancelled")
362 bpy
.utils
.register_module(__name__
)
366 bpy
.utils
.unregister_module(__name__
)
369 if __name__
== '__main__':