How to maintain the ratio of the gizmo while zooming in and out?
import bpy, blf, bgl, gpu
from bpy.types import Operator
from gpu_extras.batch import batch_for_shader
from math import radians, sin, cos
from bpy_extras.view3d_utils import *
class VIEW3D_OT_gizmo(Operator):
bl_label = 'Gizmo'
bl_idname = 'view3d.gizmo'
bl_options = {'REGISTER', 'UNDO'}
color_x = bpy.context.preferences.themes['Default'].user_interface.axis_x[:]
color_y = bpy.context.preferences.themes['Default'].user_interface.axis_y[:]
color_z = bpy.context.preferences.themes['Default'].user_interface.axis_z[:]
def overlay(self, context):
self.draw_gizmo(context.object)
def invoke(self, context, event):
args = (context,)
self._2d_handle = bpy.types.SpaceView3D.draw_handler_add(self.overlay, args, 'WINDOW', 'POST_PIXEL')
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def modal(self, context, event):
context.area.tag_redraw()
if event.type in {'ESC', 'RIGHTMOUSE'}:
bpy.types.SpaceView3D.draw_handler_remove(self._2d_handle, 'WINDOW')
return {'CANCELLED'}
elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
return {'PASS_THROUGH'}
return {'RUNNING_MODAL'}
def l3d_to_r2d(self, coord):
'''
Return the region relative 2d location of a 3d position.
coord (3d Vector) - 3d worldspace location.
'''
return location_3d_to_region_2d(bpy.context.region, bpy.context.region_data, coord, default=None)
def draw_gizmo(self, object):
''' Draw a gizmo for object.
object (bpy.types.Object) - Object to draw gizmo for.
'''
radius = 10 # Radius of the circle
d = 0.75 # Distance from object center to circle center
text_color = bpy.context.preferences.themes['Default'].user_interface.wcol_tool.text
alpha = 0.8
x, y, z = object.location
# Get 2d relative coordinates from 3d coordinates of the object
pos_x = self.l3d_to_r2d((x+d, y, z))
pos_y = self.l3d_to_r2d((x, y+d, z))
pos_z = self.l3d_to_r2d((x, y, z+d))
neg_x = self.l3d_to_r2d((x-d, y, z))
neg_y = self.l3d_to_r2d((x, y-d, z))
neg_z = self.l3d_to_r2d((x, y, z-d))
bgl.glEnable(bgl.GL_BLEND)
bgl.glLineWidth(2)
bgl.glEnable(bgl.GL_LINE_SMOOTH)
# Draw lines
coord_x = (self.l3d_to_r2d((x, y, z)), self.l3d_to_r2d((x+d, y, z)))
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'LINES', {'pos': coord_x})
shader.bind()
shader.uniform_float('color', self.color_x + (alpha,))
batch.draw(shader)
coord_y = (self.l3d_to_r2d((x, y, z)), self.l3d_to_r2d((x, y+d, z)))
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'LINES', {'pos': coord_y})
shader.bind()
shader.uniform_float('color', self.color_y + (alpha,))
batch.draw(shader)
coord_z = (self.l3d_to_r2d((x, y, z)), self.l3d_to_r2d((x, y, z+d)))
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'LINES', {'pos': coord_z})
shader.bind()
shader.uniform_float('color', self.color_z + (alpha,))
batch.draw(shader)
# Draw circles
self.draw_circle(pos_x, radius, self.color_x + (alpha,))
self.draw_circle(pos_y, radius, self.color_y + (alpha,))
self.draw_circle(pos_z, radius, self.color_z + (alpha,))
self.draw_circle(neg_x, radius, self.color_x + (0.2,), self.color_x + (alpha,))
self.draw_circle(neg_y, radius, self.color_y + (0.2,), self.color_y + (alpha,))
self.draw_circle(neg_z, radius, self.color_z + (0.2,), self.color_z + (alpha,))
# Get text dimensions
pos_dim_x = self.get_text_dims('X')
pos_dim_y = self.get_text_dims('Y')
pos_dim_z = self.get_text_dims('Z')
neg_dim_x = self.get_text_dims('-X')
neg_dim_y = self.get_text_dims('-Y')
neg_dim_z = self.get_text_dims('-Z')
# Draw texts
self.draw_text((pos_x[0]-(pos_dim_x[0]/2), pos_x[2]-(pos_dim_x[2]/2)), 'X', text_color)
self.draw_text((pos_y[0]-(pos_dim_y[0]/2), pos_y[2]-(pos_dim_y[2]/2)), 'Y', text_color)
self.draw_text((pos_z[0]-(pos_dim_z[0]/2), pos_z[2]-(pos_dim_z[2]/2)), 'Z', text_color)
self.draw_text((neg_x[0]-(neg_dim_x[0]/2), neg_x[2]-(neg_dim_x[2]/2)), '-X', text_color)
self.draw_text((neg_y[0]-(neg_dim_y[0]/2), neg_y[2]-(neg_dim_y[2]/2)), '-Y', text_color)
self.draw_text((neg_z[0]-(neg_dim_z[0]/2), neg_z[2]-(neg_dim_z[2]/2)), '-Z', text_color)
def draw_circle(self, position, radius, color, border_color=None):
''' Draw a circle.
position (2D Vector) - Position where the circle will be drawn.
radius (float) - Radius of the circle.
color (tuple containing RGBA values) - Color of the circle.
border_color (tuple containing RGBA values, optional) - Border Color of the circle.
'''
coords = []
for angle in range(0, 361, 5):
radian = radians(angle)
x = position[0] + radius * cos(radian)
y = position[2] + radius * sin(radian)
coords.append((x,y))
bgl.glEnable(bgl.GL_BLEND)
bgl.glEnable(bgl.GL_LINE_SMOOTH)
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRI_FAN', {'pos': coords})
shader.bind()
shader.uniform_float('color', color)
batch.draw(shader)
if border_color is not None:
bgl.glEnable(bgl.GL_BLEND)
bgl.glLineWidth(2)
bgl.glEnable(bgl.GL_LINE_SMOOTH)
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'LINE_STRIP', {'pos': coords})
shader.bind()
shader.uniform_float('color', border_color)
batch.draw(shader)
else:
bgl.glEnable(bgl.GL_BLEND)
bgl.glLineWidth(2)
bgl.glEnable(bgl.GL_LINE_SMOOTH)
shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'LINE_STRIP', {'pos': coords})
shader.bind()
shader.uniform_float('color', color)
batch.draw(shader)
text_color = bpy.context.preferences.themes['Default'].user_interface.wcol_tool.text
def draw_text(self, position, text, text_color=text_color):
''' Draw text.
position (2D Vector) - Position to draw text at.
text (str) - Text to draw.
text_color (tuple containing RGB values, optional) - Color of text.
'''
x, y = position
fontid = 0
text_size = 11
blf.enable(fontid, blf.SHADOW)
blf.shadow(fontid, 3, 0, 0, 0, 0.5)
blf.shadow_offset(fontid, 0, -1)
blf.size(fontid, text_size, 72)
r,g,b = text_color
blf.color(fontid, r,g,b,1)
blf.position(fontid, x, y, 0)
blf.draw(fontid, text)
def get_text_dims(self, text) -> tuple:
''' Get text dimensions.
text (str) - Text to get dimensions for.
return (tuple) - Dimension of the text in pixels.
'''
fontid = 0
text_size = 11
blf.size(fontid, text_size, 72)
return blf.dimensions(fontid, text)
def register():
bpy.utils.register_class(VIEW3D_OT_gizmo)
def unregister():
bpy.utils.unregister_class(VIEW3D_OT_gizmo)
if name == "main":
register()

d? – Gorgious Jan 16 '22 at 18:53