Skip to content
Snippets Groups Projects
add_mesh_aggregate.py 10.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### 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),
        "blender": (2, 7, 0),
        "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'}
    
        volX = FloatProperty(
                    name="Volume X",
                    min=0.1, max=25,
                    default=3,
                    description="The cloud around cursor"
                    )
        volY = FloatProperty(
                    name="Volume Y",
                    min=0.1, max=25,
                    default=3,
                    description="The cloud around cursor"
                    )
        volZ = FloatProperty(
                    name="Volume Z",
                    min=0.1, max=25,
                    default=3,
                    description="The cloud around cursor"
                     )
        baseSca = FloatProperty(
                    name="Scale",
                    min=0.01, max=5,
                    default=.25,
                    description="Particle Scale"
                    )
        varSca = FloatProperty(
                    name="Var",
                    min=0, max=1,
                    default=0,
                    description="Particle Scale Variation"
                    )
        rotX = FloatProperty(
                    name="Rot Var X",
                    min=0, max=2,
                    default=0,
                    description="X Rotation Variation"
                    )
        rotY = FloatProperty(
                    name="Rot Var Y",
                    min=0, max=2,
                    default=0,
                    description="Y Rotation Variation"
                    )
        rotZ = FloatProperty(
                    name="Rot Var Z",
                    min=0, max=2,
                    default=1,
                    description="Z Rotation Variation"
                    )
        rSeed = IntProperty(
                    name="Random seed",
                    min=0, max=999999,
                    default=1,
                    description="Seed to feed random values"
                    )
        numP = IntProperty(
                    name="Number",
                    min=1,
                    max=9999, soft_max=500,
                    default=50,
                    description="Number of particles"
                    )
        nor = BoolProperty(
                    name="Normal Oriented",
                    default=False,
                    description="Align Z axis with Faces normals"
                    )
        cent = BoolProperty(
                    name="Use Face Center",
                    default=False,
                    description="Center on Faces"
                    )
        track = BoolProperty(
                    name="Cursor Follows",
                    default=False,
                    description="Cursor moves as structure grows / more compact results"
                    )
        anim = BoolProperty(
                    name="Animatable",
                    default=False,
                    description="Sort faces so you can regrow with Build Modifier, materials are lost"
                    )
    
    
        refresh = bpy.props.BoolProperty(
                    name="Update",
                    default=False
                    )
        auto_refresh = bpy.props.BoolProperty(
                    name="Auto",
                    description="Auto update spline",
                    default=False
                    )
    
    
        def draw(self, context):
            layout = self.layout
            col = layout.column(align=True)
    
            row = col.row(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 = layout.column(align=True)
            col.prop(self, "nor")
            col.prop(self, "cent")
            col.prop(self, "track")
            col.prop(self, "anim")
    
            col.prop(self, 'numP')
    
        @classmethod
        def poll(cls, context):
            return(len(bpy.context.selected_objects) > 1 and bpy.context.object.type == 'MESH')
    
        def invoke(self, context, event):
    
            self.refresh = True
    
            return self.execute(context)
    
        def execute(self, context):
    
            if not self.refresh:
    
                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'}, "Apply transformations to Active Object first!")
                return{'FINISHED'}
            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')
            obj.select = True
            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:
                    newobj.select = True
                    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)
    
                    newobj.data.user_clear()
                    bpy.data.meshes.remove(newobj.data)
    
                else:
                    scn.objects.active = obj
                    newobj.select = True
                    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:
                o.select = True
    
            obj.select = True
    
    
            if self.auto_refresh is False:
                self.refresh = False
            #elif self.auto_refresh is True:
            #    self.refresh = True
    
    
            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__':