Skip to content
Snippets Groups Projects
mesh_mextrude_plus.py 10.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • # SPDX-License-Identifier: GPL-2.0-or-later
    
    
    # Repeats extrusion + rotation + scale for one or more faces
    # Original code by liero
    # Update by Jimmy Hazevoet 03/2017 for Blender 2.79
    # normal rotation, probability, scaled offset, object coords, initial and per step noise
    
    
    bl_info = {
        "name": "MExtrude Plus1",
        "author": "liero, Jimmy Hazevoet",
        "version": (1, 3, 0),
        "blender": (2, 77, 0),
        "location": "View3D > Tool Shelf",
        "description": "Repeat extrusions from faces to create organic shapes",
        "warning": "",
    
        "doc_url": "",
        "category": "Mesh",
    }
    
    
    
    import bpy
    import bmesh
    import random
    from bpy.types import Operator
    from random import gauss
    from math import radians
    from mathutils import (
            Euler, Vector,
            )
    from bpy.props import (
            FloatProperty,
            IntProperty,
            BoolProperty,
            )
    
    
    def gloc(self, r):
        return Vector((self.offx, self.offy, self.offz))
    
    
    def vloc(self, r):
        random.seed(self.ran + r)
        return self.off * (1 + gauss(0, self.var1 / 3))
    
    
    def nrot(self, n):
        return Euler((radians(self.nrotx) * n[0],
                      radians(self.nroty) * n[1],
                      radians(self.nrotz) * n[2]), 'XYZ')
    
    
    def vrot(self, r):
        random.seed(self.ran + r)
        return Euler((radians(self.rotx) + gauss(0, self.var2 / 3),
                      radians(self.roty) + gauss(0, self.var2 / 3),
                      radians(self.rotz) + gauss(0, self.var2 / 3)), 'XYZ')
    
    
    def vsca(self, r):
        random.seed(self.ran + r)
        return self.sca * (1 + gauss(0, self.var3 / 3))
    
    
    class MExtrude(Operator):
        bl_idname = "object.mextrude"
        bl_label = "Multi Extrude"
        bl_description = ("Extrude selected Faces with Rotation,\n"
                          "Scaling, Variation, Randomization")
        bl_options = {"REGISTER", "UNDO", "PRESET"}
    
        off: FloatProperty(
                name="Offset",
                soft_min=0.001, soft_max=10,
                min=-100, max=100,
                default=1.0,
                description="Translation"
                )
        offx: FloatProperty(
                name="Loc X",
                soft_min=-10.0, soft_max=10.0,
                min=-100.0, max=100.0,
                default=0.0,
                description="Global Translation X"
                )
        offy: FloatProperty(
                name="Loc Y",
                soft_min=-10.0, soft_max=10.0,
                min=-100.0, max=100.0,
                default=0.0,
                description="Global Translation Y"
                )
        offz: FloatProperty(
                name="Loc Z",
                soft_min=-10.0, soft_max=10.0,
                min=-100.0, max=100.0,
                default=0.0,
                description="Global Translation Z"
                )
        rotx: FloatProperty(
                name="Rot X",
                min=-85, max=85,
                soft_min=-30, soft_max=30,
                default=0,
                description="X Rotation"
                )
        roty: FloatProperty(
                name="Rot Y",
                min=-85, max=85,
                soft_min=-30,
                soft_max=30,
                default=0,
                description="Y Rotation"
                )
        rotz: FloatProperty(
                name="Rot Z",
                min=-85, max=85,
                soft_min=-30, soft_max=30,
                default=-0,
                description="Z Rotation"
                )
        nrotx: FloatProperty(
                name="N Rot X",
                min=-85, max=85,
                soft_min=-30, soft_max=30,
                default=0,
                description="Normal X Rotation"
                )
        nroty: FloatProperty(
                name="N Rot Y",
                min=-85, max=85,
                soft_min=-30, soft_max=30,
                default=0,
                description="Normal Y Rotation"
                )
        nrotz: FloatProperty(
                name="N Rot Z",
                min=-85, max=85,
                soft_min=-30, soft_max=30,
                default=-0,
                description="Normal Z Rotation"
                )
        sca: FloatProperty(
                name="Scale",
                min=0.01, max=10,
                soft_min=0.5, soft_max=1.5,
                default=1.0,
                description="Scaling of the selected faces after extrusion"
                )
        var1: FloatProperty(
                name="Offset Var", min=-10, max=10,
                soft_min=-1, soft_max=1,
                default=0,
                description="Offset variation"
                )
        var2: FloatProperty(
                name="Rotation Var",
                min=-10, max=10,
                soft_min=-1, soft_max=1,
                default=0,
                description="Rotation variation"
                )
        var3: FloatProperty(
                name="Scale Noise",
                min=-10, max=10,
                soft_min=-1, soft_max=1,
                default=0,
                description="Scaling noise"
                )
        var4: IntProperty(
                name="Probability",
                min=0, max=100,
                default=100,
                description="Probability, chance of extruding a face"
                )
        num: IntProperty(
                name="Repeat",
                min=1, max=500,
                soft_max=100,
                default=5,
                description="Repetitions"
                )
        ran: IntProperty(
                name="Seed",
                min=-9999, max=9999,
                default=0,
                description="Seed to feed random values"
                )
        opt1: BoolProperty(
                name="Polygon coordinates",
                default=True,
                description="Polygon coordinates, Object coordinates"
                )
        opt2: BoolProperty(
                name="Proportional offset",
                default=False,
                description="Scale * Offset"
                )
        opt3: BoolProperty(
                name="Per step rotation noise",
                default=False,
                description="Per step rotation noise, Initial rotation noise"
                )
        opt4: BoolProperty(
                name="Per step scale noise",
                default=False,
                description="Per step scale noise, Initial scale noise"
                )
    
        @classmethod
        def poll(cls, context):
            obj = context.object
            return (obj and obj.type == 'MESH')
    
        def draw(self, context):
            layout = self.layout
            col = layout.column(align=True)
            col.label(text="Transformations:")
            col.prop(self, "off", slider=True)
            col.prop(self, "offx", slider=True)
            col.prop(self, "offy", slider=True)
            col.prop(self, "offz", 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.prop(self, "nrotx", slider=True)
            col.prop(self, "nroty", slider=True)
            col.prop(self, "nrotz", slider=True)
            col = layout.column(align=True)
            col.prop(self, "sca", slider=True)
    
            col = layout.column(align=True)
            col.label(text="Variation settings:")
            col.prop(self, "var1", slider=True)
            col.prop(self, "var2", slider=True)
            col.prop(self, "var3", slider=True)
            col.prop(self, "var4", slider=True)
            col.prop(self, "ran")
            col = layout.column(align=False)
            col.prop(self, 'num')
    
            col = layout.column(align=True)
            col.label(text="Options:")
            col.prop(self, "opt1")
            col.prop(self, "opt2")
            col.prop(self, "opt3")
            col.prop(self, "opt4")
    
        def execute(self, context):
            obj = bpy.context.object
            om = obj.mode
            bpy.context.tool_settings.mesh_select_mode = [False, False, True]
            origin = Vector([0.0, 0.0, 0.0])
    
            # bmesh operations
            bpy.ops.object.mode_set()
            bm = bmesh.new()
            bm.from_mesh(obj.data)
            sel = [f for f in bm.faces if f.select]
    
            after = []
    
            # faces loop
            for i, of in enumerate(sel):
                nro = nrot(self, of.normal)
                off = vloc(self, i)
                loc = gloc(self, i)
                of.normal_update()
    
                # initial rotation noise
                if self.opt3 is False:
                    rot = vrot(self, i)
                # initial scale noise
                if self.opt4 is False:
                    s = vsca(self, i)
    
                # extrusion loop
                for r in range(self.num):
                    # random probability % for extrusions
                    if self.var4 > int(random.random() * 100):
                        nf = of.copy()
                        nf.normal_update()
                        no = nf.normal.copy()
    
                        # face/obj coördinates
                        if self.opt1 is True:
                            ce = nf.calc_center_bounds()
                        else:
                            ce = origin
    
                        # per step rotation noise
                        if self.opt3 is True:
                            rot = vrot(self, i + r)
                        # per step scale noise
                        if self.opt4 is True:
                            s = vsca(self, i + r)
    
                        # proportional, scale * offset
                        if self.opt2 is True:
                            off = s * off
    
                        for v in nf.verts:
                            v.co -= ce
                            v.co.rotate(nro)
                            v.co.rotate(rot)
                            v.co += ce + loc + no * off
                            v.co = v.co.lerp(ce, 1 - s)
    
                        # extrude code from TrumanBlending
                        for a, b in zip(of.loops, nf.loops):
                            sf = bm.faces.new((a.vert, a.link_loop_next.vert,
                                               b.link_loop_next.vert, b.vert))
                            sf.normal_update()
                        bm.faces.remove(of)
                        of = nf
    
                after.append(of)
    
            for v in bm.verts:
                v.select = False
            for e in bm.edges:
                e.select = False
    
            for f in after:
                if f not in sel:
                    f.select = True
                else:
                    f.select = False
    
            bm.to_mesh(obj.data)
            obj.data.update()
    
            # restore user settings
            bpy.ops.object.mode_set(mode=om)
    
            if not len(sel):
                self.report({"WARNING"},
                            "No suitable Face selection found. Operation cancelled")
                return {'CANCELLED'}
    
            return {'FINISHED'}
    
    
    def register():
        bpy.utils.register_module(__name__)
    
    
    def unregister():
        bpy.utils.unregister_module(__name__)
    
    
    if __name__ == '__main__':
        register()