keKit for Blender (2.8+)

Hi, awesome addon, definitely worth paying for!
I saw that you can rename “Orientation&Pivot” bookmarks but is there no way to rename snapping combos? I’d love to be able to rename them to something more memorable than “Snap combo 1” etc.
Keep up the great work, I used your addon for Modo before switching.

1 Like

Great add-on!
Is it possible to add functionality to the Direct Loop Cut similar to modo? Specifically, is it possible to run Loop Slice presets in one click?

Earlier post with more details:

Referencing scripts by Cristobal Vila, ETEREA Slices Presets 1.4
https://community.foundry.com/discuss/topic/26386

Hey! Yes, I’ll add that to the list. :slight_smile:

1 Like

Hey! Yes, I am familiar with the idea. I still have that pie menu in my modo install. (But I never really used them). Personal side note: Currently I experiment with “korean” bevels (aka just flat or square bevels for subd workflows) using the bevel modifier and/or edge-bevel. (2 segments, profile 1), and weighting and/or creasing.

I could make a similar pie with presets with a custom defined cut operation or two. maybe all. fairly easily (you set prefs for the end-offset %) (it would just be a macro of loop cut and offset edge slide pretty much) I’ll add it, as that is simple enough ;>

(note: it wont actually use direct loop cut at all. When I was planning on making DLC modal I actually wanted to include hotkeys for such multislices, but that got cut. pun intended.)

Well, why not give it a try - quite handy feature (besides “korean” bevel) :slight_smile:
Personally, I like it better because it’s faster and more flexible in lot of cases.
I’ve made similar functionality in Blender for myself some time ago. It isn’t an external addon, so it’s useless for people atm without some tricky manipulations.

1 Like

Awesome! Thank you!

Hi.
I have the same with a little exception, I use EVEN values on sides, in cases when mesh is not perfectly square.

Example 1:

It built from other scripts, so it also can’t be possible exported…but we all know how @Kiellog is good!

Updated:
Also it’s possible to do like this, with choosing parameters

Code:
class EDGE_OT_5_90_5 (Operator):
    bl_idname = "edge.5_90_5"
    bl_label = "Slide Offset X - XX - X"
    bl_description = "Slide Offset Even or Not"
    bl_options = {'REGISTER', 'UNDO'}

    even: bpy.props.EnumProperty(
    items=[("ON", "ON", "ON", 1),
           ("OFF", "OFF", "OFF", 2)
           ],
    name="Even",
    default="ON")
    
    values: bpy.props.EnumProperty(
    items=[("5-90-5", "5-90-5", "5-90-5", 1),
           ("10-80-10", "10-80-10", "10-80-10", 2),
           ("15-70-15", "15-70-15", "15-70-15", 3),
           ("20-60-20", "20-60-20", "20-60-20", 4),
           ("25-50-25", "25-50-25", "25-50-25", 5)                      
           ],
    name="Values",
    default="5-90-5")
    
    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        column = layout.column()
        
        row = column.row()
        row.prop(self, "even", expand=True)
