Newer
Older
# ##### BEGIN GPL LICENSE BLOCK #####
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####
# Simple aggregate of particles / meshes
# Copy the selected objects on the active object
# Based on the position of the cursor and a defined volume
# Allows to control growth by using a Build modifier
bl_info = {
"name": "Aggregate Mesh",
"author": "liero",
"version": (0, 0, 5),
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
"location": "View3D > Tool Shelf",
"description": "Adds geometry to a mesh like in DLA aggregators",
"category": "Object"}
import bpy
import bmesh
from random import (
choice,
gauss,
seed,
)
from mathutils import Matrix
from bpy.props import (
BoolProperty,
FloatProperty,
IntProperty,
)
from bpy.types import Operator
def use_random_seed(self):
seed(self.rSeed)
return
def rg(n):
return (round(gauss(0, n), 2))
def remover(sel=False):
bpy.ops.object.editmode_toggle()
if sel:
bpy.ops.mesh.select_all(action='SELECT')
bpy.ops.mesh.remove_doubles(threshold=0.0001)
bpy.ops.object.mode_set()
class OBJECT_OT_agregate_mesh(Operator):
bl_idname = "object.agregate_mesh"
bl_label = "Aggregate"
bl_description = ("Adds geometry to a mesh like in DLA aggregators\n"
"Needs at least two selected Mesh objects")
bl_options = {'REGISTER', 'UNDO', 'PRESET'}
name="Volume X",
min=0.1, max=25,
default=3,
description="The cloud around cursor"
)
name="Volume Y",
min=0.1, max=25,
default=3,
description="The cloud around cursor"
)
name="Volume Z",
min=0.1, max=25,
default=3,
description="The cloud around cursor"
)
name="Scale",
min=0.01, max=5,
default=.25,
description="Particle Scale"
)
name="Var",
min=0, max=1,
default=0,
description="Particle Scale Variation"
)
name="Rot Var X",
min=0, max=2,
default=0,
description="X Rotation Variation"
)
name="Rot Var Y",
min=0, max=2,
default=0,
description="Y Rotation Variation"
)
name="Rot Var Z",
min=0, max=2,
default=1,
description="Z Rotation Variation"
)
name="Random seed",
min=0, max=999999,
default=1,
description="Seed to feed random values"
)
name="Number",
min=1,
max=9999, soft_max=500,
default=50,
description="Number of particles"
)
name="Normal Oriented",
default=False,
description="Align Z axis with Faces normals"
)
name="Use Face Center",
default=False,
description="Center on Faces"
)
name="Cursor Follows",
default=False,
description="Cursor moves as structure grows / more compact results"
)
name="Animatable",
default=False,
description="Sort faces so you can regrow with Build Modifier, materials are lost"
)
name="Update",
default=False
)
name="Auto",
description="Auto update spline",
default=False
)
def draw(self, context):
layout = self.layout
col = layout.column(align=True)
if self.auto_refresh is False:
self.refresh = False
elif self.auto_refresh is True:
self.refresh = True
row.prop(self, "auto_refresh", toggle=True, icon="AUTO")
row.prop(self, "refresh", toggle=True, icon="FILE_REFRESH")
col = layout.column(align=True)
col.separator()
col = layout.column(align=True)
col.prop(self, "volX", slider=True)
col.prop(self, "volY", slider=True)
col.prop(self, "volZ", slider=True)
layout.label(text="Particles:")
col = layout.column(align=True)
col.prop(self, "baseSca", slider=True)
col.prop(self, "varSca", slider=True)
col = layout.column(align=True)
col.prop(self, "rotX", slider=True)
col.prop(self, "rotY", slider=True)
col.prop(self, "rotZ", slider=True)
col = layout.column(align=True)
col.prop(self, "rSeed", slider=False)
col.prop(self, "numP")
row = layout.row(align=True)
row.prop(self, "nor")
row.prop(self, "cent")
row = layout.row(align=True)
row.prop(self, "track")
row.prop(self, "anim")
@classmethod
def poll(cls, context):
return (len(bpy.context.selected_objects) > 1 and
bpy.context.object.type == 'MESH')
def invoke(self, context, event):
return self.execute(context)
def execute(self, context):
return {'PASS_THROUGH'}
scn = bpy.context.scene
obj = bpy.context.active_object
use_random_seed(self)
mat = Matrix((
(1, 0, 0, 0),
(0, 1, 0, 0),
(0, 0, 1, 0),
(0, 0, 0, 1))
)
if obj.matrix_world != mat:
self.report({'WARNING'},
"Please, Apply transformations to Active Object first")
par = [o for o in bpy.context.selected_objects if o.type == 'MESH' and o != obj]
if not par:
return{'FINISHED'}
bpy.ops.object.mode_set()
bpy.ops.object.select_all(action='DESELECT')
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
msv = []
for i in range(len(obj.modifiers)):
msv.append(obj.modifiers[i].show_viewport)
obj.modifiers[i].show_viewport = False
cur = scn.cursor_location
for i in range(self.numP):
mes = choice(par).data
newobj = bpy.data.objects.new('nuevo', mes)
scn.objects.link(newobj)
origen = (rg(self.volX) + cur[0], rg(self.volY) + cur[1], rg(self.volZ) + cur[2])
cpom = obj.closest_point_on_mesh(origen)
if self.cent:
bm = bmesh.new()
bm.from_mesh(obj.data)
if hasattr(bm.verts, "ensure_lookup_table"):
bm.verts.ensure_lookup_table()
bm.faces.ensure_lookup_table()
newobj.location = bm.faces[cpom[3]].calc_center_median()
bm.free()
else:
newobj.location = cpom[1]
if self.nor:
newobj.rotation_mode = 'QUATERNION'
newobj.rotation_quaternion = cpom[1].to_track_quat('Z', 'Y')
newobj.rotation_mode = 'XYZ'
newobj.rotation_euler[0] += rg(self.rotX)
newobj.rotation_euler[1] += rg(self.rotY)
newobj.rotation_euler[2] += rg(self.rotZ)
else:
newobj.rotation_euler = (rg(self.rotX), rg(self.rotY), rg(self.rotZ))
newobj.scale = [self.baseSca + self.baseSca * rg(self.varSca)] * 3
if self.anim:
bpy.ops.object.make_single_user(type='SELECTED_OBJECTS', obdata=True)
bpy.ops.object.transform_apply(location=True, rotation=True, scale=True)
bme = bmesh.new()
bme.from_mesh(obj.data)
tmp = bmesh.new()
tmp.from_mesh(newobj.data)
for f in tmp.faces:
# z = len(bme.verts)
for v in f.verts:
bme.verts.new(list(v.co))
bme.faces.new(bme.verts[-len(f.verts):])
bme.to_mesh(obj.data)
remover(True)
# Note: foo.user_clear() is deprecated use do_unlink=True instead
bpy.data.meshes.remove(newobj.data, do_unlink=True)
else:
scn.objects.active = obj
bpy.ops.object.join()
if self.track:
cur = scn.cursor_location = cpom[1]
for i in range(len(msv)):
obj.modifiers[i].show_viewport = msv[i]
for o in par:
if self.auto_refresh is False:
self.refresh = False
return{'FINISHED'}
def register():
bpy.utils.register_class(OBJECT_OT_agregate_mesh)
def unregister():
bpy.utils.unregister_class(OBJECT_OT_agregate_mesh)
if __name__ == '__main__':