Import_3ds: Improved distance cue chunk import
[blender-addons.git] / mesh_tools / mesh_mextrude_plus.py
blobeb6fd40f439ca4f8e0a484acb9a627ee33c98b5f
1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # Repeats extrusion + rotation + scale for one or more faces
6 # Original code by liero
7 # Update by Jimmy Hazevoet 03/2017 for Blender 2.79
8 # normal rotation, probability, scaled offset, object coords, initial and per step noise
11 bl_info = {
12 "name": "MExtrude Plus1",
13 "author": "liero, Jimmy Hazevoet",
14 "version": (1, 3, 0),
15 "blender": (2, 77, 0),
16 "location": "View3D > Tool Shelf",
17 "description": "Repeat extrusions from faces to create organic shapes",
18 "warning": "",
19 "doc_url": "",
20 "category": "Mesh",
24 import bpy
25 import bmesh
26 import random
27 from bpy.types import Operator
28 from random import gauss
29 from math import radians
30 from mathutils import (
31 Euler, Vector,
33 from bpy.props import (
34 FloatProperty,
35 IntProperty,
36 BoolProperty,
40 def gloc(self, r):
41 return Vector((self.offx, self.offy, self.offz))
44 def vloc(self, r):
45 random.seed(self.ran + r)
46 return self.off * (1 + gauss(0, self.var1 / 3))
49 def nrot(self, n):
50 return Euler((radians(self.nrotx) * n[0],
51 radians(self.nroty) * n[1],
52 radians(self.nrotz) * n[2]), 'XYZ')
55 def vrot(self, r):
56 random.seed(self.ran + r)
57 return Euler((radians(self.rotx) + gauss(0, self.var2 / 3),
58 radians(self.roty) + gauss(0, self.var2 / 3),
59 radians(self.rotz) + gauss(0, self.var2 / 3)), 'XYZ')
62 def vsca(self, r):
63 random.seed(self.ran + r)
64 return self.sca * (1 + gauss(0, self.var3 / 3))
67 class MExtrude(Operator):
68 bl_idname = "object.mextrude"
69 bl_label = "Multi Extrude"
70 bl_description = ("Extrude selected Faces with Rotation,\n"
71 "Scaling, Variation, Randomization")
72 bl_options = {"REGISTER", "UNDO", "PRESET"}
74 off: FloatProperty(
75 name="Offset",
76 soft_min=0.001, soft_max=10,
77 min=-100, max=100,
78 default=1.0,
79 description="Translation"
81 offx: FloatProperty(
82 name="Loc X",
83 soft_min=-10.0, soft_max=10.0,
84 min=-100.0, max=100.0,
85 default=0.0,
86 description="Global Translation X"
88 offy: FloatProperty(
89 name="Loc Y",
90 soft_min=-10.0, soft_max=10.0,
91 min=-100.0, max=100.0,
92 default=0.0,
93 description="Global Translation Y"
95 offz: FloatProperty(
96 name="Loc Z",
97 soft_min=-10.0, soft_max=10.0,
98 min=-100.0, max=100.0,
99 default=0.0,
100 description="Global Translation Z"
102 rotx: FloatProperty(
103 name="Rot X",
104 min=-85, max=85,
105 soft_min=-30, soft_max=30,
106 default=0,
107 description="X Rotation"
109 roty: FloatProperty(
110 name="Rot Y",
111 min=-85, max=85,
112 soft_min=-30,
113 soft_max=30,
114 default=0,
115 description="Y Rotation"
117 rotz: FloatProperty(
118 name="Rot Z",
119 min=-85, max=85,
120 soft_min=-30, soft_max=30,
121 default=-0,
122 description="Z Rotation"
124 nrotx: FloatProperty(
125 name="N Rot X",
126 min=-85, max=85,
127 soft_min=-30, soft_max=30,
128 default=0,
129 description="Normal X Rotation"
131 nroty: FloatProperty(
132 name="N Rot Y",
133 min=-85, max=85,
134 soft_min=-30, soft_max=30,
135 default=0,
136 description="Normal Y Rotation"
138 nrotz: FloatProperty(
139 name="N Rot Z",
140 min=-85, max=85,
141 soft_min=-30, soft_max=30,
142 default=-0,
143 description="Normal Z Rotation"
145 sca: FloatProperty(
146 name="Scale",
147 min=0.01, max=10,
148 soft_min=0.5, soft_max=1.5,
149 default=1.0,
150 description="Scaling of the selected faces after extrusion"
152 var1: FloatProperty(
153 name="Offset Var", min=-10, max=10,
154 soft_min=-1, soft_max=1,
155 default=0,
156 description="Offset variation"
158 var2: FloatProperty(
159 name="Rotation Var",
160 min=-10, max=10,
161 soft_min=-1, soft_max=1,
162 default=0,
163 description="Rotation variation"
165 var3: FloatProperty(
166 name="Scale Noise",
167 min=-10, max=10,
168 soft_min=-1, soft_max=1,
169 default=0,
170 description="Scaling noise"
172 var4: IntProperty(
173 name="Probability",
174 min=0, max=100,
175 default=100,
176 description="Probability, chance of extruding a face"
178 num: IntProperty(
179 name="Repeat",
180 min=1, max=500,
181 soft_max=100,
182 default=5,
183 description="Repetitions"
185 ran: IntProperty(
186 name="Seed",
187 min=-9999, max=9999,
188 default=0,
189 description="Seed to feed random values"
191 opt1: BoolProperty(
192 name="Polygon coordinates",
193 default=True,
194 description="Polygon coordinates, Object coordinates"
196 opt2: BoolProperty(
197 name="Proportional offset",
198 default=False,
199 description="Scale * Offset"
201 opt3: BoolProperty(
202 name="Per step rotation noise",
203 default=False,
204 description="Per step rotation noise, Initial rotation noise"
206 opt4: BoolProperty(
207 name="Per step scale noise",
208 default=False,
209 description="Per step scale noise, Initial scale noise"
212 @classmethod
213 def poll(cls, context):
214 obj = context.object
215 return (obj and obj.type == 'MESH')
217 def draw(self, context):
218 layout = self.layout
219 col = layout.column(align=True)
220 col.label(text="Transformations:")
221 col.prop(self, "off", slider=True)
222 col.prop(self, "offx", slider=True)
223 col.prop(self, "offy", slider=True)
224 col.prop(self, "offz", slider=True)
226 col = layout.column(align=True)
227 col.prop(self, "rotx", slider=True)
228 col.prop(self, "roty", slider=True)
229 col.prop(self, "rotz", slider=True)
230 col.prop(self, "nrotx", slider=True)
231 col.prop(self, "nroty", slider=True)
232 col.prop(self, "nrotz", slider=True)
233 col = layout.column(align=True)
234 col.prop(self, "sca", slider=True)
236 col = layout.column(align=True)
237 col.label(text="Variation settings:")
238 col.prop(self, "var1", slider=True)
239 col.prop(self, "var2", slider=True)
240 col.prop(self, "var3", slider=True)
241 col.prop(self, "var4", slider=True)
242 col.prop(self, "ran")
243 col = layout.column(align=False)
244 col.prop(self, 'num')
246 col = layout.column(align=True)
247 col.label(text="Options:")
248 col.prop(self, "opt1")
249 col.prop(self, "opt2")
250 col.prop(self, "opt3")
251 col.prop(self, "opt4")
253 def execute(self, context):
254 obj = bpy.context.object
255 om = obj.mode
256 bpy.context.tool_settings.mesh_select_mode = [False, False, True]
257 origin = Vector([0.0, 0.0, 0.0])
259 # bmesh operations
260 bpy.ops.object.mode_set()
261 bm = bmesh.new()
262 bm.from_mesh(obj.data)
263 sel = [f for f in bm.faces if f.select]
265 after = []
267 # faces loop
268 for i, of in enumerate(sel):
269 nro = nrot(self, of.normal)
270 off = vloc(self, i)
271 loc = gloc(self, i)
272 of.normal_update()
274 # initial rotation noise
275 if self.opt3 is False:
276 rot = vrot(self, i)
277 # initial scale noise
278 if self.opt4 is False:
279 s = vsca(self, i)
281 # extrusion loop
282 for r in range(self.num):
283 # random probability % for extrusions
284 if self.var4 > int(random.random() * 100):
285 nf = of.copy()
286 nf.normal_update()
287 no = nf.normal.copy()
289 # face/obj coördinates
290 if self.opt1 is True:
291 ce = nf.calc_center_bounds()
292 else:
293 ce = origin
295 # per step rotation noise
296 if self.opt3 is True:
297 rot = vrot(self, i + r)
298 # per step scale noise
299 if self.opt4 is True:
300 s = vsca(self, i + r)
302 # proportional, scale * offset
303 if self.opt2 is True:
304 off = s * off
306 for v in nf.verts:
307 v.co -= ce
308 v.co.rotate(nro)
309 v.co.rotate(rot)
310 v.co += ce + loc + no * off
311 v.co = v.co.lerp(ce, 1 - s)
313 # extrude code from TrumanBlending
314 for a, b in zip(of.loops, nf.loops):
315 sf = bm.faces.new((a.vert, a.link_loop_next.vert,
316 b.link_loop_next.vert, b.vert))
317 sf.normal_update()
318 bm.faces.remove(of)
319 of = nf
321 after.append(of)
323 for v in bm.verts:
324 v.select = False
325 for e in bm.edges:
326 e.select = False
328 for f in after:
329 if f not in sel:
330 f.select = True
331 else:
332 f.select = False
334 bm.to_mesh(obj.data)
335 obj.data.update()
337 # restore user settings
338 bpy.ops.object.mode_set(mode=om)
340 if not len(sel):
341 self.report({"WARNING"},
342 "No suitable Face selection found. Operation cancelled")
343 return {'CANCELLED'}
345 return {'FINISHED'}
348 def register():
349 bpy.utils.register_module(__name__)
352 def unregister():
353 bpy.utils.unregister_module(__name__)
356 if __name__ == '__main__':
357 register()