#        row = column.row()
#        row.prop(self, "values", expand=True)
        col = layout.column()
        col.prop(self, "values", expand=True)
        
    def main(self, context):
        if self.even == 'ON':
            if self.values == '5-90-5':
                return 0.9
            elif self.values == '10-80-10':
                return 0.8
            elif self.values == '15-70-15':
                return 0.7
            elif self.values == '20-60-20':
                return 0.6
            elif self.values == '25-50-25':
                return 0.5
            
        if self.even == 'OFF':
            if self.values == '5-90-5':
                return 0.9
            elif self.values == '10-80-10':
                return 0.8
            elif self.values == '15-70-15':
                return 0.7
            elif self.values == '20-60-20':
                return 0.6
            elif self.values == '25-50-25':
                return 0.5
            
    def execute(self, context):        
        ob = context.edit_object
        me = ob.data
        bm = bmesh.from_edit_mesh(me)
        
        bpy.ops.armored.connect()
        selEdges = [e for e in bm.edges if e.select]
   
        if self.even =='ON': 
            bpy.ops.mesh.offset_edge_loops_slide(TRANSFORM_OT_edge_slide={
            "value":self.main(context), 
            "single_side":True, 
            "use_even":True, 
            "flipped":True, 
            "use_clamp":True, 
            "mirror":True, 
            "snap":False, 
            "snap_target":'CLOSEST', 
            "snap_point":(0, 0, 0), 
            "snap_align":False, 
            "snap_normal":(0, 0, 0), 
            "correct_uv":True, 
            "release_confirm":False, 
            "use_accurate":False
            })
        
        if self.even =='OFF': 
            bpy.ops.mesh.offset_edge_loops_slide(TRANSFORM_OT_edge_slide={
            "value":self.main(context), 
            "single_side":True, 
            "use_even":False, 
            "flipped":False, 
            "use_clamp":True, 
            "mirror":True, 
            "snap":False, 
            "snap_target":'CLOSEST', 
            "snap_point":(0, 0, 0), 
            "snap_align":False, 
            "snap_normal":(0, 0, 0), 
            "correct_uv":True, 
            "release_confirm":False, 
            "use_accurate":False
            })
            
        selEdges2 = [e for e in bm.edges if e.select]

        bpy.ops.mesh.select_all(action='DESELECT')
        for e in selEdges:
            e.select = True
        bpy.ops.mesh.dissolve_mode(use_verts=True)
        
        for e in selEdges2:
            e.select = True

        return {'FINISHED'}

I used other script from ARMORED COLONY (bpy.ops.armored.connect()) which basically slice selected edge without selecting whole loop. Then I just used default blender “Edge Offset” with some tweaks and remembering selection for dissolving unneeded loop after…

2 Likes

So, if @Kiellog not mind
here is complete code for loop tool in my previous post.
Works only with edges. Need exceptions for verts/faces selection (it recognize them as edges too).
Slide_Offset_X-XX-X_tool
modify whatever you need

code
class EDGE_OT_5_90_5 (Operator):
    bl_idname = "edge.5_90_5"
    bl_label = "Slide Offset X - XX - X"
    bl_description = "Slide Offset Even or Not"
    bl_options = {'REGISTER', 'UNDO'}

    even: bpy.props.EnumProperty(
    items=[("ON", "ON", "ON", 1),
           ("OFF", "OFF", "OFF", 2)
           ],
    name="Even",
    default="ON")
    
    values: bpy.props.EnumProperty(
    items=[("5-90-5", "5-90-5", "5-90-5", 1),
           ("10-80-10", "10-80-10", "10-80-10", 2),
           ("15-70-15", "15-70-15", "15-70-15", 3),
           ("20-60-20", "20-60-20", "20-60-20", 4),
           ("25-50-25", "25-50-25", "25-50-25", 5)                      
           ],
    name="Values",
    default="5-90-5")
    
    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        column = layout.column()
        
        row = column.row()
        row.prop(self, "even", expand=True)
