I'm drawing some lines using a 2D_UNIFORM_COLOR
shader based on object location. When I rotate my view, the lines update only when the rotation is done: I thought that I can just put a function that updates my coordinates in modal()
but this didn't work. So I wonder if there's a way to update the regions while the view is rotated?
P.S. I realise that a 3d shader would work better for drawing axes, but axes here is just an example
My code:
import bpy
from mathutils import Vector
import bgl
import gpu
from gpu_extras.batch import batch_for_shader
from bpy_extras.view3d_utils import location_3d_to_region_2d
def draw_callback_px(self, context):
# do I really have to create a separate shader for each line? Ugh
shader_X = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
shader_Y = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
shader_Z = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
bgl.glEnable(bgl.GL_BLEND)
bgl.glLineWidth(2)
v_x = (self.axes[0] - self.ob_location).normalized()
v_y = (self.axes[1] - self.ob_location).normalized()
v_z = (self.axes[2] - self.ob_location).normalized()
batch_X = batch_for_shader(shader_X, 'LINE_STRIP', {"pos": [self.ob_location, self.ob_location + v_x*100]})
batch_Y = batch_for_shader(shader_Y, 'LINE_STRIP', {"pos": [self.ob_location, self.ob_location + v_y*100]})
batch_Z = batch_for_shader(shader_Z, 'LINE_STRIP', {"pos": [self.ob_location, self.ob_location + v_z*100]})
shader_X.bind()
shader_X.uniform_float("color", (1, 0, 0, 0.2))
batch_X.draw(shader_X)
shader_Y.bind()
shader_Y.uniform_float("color", (0, 1, 0, 0.2))
batch_Y.draw(shader_Y)
shader_Z.bind()
shader_Z.uniform_float("color", (0, 0, 1, 0.2))
batch_Z.draw(shader_Z)
# restore opengl defaults
bgl.glLineWidth(1)
bgl.glDisable(bgl.GL_BLEND)
class ModalOperator(bpy.types.Operator):
"""Move an object with the mouse, example"""
bl_idname = "object.modal_operator_test"
bl_label = "Simple Modal Operator Test"
def modal(self, context, event):
context.area.tag_redraw()
self.update_region(context, event)
# Navigation
if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'} or event.alt:
return {'PASS_THROUGH'}
elif event.type in {'RIGHTMOUSE', 'ESC'}:
bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
return {'CANCELLED'}
return {'RUNNING_MODAL'}
def invoke(self, context, event):
self.ob = bpy.context.view_layer.objects.active
if not self.ob:
return {'CANCELLED'}
self.update_region(context, event)
args = (self, context)
self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_PIXEL')
context.window_manager.modal_handler_add(self)
return {'RUNNING_MODAL'}
def update_region(self, context, event):
self.region = context.region
self.rv3d = context.space_data.region_3d
self.ob_location = location_3d_to_region_2d(self.region, self.rv3d, self.ob.location, default=None)
self.axes = [location_3d_to_region_2d(self.region, self.rv3d, self.ob.location + Vector((1,0,0)), default=None),
location_3d_to_region_2d(self.region, self.rv3d, self.ob.location + Vector((0,1,0)), default=None),
location_3d_to_region_2d(self.region, self.rv3d, self.ob.location + Vector((0,0,1)), default=None)]
def register():
bpy.utils.register_class(ModalOperator)
def unregister():
bpy.utils.unregister_class(ModalOperator)
if __name__ == "__main__":
register()