I want do draw a line from one point to another using the blender opengl functions provided in bgl. Both points are in world coordinate system. I found a function in bpy_extras.object_utils called world_to_camera_view which needs a scene, a camera and a Vector of the point as a parameter. I can get the scene from


but how do I get the viewport camera?

Background: What I want to do is, to write a modal operator which lets the user move, rotate and zoom the camera in the viewport and the lines between the points in the viewport are drawn correctly. Its just an operator to learn drawing with bgl.


1 Answer 1


If you use 'PRE_VIEW' or 'POST_VIEW' as draw handler type, you will be in world space and can draw straight ahead. If you want to draw on top of everything ("X-Ray"), then calculate the 2d region coordinates and use 'POST_PIXEL' - there is a utility function for the conversion:

from bpy_extras.view3d_utils import location_3d_to_region_2d

Here is a quick example that draws a line between 3D cursor and active object origin, using 'POST_VIEW', which will consider depth of other objects ('PRE_VIEW' would draw under everything else):

import bpy
import bgl
import blf

def draw_callback_px(self, context):

    ob = context.object
    if ob is None:

    font_id = 0  # XXX, need to find out how best to get this.

    # draw some text
    blf.position(font_id, 15, 30, 0)
    blf.size(font_id, 20, 72)
    blf.draw(font_id, ob.name)

    # 50% alpha, 2 pixel width line
    bgl.glColor4f(1.0, 1.0, 1.0, 0.5)


    # restore opengl defaults
    bgl.glColor4f(0.0, 0.0, 0.0, 1.0)

class ModalDrawOperator(bpy.types.Operator):
    """Draw a line with the mouse"""
    bl_idname = "view3d.modal_operator"
    bl_label = "Simple Modal View3D Operator"

    def modal(self, context, event):

        if event.type in {'RIGHTMOUSE', 'ESC'}:
            bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
            return {'CANCELLED'}

        return {'PASS_THROUGH'}

    def invoke(self, context, event):
        if context.area.type == 'VIEW_3D':
            # the arguments we pass the the callback
            args = (self, context)
            # Add the region OpenGL drawing callback
            # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
            self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_px, args, 'WINDOW', 'POST_VIEW')

            self.mouse_path = []

            return {'RUNNING_MODAL'}
            self.report({'WARNING'}, "View3D not found, cannot run operator")
            return {'CANCELLED'}

def register():

def unregister():

if __name__ == "__main__":

    for area in bpy.context.screen.areas:
        if area.type == 'VIEW_3D':
            bpy.ops.view3d.modal_operator({'area': area}, 'INVOKE_DEFAULT')
  • $\begingroup$ Thanks for this snippet - works nicely, but I have a console output: "Context 'region' not found". Can't find the issue. Do you have an idea to get rid of this? $\endgroup$
    – p2or
    Feb 7, 2015 at 20:20
  • 1
    $\begingroup$ The operator invocation at the bottom uses a custom context, whose only attribute is 'area'. Thus, you receive a note about no region attribute found in the context. If you supply one, it will stop to print to console. It's usually not a problem to have these kinds of warnings printed however. $\endgroup$
    – CodeManX
    Feb 7, 2015 at 21:30
  • $\begingroup$ Ahhh. Makes sense - Thanks for your response! $\endgroup$
    – p2or
    Feb 7, 2015 at 21:53

Not the answer you're looking for? Browse other questions tagged .