#        row = column.row()
#        row.prop(self, "values", expand=True)
        col = layout.column()
        col.prop(self, "values", expand=True)
        
    def main(self, context):
        if self.even == 'ON':
            if self.values == '5-90-5':
                return 0.9
            elif self.values == '10-80-10':
                return 0.8
            elif self.values == '15-70-15':
                return 0.7
            elif self.values == '20-60-20':
                return 0.6
            elif self.values == '25-50-25':
                return 0.5
            
        if self.even == 'OFF':
            if self.values == '5-90-5':
                return 0.9
            elif self.values == '10-80-10':
                return 0.8
            elif self.values == '15-70-15':
                return 0.7
            elif self.values == '20-60-20':
                return 0.6
            elif self.values == '25-50-25':
                return 0.5
            
    def execute(self, context):        
        ob = context.edit_object
        me = ob.data
        bm = bmesh.from_edit_mesh(me)
        
        bpy.ops.mesh.loop_multi_select(ring=True)

        #remembering ring of cuting edges
        cut_edges = [e for e in bm.edges if e.select]

        bpy.ops.mesh.select_all(action='DESELECT')

        #remembering new edges after subdivide_edges for dissolving them later
        middle_edges = bmesh.ops.subdivide_edges(bm, edges=list(cut_edges), cuts=1, use_grid_fill=False)
        
        #selecting inner edges with 'geom_inner' after subdivide_edges operation
        for e in middle_edges['geom_inner']: e.select = True
        
        bmesh.update_edit_mesh(me)
   
        #Offset Edge Slide with Even:ON
        if self.even =='ON': 
            bpy.ops.mesh.offset_edge_loops_slide(TRANSFORM_OT_edge_slide={
            "value":self.main(context), 
            "single_side":True, 
            "use_even":True, 
            "flipped":True, 
            "use_clamp":True, 
            "mirror":True, 
            "snap":False, 
            "snap_target":'CLOSEST', 
            "snap_point":(0, 0, 0), 
            "snap_align":False, 
            "snap_normal":(0, 0, 0), 
            "correct_uv":True, 
            "release_confirm":False, 
            "use_accurate":False
            })
        
        #Offset Edge Slide with Even:OFF
        if self.even =='OFF': 
            bpy.ops.mesh.offset_edge_loops_slide(TRANSFORM_OT_edge_slide={
            "value":self.main(context), 
            "single_side":True, 
            "use_even":False, 
            "flipped":False, 
            "use_clamp":True, 
            "mirror":True, 
            "snap":False, 
            "snap_target":'CLOSEST', 
            "snap_point":(0, 0, 0), 
            "snap_align":False, 
            "snap_normal":(0, 0, 0), 
            "correct_uv":True, 
            "release_confirm":False, 
            "use_accurate":False
            })
            
        #remembering edges after Offset Edge Slide operation
        sel_edges = [e for e in bm.edges if e.select]

        bpy.ops.mesh.select_all(action='DESELECT')
        
        #selecting inner middle edge loop for dissolving
        for e in middle_edges['geom_inner']: e.select = True

        bpy.ops.mesh.dissolve_mode(use_verts=True)
        
        #selecting earlier remembering edges after Offset Edge Slide operation
        for e in sel_edges:
            e.select = True

        return {'FINISHED'}

col.operator("edge.5_90_5", icon="PAUSE") for UI pannel

cheers,
I do have a test version up & running already, it will get the offsets correct automatically, like so;


Redo panel comes later, I’ll add as much customization as I can ;>
(I got stuck trying to add more cuts though, very strange issues using ops. So I either have to code it or just stick to 3 cuts max (center cut optional)…which I’m leaning towards. )

1 Like

IMO you don’t need more then 3 cuts, in most cases it even no need middle cut. So you just cut at the sides and then do other operation with created selected loops.

My best wish if this tool be interactive. Not just select presets 5-90-5, and how in modo with interactive sliding…but it’s not possible with simple macros.

Updated:
Lol, just forgot about FloatProperty possibility

Updated Code
class EDGE_OT_5_90_5 (Operator):
    bl_idname = "edge.5_90_5"
    bl_label = "Slide Offset X - XX - X"
    bl_description = "Slide Offset Even or Not"
    bl_options = {'REGISTER', 'UNDO'}

    even: bpy.props.EnumProperty(
    items=[("ON", "ON", "ON", 1),
           ("OFF", "OFF", "OFF", 2)
           ],
    name="Even",
    default="ON")
        
    factor: FloatProperty(
        name="Factor",      
        min=0.1, 
        max=1.0,
        step=0.1,
        default=0.9
    )
    
    def draw(self, context):
        layout = self.layout
        layout.use_property_split = True
        column = layout.column()
        
        row = column.row()
        row.prop(self, "even", expand=True)
