Fix T52833: OBJ triangulate doesn't match viewport
[blender-addons.git] / add_advanced_objects_menu / add_mesh_aggregate.py
blob6072cb9c447c5b972c9e25ad403d816c5fd3df50
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 # Simple aggregate of particles / meshes
20 # Copy the selected objects on the active object
21 # Based on the position of the cursor and a defined volume
22 # Allows to control growth by using a Build modifier
24 bl_info = {
25 "name": "Aggregate Mesh",
26 "author": "liero",
27 "version": (0, 0, 5),
28 "blender": (2, 7, 0),
29 "location": "View3D > Tool Shelf",
30 "description": "Adds geometry to a mesh like in DLA aggregators",
31 "category": "Object"}
34 import bpy
35 import bmesh
36 from random import (
37 choice,
38 gauss,
39 seed,
41 from mathutils import Matrix
42 from bpy.props import (
43 BoolProperty,
44 FloatProperty,
45 IntProperty,
47 from bpy.types import Operator
50 def use_random_seed(self):
51 seed(self.rSeed)
52 return
55 def rg(n):
56 return (round(gauss(0, n), 2))
59 def remover(sel=False):
60 bpy.ops.object.editmode_toggle()
61 if sel:
62 bpy.ops.mesh.select_all(action='SELECT')
63 bpy.ops.mesh.remove_doubles(threshold=0.0001)
64 bpy.ops.object.mode_set()
67 class OBJECT_OT_agregate_mesh(Operator):
68 bl_idname = "object.agregate_mesh"
69 bl_label = "Aggregate"
70 bl_description = ("Adds geometry to a mesh like in DLA aggregators\n"
71 "Needs at least two selected Mesh objects")
72 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
74 volX = FloatProperty(
75 name="Volume X",
76 min=0.1, max=25,
77 default=3,
78 description="The cloud around cursor"
80 volY = FloatProperty(
81 name="Volume Y",
82 min=0.1, max=25,
83 default=3,
84 description="The cloud around cursor"
86 volZ = FloatProperty(
87 name="Volume Z",
88 min=0.1, max=25,
89 default=3,
90 description="The cloud around cursor"
92 baseSca = FloatProperty(
93 name="Scale",
94 min=0.01, max=5,
95 default=.25,
96 description="Particle Scale"
98 varSca = FloatProperty(
99 name="Var",
100 min=0, max=1,
101 default=0,
102 description="Particle Scale Variation"
104 rotX = FloatProperty(
105 name="Rot Var X",
106 min=0, max=2,
107 default=0,
108 description="X Rotation Variation"
110 rotY = FloatProperty(
111 name="Rot Var Y",
112 min=0, max=2,
113 default=0,
114 description="Y Rotation Variation"
116 rotZ = FloatProperty(
117 name="Rot Var Z",
118 min=0, max=2,
119 default=1,
120 description="Z Rotation Variation"
122 rSeed = IntProperty(
123 name="Random seed",
124 min=0, max=999999,
125 default=1,
126 description="Seed to feed random values"
128 numP = IntProperty(
129 name="Number",
130 min=1,
131 max=9999, soft_max=500,
132 default=50,
133 description="Number of particles"
135 nor = BoolProperty(
136 name="Normal Oriented",
137 default=False,
138 description="Align Z axis with Faces normals"
140 cent = BoolProperty(
141 name="Use Face Center",
142 default=False,
143 description="Center on Faces"
145 track = BoolProperty(
146 name="Cursor Follows",
147 default=False,
148 description="Cursor moves as structure grows / more compact results"
150 anim = BoolProperty(
151 name="Animatable",
152 default=False,
153 description="Sort faces so you can regrow with Build Modifier, materials are lost"
155 refresh = BoolProperty(
156 name="Update",
157 default=False
159 auto_refresh = BoolProperty(
160 name="Auto",
161 description="Auto update spline",
162 default=False
165 def draw(self, context):
166 layout = self.layout
167 col = layout.column(align=True)
168 row = col.row(align=True)
170 if self.auto_refresh is False:
171 self.refresh = False
172 elif self.auto_refresh is True:
173 self.refresh = True
175 row.prop(self, "auto_refresh", toggle=True, icon="AUTO")
176 row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH")
178 col = layout.column(align=True)
179 col.separator()
181 col = layout.column(align=True)
182 col.prop(self, "volX", slider=True)
183 col.prop(self, "volY", slider=True)
184 col.prop(self, "volZ", slider=True)
186 layout.label(text="Particles:")
187 col = layout.column(align=True)
188 col.prop(self, "baseSca", slider=True)
189 col.prop(self, "varSca", slider=True)
191 col = layout.column(align=True)
192 col.prop(self, "rotX", slider=True)
193 col.prop(self, "rotY", slider=True)
194 col.prop(self, "rotZ", slider=True)
196 col = layout.column(align=True)
197 col.prop(self, "rSeed", slider=False)
198 col.prop(self, "numP")
200 row = layout.row(align=True)
201 row.prop(self, "nor")
202 row.prop(self, "cent")
204 row = layout.row(align=True)
205 row.prop(self, "track")
206 row.prop(self, "anim")
208 @classmethod
209 def poll(cls, context):
210 return (len(bpy.context.selected_objects) > 1 and
211 bpy.context.object.type == 'MESH')
213 def invoke(self, context, event):
214 self.refresh = True
215 return self.execute(context)
217 def execute(self, context):
218 if not self.refresh:
219 return {'PASS_THROUGH'}
221 scn = bpy.context.scene
222 obj = bpy.context.active_object
224 use_random_seed(self)
226 mat = Matrix((
227 (1, 0, 0, 0),
228 (0, 1, 0, 0),
229 (0, 0, 1, 0),
230 (0, 0, 0, 1))
232 if obj.matrix_world != mat:
233 self.report({'WARNING'},
234 "Please, Apply transformations to Active Object first")
235 return{'FINISHED'}
237 par = [o for o in bpy.context.selected_objects if o.type == 'MESH' and o != obj]
238 if not par:
239 return{'FINISHED'}
241 bpy.ops.object.mode_set()
242 bpy.ops.object.select_all(action='DESELECT')
243 obj.select = True
244 msv = []
246 for i in range(len(obj.modifiers)):
247 msv.append(obj.modifiers[i].show_viewport)
248 obj.modifiers[i].show_viewport = False
250 cur = scn.cursor_location
251 for i in range(self.numP):
253 mes = choice(par).data
254 newobj = bpy.data.objects.new('nuevo', mes)
255 scn.objects.link(newobj)
256 origen = (rg(self.volX) + cur[0], rg(self.volY) + cur[1], rg(self.volZ) + cur[2])
258 cpom = obj.closest_point_on_mesh(origen)
260 if self.cent:
261 bm = bmesh.new()
262 bm.from_mesh(obj.data)
263 if hasattr(bm.verts, "ensure_lookup_table"):
264 bm.verts.ensure_lookup_table()
265 bm.faces.ensure_lookup_table()
267 newobj.location = bm.faces[cpom[3]].calc_center_median()
269 bm.free()
270 else:
271 newobj.location = cpom[1]
273 if self.nor:
274 newobj.rotation_mode = 'QUATERNION'
275 newobj.rotation_quaternion = cpom[1].to_track_quat('Z', 'Y')
276 newobj.rotation_mode = 'XYZ'
277 newobj.rotation_euler[0] += rg(self.rotX)
278 newobj.rotation_euler[1] += rg(self.rotY)
279 newobj.rotation_euler[2] += rg(self.rotZ)
280 else:
281 newobj.rotation_euler = (rg(self.rotX), rg(self.rotY), rg(self.rotZ))
283 newobj.scale = [self.baseSca + self.baseSca * rg(self.varSca)] * 3
285 if self.anim:
286 newobj.select = True
287 bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', obdata=True)
288 bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
290 bme = bmesh.new()
291 bme.from_mesh(obj.data)
293 tmp = bmesh.new()
294 tmp.from_mesh(newobj.data)
296 for f in tmp.faces:
297 # z = len(bme.verts)
298 for v in f.verts:
299 bme.verts.new(list(v.co))
300 bme.faces.new(bme.verts[-len(f.verts):])
302 bme.to_mesh(obj.data)
303 remover(True)
304 # Note: foo.user_clear() is deprecated use do_unlink=True instead
305 bpy.data.meshes.remove(newobj.data, do_unlink=True)
307 else:
308 scn.objects.active = obj
309 newobj.select = True
310 bpy.ops.object.join()
312 if self.track:
313 cur = scn.cursor_location = cpom[1]
315 for i in range(len(msv)):
316 obj.modifiers[i].show_viewport = msv[i]
318 for o in par:
319 o.select = True
321 obj.select = True
323 if self.auto_refresh is False:
324 self.refresh = False
326 return{'FINISHED'}
329 def register():
330 bpy.utils.register_class(OBJECT_OT_agregate_mesh)
333 def unregister():
334 bpy.utils.unregister_class(OBJECT_OT_agregate_mesh)
337 if __name__ == '__main__':
338 register()