So, i am doing like @Xikon and sharing here a piece of code I use a lot right now.
Currently drawing in the viewport is easier than never!, I really like the new draw api!
But as everyone know, we, python programmers are lazy, so having a package that does stuff for us is fantastic.
Following that principle, I made a package for myself, and found it so handy that I had to share.
class DrawCallbackPx:
def __init__(self):
self.vertices = []
self.colors = []
self.text = []
self.thickness = 2
self.font_shadow = (0, 0, 0, 0.5)
self.shader = gpu.shader.from_builtin("2D_SMOOTH_COLOR")
self.batch_redraw = False
self.batch = None
self.handler = None
def __call__(self):
self.draw()
def add_text(self, text, location, size, color=(0, 0, 0, 1), dpi=72):
self.text.append((text, location, size, color, dpi))
def add_circle(self, center, radius, resolution, color=(1, 0, 0, 1)):
self.batch_redraw = True
for i in range(resolution):
line_point_a = (sin(i / resolution * 2 * pi) * radius + center[0],
cos(i / resolution * 2 * pi) * radius + center[1])
line_point_b = (sin((i + 1) / resolution * 2 * pi) * radius + center[0],
cos((i + 1) / resolution * 2 * pi) * radius + center[1])
self.add_line(line_point_a, line_point_b, color)
def add_line(self, point_a, point_b, color_a=(1, 0, 0, 1), color_b=None):
self.batch_redraw = True
self.vertices.append(point_a)
self.vertices.append(point_b)
self.colors.append(color_a)
self.colors.append(color_b if color_b else color_a)
def remove_last_line(self):
self.vertices.pop(-1)
self.vertices.pop(-1)
def remove_last_text(self):
self.text.pop(-1)
def clear(self):
self.vertices.clear()
self.colors.clear()
self.text.clear()
def update_batch(self):
self.batch_redraw = False
self.batch = gpu_extras.batch.batch_for_shader(self.shader, "LINES",
{"pos": self.vertices, "color": self.colors})
def setup_handler(self):
self.handler = bpy.types.SpaceView3D.draw_handler_add(self, (), "WINDOW", "POST_PIXEL")
def remove_handler(self):
bpy.types.SpaceView3D.draw_handler_remove(self.handler, "WINDOW")
def draw(self):
bgl.glEnable(bgl.GL_BLEND)
if self.batch_redraw or not self.batch:
self.update_batch()
bgl.glLineWidth(self.thickness)
self.shader.bind()
self.batch.draw(self.shader)
bgl.glLineWidth(1)
for text, location, size, color, dpi in self.text:
blf.position(0, location[0], location[1], 0)
blf.size(0, size, dpi)
blf.color(0, *color)
blf.shadow(0, 3, *self.font_shadow)
blf.draw(0, text)
bgl.glDisable(bgl.GL_BLEND)
For drawing in blender, the main pattern is to make a function and add it as a handler, it somehow has some funky namespace problems so, we have to pass all the data for drawing through a tuple argument.
I really don’t like this pattern, so here is what I did.
Its a class that contains a __call__
method which makes it behave like a function, BUT it can store data in its own namespace and anyone who get a reference to its instance can change that data remotely, different from a method, it can be accessed from multiple operators and still be a single instance.
Personally, I like it because it abstracts all the drawing funkyness into add_line() methods which are really easy to use, I guess it could be extended with add_triangle() and add_primitive() methods also.