#        row = column.row()
#        row.prop(self, "values", expand=True)
        col = layout.column()
        col.prop(self, "factor")      
            
    def main(self, context):
        if self.even == 'ON':
            if self.factor:
                return self.factor
            
        if self.even == 'OFF':
            if self.factor:
                return self.factor
            
    def execute(self, context):        
        ob = context.edit_object
        me = ob.data
        bm = bmesh.from_edit_mesh(me)
        
        bpy.ops.mesh.loop_multi_select(ring=True)

        #remembering ring of cuting edges
        cut_edges = [e for e in bm.edges if e.select]

        bpy.ops.mesh.select_all(action='DESELECT')

        #remembering new edges after subdivide_edges for dissolving them later
        middle_edges = bmesh.ops.subdivide_edges(bm, edges=list(cut_edges), cuts=1, use_grid_fill=False)
        
        #selecting inner edges with 'geom_inner' after subdivide_edges operation
        for e in middle_edges['geom_inner']: e.select = True
        
        bmesh.update_edit_mesh(me)
   
        #Offset Edge Slide with Even:ON
        if self.even =='ON': 
            bpy.ops.mesh.offset_edge_loops_slide(TRANSFORM_OT_edge_slide={
            "value":self.main(context), 
            "single_side":True, 
            "use_even":True, 
            "flipped":True, 
            "use_clamp":True, 
            "mirror":True, 
            "snap":False, 
            "snap_target":'CLOSEST', 
            "snap_point":(0, 0, 0), 
            "snap_align":False, 
            "snap_normal":(0, 0, 0), 
            "correct_uv":True, 
            "release_confirm":False, 
            "use_accurate":False
            })
        
        #Offset Edge Slide with Even:OFF
        if self.even =='OFF': 
            bpy.ops.mesh.offset_edge_loops_slide(TRANSFORM_OT_edge_slide={
            "value":self.main(context), 
            "single_side":True, 
            "use_even":False, 
            "flipped":False, 
            "use_clamp":True, 
            "mirror":True, 
            "snap":False, 
            "snap_target":'CLOSEST', 
            "snap_point":(0, 0, 0), 
            "snap_align":False, 
            "snap_normal":(0, 0, 0), 
            "correct_uv":True, 
            "release_confirm":False, 
            "use_accurate":False
            })
            
        #remembering edges after Offset Edge Slide operation
        sel_edges = [e for e in bm.edges if e.select]

        bpy.ops.mesh.select_all(action='DESELECT')
        
        #selecting inner middle edge loop for dissolving
        for e in middle_edges['geom_inner']: e.select = True

        bpy.ops.mesh.dissolve_mode(use_verts=True)
        
        #selecting earlier remembering edges after Offset Edge Slide operation
        for e in sel_edges:
            e.select = True

        return {'FINISHED'}

in UI panel you can create presets like this

        op = col.operator("edge.5_90_5", text="Slide Offset | OFF | 0.5 ", icon="PAUSE")
        op.even = 'OFF'
        op.factor = 0.5

Heh ;>
Well, not being a huge fan of the redo panel (I like things immediate) one could do a little hack where you just use offset loop slide in modal, and dissolve the center when LMB (if one does not want it)

3 Likes

Why it’s very convenient to have loops selected after running tool, you can do like this

Oh nice! As someone coming from Modo having to use the redo panel feels slow, I’d much rather have the tool do what I need in one action.
Would it be possible to add an option to use absolute distance instead of percentages? If I have two differently sized props Id still want the bevels to be the same size to ground the assets.

It is possible, sure. Hmm, there are some complex issues trying to combine everything in the same behaviour - I think the modal variant will be a bit different, and maybe a little more dynamic (not as big of a need for presets, as it just a starting point anyways in modal) Probably will use the same operator, just different modes. Or i separate them to keep it simple. we´ll see :slight_smile:

2 Likes

I cant replicate. Works afaict. Could you be more specific? (extruding verts? using split edges with “auto merge”, value not at 0.001?)

1 Like

Thank you for letting know, so, knowing its bug on my side, i found a solution.
Installed on vanilla one - everything works as expected, so made some tests in my 2.9 and found that it needs:

  1. to hold standard extrude operator in mesh section (was deleted by myself)
  2. and place “context extrude” below it (adding operator back didnt get result at first so i reapplied “context delete”)
    Maybe i missed or misunderstand something but for me everything works now (•¯ ∀ ¯•)

Ok! Good that you found a solution.
I could not understand your explanation/process though :wink:
Are you talking about shortcut mappings or modifying python code, or something else?

just about shortcuts mapping )
Untitled-1

I see! Tip: If you get conflicts with multple assignments to the same hotkey,it does look a bit dubious, just disabling the unwanted hotkeys is an option. (then you can easily re-assign as well) Example:
image

1 Like

Could you please check will it work with TT mode activated?