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
25 "name": "Aggregate Mesh",
29 "location": "View3D > Tool Shelf",
30 "description": "Adds geometry to a mesh like in DLA aggregators",
41 from mathutils
import Matrix
42 from bpy
.props
import (
47 from bpy
.types
import Operator
50 def use_random_seed(self
):
56 return (round(gauss(0, n
), 2))
59 def remover(sel
=False):
60 bpy
.ops
.object.editmode_toggle()
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'}
78 description
="The cloud around cursor"
84 description
="The cloud around cursor"
90 description
="The cloud around cursor"
92 baseSca
= FloatProperty(
96 description
="Particle Scale"
98 varSca
= FloatProperty(
102 description
="Particle Scale Variation"
104 rotX
= FloatProperty(
108 description
="X Rotation Variation"
110 rotY
= FloatProperty(
114 description
="Y Rotation Variation"
116 rotZ
= FloatProperty(
120 description
="Z Rotation Variation"
126 description
="Seed to feed random values"
131 max=9999, soft_max
=500,
133 description
="Number of particles"
136 name
="Normal Oriented",
138 description
="Align Z axis with Faces normals"
141 name
="Use Face Center",
143 description
="Center on Faces"
145 track
= BoolProperty(
146 name
="Cursor Follows",
148 description
="Cursor moves as structure grows / more compact results"
153 description
="Sort faces so you can regrow with Build Modifier, materials are lost"
155 refresh
= BoolProperty(
159 auto_refresh
= BoolProperty(
161 description
="Auto update spline",
165 def draw(self
, context
):
167 col
= layout
.column(align
=True)
168 row
= col
.row(align
=True)
170 if self
.auto_refresh
is False:
172 elif self
.auto_refresh
is 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)
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")
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
):
215 return self
.execute(context
)
217 def execute(self
, context
):
219 return {'PASS_THROUGH'}
221 scn
= bpy
.context
.scene
222 obj
= bpy
.context
.active_object
224 use_random_seed(self
)
232 if obj
.matrix_world
!= mat
:
233 self
.report({'WARNING'},
234 "Please, Apply transformations to Active Object first")
237 par
= [o
for o
in bpy
.context
.selected_objects
if o
.type == 'MESH' and o
!= obj
]
241 bpy
.ops
.object.mode_set()
242 bpy
.ops
.object.select_all(action
='DESELECT')
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
)
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()
271 newobj
.location
= cpom
[1]
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
)
281 newobj
.rotation_euler
= (rg(self
.rotX
), rg(self
.rotY
), rg(self
.rotZ
))
283 newobj
.scale
= [self
.baseSca
+ self
.baseSca
* rg(self
.varSca
)] * 3
287 bpy
.ops
.object.make_single_user(type='SELECTED_OBJECTS', obdata
=True)
288 bpy
.ops
.object.transform_apply(location
=True, rotation
=True, scale
=True)
291 bme
.from_mesh(obj
.data
)
294 tmp
.from_mesh(newobj
.data
)
299 bme
.verts
.new(list(v
.co
))
300 bme
.faces
.new(bme
.verts
[-len(f
.verts
):])
302 bme
.to_mesh(obj
.data
)
304 # Note: foo.user_clear() is deprecated use do_unlink=True instead
305 bpy
.data
.meshes
.remove(newobj
.data
, do_unlink
=True)
308 scn
.objects
.active
= obj
310 bpy
.ops
.object.join()
313 cur
= scn
.cursor_location
= cpom
[1]
315 for i
in range(len(msv
)):
316 obj
.modifiers
[i
].show_viewport
= msv
[i
]
323 if self
.auto_refresh
is False:
330 bpy
.utils
.register_class(OBJECT_OT_agregate_mesh
)
334 bpy
.utils
.unregister_class(OBJECT_OT_agregate_mesh
)
337 if __name__
== '__main__':