Hi I did this little addon, shortcut Alt Q
what it does?
activate desactivate every gizmo
-move the origin with a gizmo and even in edit mode!
-move the cursor with a gizmo and rotate it
-snap selection to cursor with rotation!
and more…
6 Likes
ok I did an update because of a bug in edit mode. Now I force vertex select mode
"name": "Gizmo Pie Menu",
"author": "1conscience0dimension",
"version": (1, 4, 0),
"blender": (2, 80, 0),
"location": "View3D",
"description": "a Gizmo pie menu, Alt Q",
"warning": "",
"wiki_url": "",
"category": "Pie Menu"
}
import bpy
import bmesh
from bpy.types import Menu
from bpy.types import Operator
from bpy.props import FloatVectorProperty
from bpy_extras.object_utils import AddObjectHelper, object_data_add
from mathutils import Vector
# 1-class operator gizmo scale
class VIEW3D_OT_gizmo_scale(bpy.types.Operator):
"""Show Scale Gizmo"""
bl_idname = "view3d.gizmo_scale"
bl_label = "gizmo scale"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
areas = context.workspace.screens[0].areas # find View3D
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo_object_scale^= True #to do a switch
space.show_gizmo= True
space.show_gizmo_context= True
space.show_gizmo_tool= True
return{'FINISHED'}
#2-class operator gizmo translate
class VIEW3D_OT_gizmo_translate(bpy.types.Operator):
"""Show Translate Gizmo"""
bl_idname = "view3d.gizmo_translate"
bl_label = "gizmo translate"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
areas = context.workspace.screens[0].areas
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo_object_translate^= True
space.show_gizmo= True
space.show_gizmo_context= True
space.show_gizmo_tool= True
return{'FINISHED'}
#3-class operator gizmo rotate
class VIEW3D_OT_gizmo_rotate(bpy.types.Operator):
"""Show Rotate Gizmo"""
bl_idname = "view3d.gizmo_rotate"
bl_label = "gizmo rotate"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
areas = context.workspace.screens[0].areas
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo_object_rotate^= True
space.show_gizmo= True
space.show_gizmo_context= True
space.show_gizmo_tool= True
return{'FINISHED'}
#4-class operator 0 gizmo
class VIEW3D_OT_gizmo_0(bpy.types.Operator):
"""no Gizmo"""
bl_idname = "view3d.gizmo_zero"
bl_label = "gizmo 0"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
areas = context.workspace.screens[0].areas
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo_object_rotate= False
space.show_gizmo_object_scale= False
space.show_gizmo_object_translate= False
return{'FINISHED'}
#5-add object 1 vertex 'XOrigin'
def add_object_XOrigin(self, context):
scale_x = self.scale.x
scale_y = self.scale.y
scale_z = self.scale.z
verts = [Vector((0, 0, 0))]
edges=[]
faces = []
mesh = bpy.data.meshes.new(name="XOrigin") #add mesh at cursor position
mesh.from_pydata(verts, edges, faces)
object_data_add(context, mesh, operator=self)
areas = context.workspace.screens[0].areas #gizmo translate
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo= True
space.show_gizmo_context= True
space.show_gizmo_tool= True
space.show_gizmo_object_rotate= False
space.show_gizmo_object_scale= False
space.show_gizmo_object_translate= True
class OBJECT_OT_add_object_XOrigin(Operator, AddObjectHelper):
"""Create XOrigin Object"""
bl_idname = "mesh.add_xorigin"
bl_label = "Add XOrigin Object"
bl_options = {'REGISTER', 'UNDO'}
scale: FloatVectorProperty(
name="scale",
default=(0, 0, 0),
subtype='TRANSLATION',
description="scaling",
)
@classmethod
def poll(cls, context):
cao = context.active_object
if cao is None or 'XOrigin' in bpy.data.objects:
return False
if 'XOriginVert' in cao.vertex_groups or 'OldVert' in cao.vertex_groups:
return False
#interaction Cursor gizmo
if 'XCursor' in bpy.data.objects or 'XCursorVert' in cao.vertex_groups:
return False
else:
return True
def execute(self, context):
cursor=context.scene.cursor.location.copy() #cursor location
cao=context.active_object
context.scene.cursor.location = cao.location #cursor to active
my_areas = context.workspace.screens[0].areas #wireframe view
my_shading = 'WIREFRAME'
for area in my_areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.shading.type = my_shading
if cao and cao.mode in {'OBJECT'}: # group to record activ main obj
newCol = bpy.data.collections.new('ob') #new collection 'ob'
context.scene.collection.children.link(newCol) # link to the scene
bpy.data.collections['ob'].objects.link(cao) # link object to collection
add_object_XOrigin(self, context)
if cao and cao.mode in {'EDIT'}:
if bpy.ops.mesh.select_mode(type!='VERT'):
bpy.ops.mesh.select_mode(type='VERT')
group = cao.vertex_groups.new() #old vert selection
group.name = ("OldVert")
bpy.ops.object.vertex_group_assign() #assign selected vertices
add_object_XOrigin(self, context)
group = cao.vertex_groups.new() #group with active vertices
group.name = ("XOriginVert")
bpy.ops.object.vertex_group_assign() #assign selected vertices
for group in cao.vertex_groups:
group.lock_weight = True
context.scene.cursor.location = cursor #cursor back
return {'FINISHED'}
#6-confirm new origin
def XOrigin_set(self, context):
cao=context.active_object
if cao is None or cao.mode in {'OBJECT'}: ###object
if 'XOrigin' in bpy.data.objects:
bpy.context.view_layer.objects.active=bpy.data.objects ['XOrigin'] #now active object
cao=context.active_object
ob=context.scene.collection.children['ob']
if len(list(bpy.data.collections['ob'].all_objects))==0: #main object erased
for block in bpy.data.meshes: #purge meshes
if block.users == 0:
bpy.data.meshes.remove(block)
bpy.data.objects.remove(bpy.data.objects ['XOrigin'], do_unlink=True)
context.scene.collection.children.unlink(ob)
else:
bpy.ops.object.select_all(action='DESELECT')
ob= context.scene.collection.children['ob']
for obj in ob.objects:
obj.select_set(True) #select our object in 'ob'
sel=context.selected_objects #record select
obj = context.active_object.name #record active (XOrigin)
for obj in sel:
context.view_layer.objects.active=obj #now main object is active
cursor=context.scene.cursor.location.copy()
context.scene.cursor.location=bpy.data.objects['XOrigin'].location #cursor to XOrigin
bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') #origin to cursor
context.scene.cursor.location = cursor
bpy.data.objects.remove(bpy.data.objects ['XOrigin'], do_unlink=True) #delete 'XOrigin'
context.scene.collection.children.unlink(ob) # unlink the Col ob
else:
ob=context.scene.collection.children['ob']
for obj in ob.objects:
bpy.context.view_layer.objects.active=obj
context.scene.collection.children.unlink(ob)
for block in bpy.data.collections: #purge collections
if block.users == 0:
bpy.data.collections.remove(block)
for block in bpy.data.meshes: #purge meshes
if block.users == 0:
bpy.data.meshes.remove(block)
cao=context.active_object #### edit mode
if cao and cao.mode in {'EDIT'}:
if bpy.ops.mesh.select_mode(type!='VERT'):
bpy.ops.mesh.select_mode(type='VERT')
cao= context.active_object
cursor=context.scene.cursor.location.copy() #record cursor
vtx_group = cao.vertex_groups['XOriginVert']
bpy.ops.object.vertex_group_select()
bm = bmesh.from_edit_mesh(cao.data)
selected_count = sum(int(v.select) for v in bm.verts)
if int(selected_count)>=1:
bpy.ops.mesh.select_all(action='DESELECT') #we get a problem if selection modified
vtx_group = cao.vertex_groups['XOriginVert']
bpy.ops.object.vertex_group_select()
bpy.ops.view3d.snap_cursor_to_selected()
bpy.ops.mesh.delete(type='VERT')
bpy.ops.object.editmode_toggle() #object mode
bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') #origin to cursor
bpy.ops.object.editmode_toggle() #edit mode
context.scene.cursor.location=cursor #cursor back
if 'XOriginVert' in cao.vertex_groups: #remove Oldvert
vtx_group = cao.vertex_groups['XOriginVert']
cao.vertex_groups.remove(vtx_group)
if 'OldVert' in cao.vertex_groups: #remove Oldvert
vtx_group = cao.vertex_groups['OldVert']
bpy.ops.object.vertex_group_select()
cao.vertex_groups.remove(vtx_group)
areas = bpy.context.workspace.screens[0].areas # show gizmow translate
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo_object_rotate= False
space.show_gizmo_object_scale= False
space.show_gizmo_object_translate= False
class XORIGIN_OT_confirm(Operator):
"""confirms new origin"""
bl_idname = "xorigin.confirm"
bl_label = "Origin confirm"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
cao = context.active_object
if cao is None or not 'XOriginVert' in cao.vertex_groups and cao.mode != "OBJECT":
return False
if cao is None or not 'XOrigin' in bpy.data.objects and cao.mode != "EDIT":
return False
else:
return True
def execute(self, context):
XOrigin_set(self, context)
return {'FINISHED'}
#7-add object 1 vertex 'XCursor'
def add_object_XCursor(self, context):
for block in bpy.data.meshes: #delete unused blocks (orphan data)
if block.users == 0:
bpy.data.meshes.remove(block)
o = bpy.data.objects.new( "XCursor", None)
o.empty_display_type = 'ARROWS'
o.empty_display_size = 0.8
o.location=context.scene.cursor.location
o.rotation_euler=context.scene.cursor.rotation_euler
bpy.context.scene.collection.objects.link( o )
bpy.context.view_layer.objects.active = o
areas = context.workspace.screens[0].areas #gizmo translate
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo= True
space.show_gizmo_context= True
space.show_gizmo_tool= True
space.show_gizmo_object_rotate= False
space.show_gizmo_object_scale= False
space.show_gizmo_object_translate= True
class OBJECT_OT_add_object_XCursor(Operator, AddObjectHelper):
"""Create XCursor Object"""
bl_idname = "mesh.add_xcursor"
bl_label = "Add XCursor Object"
bl_options = {'REGISTER', 'UNDO'}
scale: FloatVectorProperty(
name="scale",
default=(0.5, 0.5, 0.5),
subtype='TRANSLATION',
description="scaling",
)
@classmethod
def poll(cls, context):
cao=context.active_object
co=context.object
if cao is not None:
if 'XOriginVert' in cao.vertex_groups:
return False
if 'XCursor' in bpy.data.objects:
return False
if cao is not None:
if 'XOrigin' in bpy.data.objects:
return False
if cao is not None:
if not 'XCursorVert' in cao.vertex_groups:
return True
else:
return False
if cao is not None:
if 'XCursorVert' in cao.vertex_groups and cao.mode == "EDIT":
return False
else:
return True
def execute(self, context):
areas = bpy.context.workspace.screens[0].areas # find View3D
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.overlay.show_cursor = True
cao=context.active_object
if cao is None:
add_object_XCursor(self, context)
bpy.data.objects['XCursor'].select_set(True)
context.scene.transform_orientation_slots[0].type = 'LOCAL'
elif cao.mode=='EDIT': ###edit
if bpy.ops.mesh.select_mode(type!='VERT'):
bpy.ops.mesh.select_mode(type='VERT')
group = cao.vertex_groups.new() #old vert selection
group.name = ("OldVert1")
bpy.ops.object.vertex_group_assign() #assign selected vertices
add_object_XOrigin(self, context)
group = cao.vertex_groups.new() #group with active vertices
group.name = ("XCursorVert")
bpy.ops.object.vertex_group_assign() #assign selected vertices
for group in cao.vertex_groups:
group.lock_weight = True
bpy.ops.view3d.snap_selected_to_cursor(use_offset=False)
else: ###object
add_object_XCursor(self, context)
for obj in bpy.data.objects:
obj.select_set(False)
bpy.data.objects['XCursor'].select_set(True)
context.scene.transform_orientation_slots[0].type = 'LOCAL'
return {'FINISHED'}
#8-confirm new Cursor
def origin_set(self, context):
cao=context.active_object
if cao.mode=='OBJECT': ###object
if 'XCursor' in bpy.data.objects: # ? utile
context.scene.cursor.location=bpy.data.objects['XCursor'].location
context.scene.cursor.rotation_euler=bpy.data.objects['XCursor'].rotation_euler
bpy.ops.object.origin_set(type='ORIGIN_CURSOR', center='MEDIAN') #XCursor to cursor C
bpy.data.objects.remove(bpy.data.objects ['XCursor'], do_unlink=True) #delete 'Xcursor'
for block in bpy.data.meshes: #delete unused blocks (orphan data)
if block.users == 0:
bpy.data.meshes.remove(block)
context.scene.transform_orientation_slots[0].type = 'GLOBAL'
else: #### edit
if bpy.ops.mesh.select_mode(type!='VERT'):
bpy.ops.mesh.select_mode(type='VERT')
cao=context.active_object
vtx_group = cao.vertex_groups['XCursorVert']
bpy.ops.object.vertex_group_select()
bm = bmesh.from_edit_mesh(cao.data)
selected_count = sum(int(v.select) for v in bm.verts)
if int(selected_count)>=1:
print(selected_count)
bpy.ops.mesh.select_all(action='DESELECT') #we get a problem if selection modified
vtx_group = cao.vertex_groups['XCursorVert']
bpy.ops.object.vertex_group_select()
bpy.ops.view3d.snap_cursor_to_selected() #ops? maybe cursor=vao
bpy.ops.mesh.delete(type='VERT')
if 'XCursorVert' in cao.vertex_groups: #remove groups
vtx_group = cao.vertex_groups['XCursorVert']
cao.vertex_groups.remove(vtx_group)
if 'OldVert1' in cao.vertex_groups:
vtx_group = cao.vertex_groups['OldVert1']
bpy.ops.object.vertex_group_select()
cao.vertex_groups.remove(vtx_group)
class XCURSOR_OT_confirm(Operator):
"""Create a new Mesh Object"""
bl_idname = "xcursor.confirm"
bl_label = "Cursor confirm"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
cao=context.active_object
if cao is None:
return False
if 'XCursor' not in bpy.data.objects and cao.mode != "EDIT" :
return False
if 'XCursor' in bpy.data.objects and cao.mode == "EDIT":
return False
if 'XCursor' in bpy.data.objects and cao.name !='XCursor':
return True
if 'XCursorVert' not in cao.vertex_groups and cao.mode == "EDIT":
return False
if cao.name !='XCursor' and cao.mode != "EDIT" :
return False
else:
return True
def execute(self, context):
areas = bpy.context.workspace.screens[0].areas # find View3D
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.overlay.show_cursor = True
areas = context.workspace.screens[0].areas #gizmo settings
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo_object_rotate= False
space.show_gizmo_object_scale= False
space.show_gizmo_object_translate= False
origin_set(self, context)
return {'FINISHED'}
#9-Cursor rotation clear
class CURSOR_OT_clear_rotation_euler(bpy.types.Operator):
"""rotation_euler to 0"""
bl_idname = "cursor.rotation_zero"
bl_label = "curs rot 0"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
context.scene.cursor.rotation_euler=(0, 0, 0)
return {'FINISHED'}
#10-snap active to cursor with rotation
def cursor_snap(self, context):
cao=context.active_object
cao.location=context.scene.cursor.location
cao.rotation_euler=context.scene.cursor.rotation_euler
class CURSOR_OT_snap(bpy.types.Operator):
"""snap to cursor including rotation"""
bl_idname = "cursor.snap"
bl_label = "cursor snap"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
cao=context.active_object
if cao is None or cao.mode == "EDIT":
return False
else:
return True
def execute(self, context):
cao=context.active_object
if cao is not None and cao.mode == "OBJECT":
cursor_snap(self, context)
areas = context.workspace.screens[0].areas #gizmo settings
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.show_gizmo_object_rotate= False
space.show_gizmo_object_scale= False
space.show_gizmo_object_translate= False
return {'FINISHED'}
#11-show/hide cursor
class VIEW3D_OT_cursor_hide(bpy.types.Operator):
"""Show hide 3D Cursor"""
bl_idname = "cursor.hide"
bl_label = "cursor show/hide"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
areas = context.workspace.screens[0].areas # find View3D
for area in areas:
for space in area.spaces:
if space.type == 'VIEW_3D':
space.overlay.show_cursor ^= True #to do a switch
return{'FINISHED'}
#12-class piemenu
class VIEW3D_MT_gizmopie(Menu):
bl_label = "Gizmo pie menu"
@classmethod
def poll(cls, context):
return context.active_object is None or context.object.mode == "OBJECT" or context.object.mode == "EDIT" or context.active_object.mode in {'POSE','EDIT_GPENCIL'}
def draw(self, context):
layout = self.layout
cao=context.active_object
if cao is None:
pie = layout.menu_pie()
pie.split()
pie.split()
pie.split()
pie.split()
box = pie.split().column()
row = box.row()
box.split()
box.split()
box = pie.split().column()
row = box.row()
box.operator("mesh.add_xcursor",text= "Move Cursor")
box.operator("xcursor.confirm",text= "Confirm")
box = pie.split().column()
row = box.row()
box.operator("cursor.rotation_zero",text= "0 Rot Cursor")
box.operator("cursor.hide",text= "Switch Cursor")
box.split()
if cao and cao.type in {'MESH'}:
pie = layout.menu_pie()
pie.operator("view3d.gizmo_translate", text="Translate") #ops idname
pie.operator("view3d.gizmo_rotate", text="Rotate")
pie.operator("view3d.gizmo_scale", text="Scale")
pie.operator("view3d.gizmo_zero", text="0")
#pie.split() #if you want jump to next position in the pie menu
box = pie.split().column()
row = box.row()
box.operator("mesh.add_xorigin",text= "Move Origin")
box.operator("xorigin.confirm",text= "Confirm")
box = pie.split().column()
row = box.row()
box.operator("mesh.add_xcursor",text= "Move Cursor")
box.operator("xcursor.confirm",text= "Confirm")
box = pie.split().column()
row = box.row()
box.operator("cursor.rotation_zero",text= "0 Rot Cursor")
box.operator("cursor.snap",text= "Snap obj to curs")
box.operator("cursor.hide",text= "Switch Cursor")
if cao and cao.type in {'EMPTY','LIGHT','SPEAKER','CAMERA','LIGHT_PROBE',}:
pie = layout.menu_pie()
pie.operator("view3d.gizmo_translate", text="Translate") #ops idname
pie.operator("view3d.gizmo_rotate", text="Rotate")
pie.operator("view3d.gizmo_scale", text="Scale")
pie.operator("view3d.gizmo_zero", text="0")
#pie.split() #if you want jump to next position in the pie menu
box = pie.split().column()
row = box.row()
box.split()
box.split()
box = pie.split().column()
row = box.row()
box.operator("mesh.add_xcursor",text= "Move Cursor")
box.operator("xcursor.confirm",text= "Confirm")
box = pie.split().column()
row = box.row()
box.operator("cursor.rotation_zero",text= "0 Rot Cursor")
box.operator("cursor.snap",text= "Snap obj to curs")
box.operator("cursor.hide",text= "Switch Cursor")
if cao and cao.type in {'LATTICE','META','CURVE','SURFACE','ARMATURE','FONT','GPENCIL'}:
if cao.mode in {'OBJECT'}:
pie = layout.menu_pie()
pie.operator("view3d.gizmo_translate", text="Translate") #ops idname
pie.operator("view3d.gizmo_rotate", text="Rotate")
pie.operator("view3d.gizmo_scale", text="Scale")
pie.operator("view3d.gizmo_zero", text="0")
#pie.split() #if you want jump to next position in the pie menu
box = pie.split().column()
row = box.row()
box.operator("mesh.add_xorigin",text= "Move Origin")
box.operator("xorigin.confirm",text= "Confirm")
box = pie.split().column()
row = box.row()
box.operator("mesh.add_xcursor",text= "Move Cursor")
box.operator("xcursor.confirm",text= "Confirm")
box = pie.split().column()
row = box.row()
box.operator("cursor.rotation_zero",text= "0 Rot Cursor")
box.operator("cursor.snap",text= "Snap obj to curs")
box.operator("cursor.hide",text= "Switch Cursor")
else:
pie = layout.menu_pie()
pie.operator("view3d.gizmo_translate", text="Translate") #ops idname
pie.operator("view3d.gizmo_rotate", text="Rotate")
pie.operator("view3d.gizmo_scale", text="Scale")
pie.operator("view3d.gizmo_zero", text="0")
#pie.split() #if you want jump to next position in the pie menu
box = pie.split().column()
row = box.row()
box.split()
box.split()
box = pie.split().column()
row = box.row()
box.split()
box.split()
box = pie.split().column()
row = box.row()
box.operator("cursor.rotation_zero",text= "0 Rot Cursor")
box.operator("cursor.hide",text= "Switch Cursor")
box.split()
#13-script end
classes = (
VIEW3D_MT_gizmopie,
VIEW3D_OT_gizmo_translate,
VIEW3D_OT_gizmo_rotate,
VIEW3D_OT_gizmo_scale,
VIEW3D_OT_gizmo_0,
OBJECT_OT_add_object_XOrigin,
XORIGIN_OT_confirm,
OBJECT_OT_add_object_XCursor,
XCURSOR_OT_confirm,
CURSOR_OT_clear_rotation_euler,
CURSOR_OT_snap,
VIEW3D_OT_cursor_hide,
)
addon_keymaps = []
def register():
for cls in classes:
bpy.utils.register_class(cls)
wm = bpy.context.window_manager
if wm.keyconfigs.addon:
# Object Modes
km = wm.keyconfigs.addon.keymaps.new(name = '3D View Generic', space_type = 'VIEW_3D')
kmi = km.keymap_items.new('wm.call_menu_pie', 'Q', 'PRESS', alt=True)
kmi.properties.name = "VIEW3D_MT_gizmopie"
# kmi.active = True
addon_keymaps.append((km, kmi))
def unregister():
from bpy.utils import unregister_class
for cls in reversed(classes):
unregister_class(cls)
wm = bpy.context.window_manager
kc = wm.keyconfigs.addon
if kc:
for km, kmi in addon_keymaps:
km.keymap_items.remove(kmi)
addon_keymaps.clear()
if __name__ == "__main__":
register()
#bpy.ops.wm.call_menu_pie(name=VIEW3D_PIE_gizmo.bl_idname)```
interesting. How can I change the keymap?
at the end
def register():
for cls in classes:
bpy.utils.register_class(cls)
wm = bpy.context.window_manager
if wm.keyconfigs.addon:
# Object Modes
km = wm.keyconfigs.addon.keymaps.new(name = '3D View Generic', space_type = 'VIEW_3D')
kmi = km.keymap_items.new('wm.call_menu_pie', 'Q', 'PRESS', alt=True)
you put your key instead of ‘Q’ and if you need a shift maybe you write shift=True instead of alt=True or nothing, just the key if you just need a simple key.
for info I did a new version (link into the description) https://www.youtube.com/watch?v=oilGT4WTMOg
just add here v 1.5.1 from rightclickselect
gizmo_pie_menu_v1_5_1.py (31.6 KB)
and some video from author of addon
7 Likes
what is the state of this addon?