3
$\begingroup$

Since there isn't demo in API docs, so I tried simple script below:

import bpy
from bgl import *
from bpy import context


def draw_line():

    glColor3f(1.0, 0.0, 0.0)
    glLineWidth(10)

    glBegin(GL_LINES)
    glVertex3f(0, 0, 0)
    glVertex3f(0.5 ,0.5, 0.5)
    glEnd()

draw_line()

But it doesn't work, I can't find a line in the viewport. I want to know the simplest way to draw a line with bgl, not with modal operator. So I tried script like below:

from bgl import *
import bpy


def draw_line():

    glColor3f(1.0, 0.0, 0.0)
    glLineWidth(2.0)
    glBegin(GL_LINES)
    glVertex3f(0.0, 0.0, 0.0)
    glVertex3f(1.0, 1.0, 1.0)
    glEnd()

def draw():
    if bpy.context.area.type == 'VIEW_3D':
        handle = bpy.types.SpaceView3D.draw_handler_add(draw_line, 'WINDOW', 'POST_VIEW')

    bpy.context.area.tag_redraw()

draw()

But it doesn't work, too. What's wrong with it?

$\endgroup$
  • 1
    $\begingroup$ Try to play with the template scripts in blender's text editor. There is one or two for drawing in the viewport. $\endgroup$ – JakeD Aug 26 '16 at 16:13
  • 2
    $\begingroup$ The simplest way to draw to the 3dView with bgl is with a modal Operator. $\endgroup$ – zeffii Sep 27 '16 at 7:01
10
$\begingroup$

Using a modal operator is the simplest and especially the correct way to draw something with bgl.

enter image description here Click to enlarge

import bpy
import bgl
import blf

def draw_line_3d(color, start, end, width=1):
    bgl.glLineWidth(width)
    bgl.glColor4f(*color)
    bgl.glBegin(bgl.GL_LINES)
    bgl.glVertex3f(*start)
    bgl.glVertex3f(*end)

def draw_typo_2d(color, text):
    font_id = 0  # XXX, need to find out how best to get this.
    # draw some text
    bgl.glColor4f(*color)
    blf.position(font_id, 20, 70, 0)
    blf.size(font_id, 20, 72)
    blf.draw(font_id, text)


def draw_callback_3d(self, context):
    bgl.glEnable(bgl.GL_BLEND)

    # object locations
    cube_loc = bpy.data.objects['Cube'].location
    lamp_loc = bpy.data.objects['Lamp'].location
    camera_loc = bpy.data.objects['Camera'].location

    # green line   
    draw_line_3d((0.0, 1.0, 0.0, 0.7), camera_loc, cube_loc)

    # red line
    draw_line_3d((1.0, 0.0, 0.0, 0.7), cube_loc, lamp_loc)

    # blue line
    draw_line_3d((0.0, 0.0, 1.0, 0.7), lamp_loc, camera_loc, 2)

    bgl.glEnd()
    # restore opengl defaults
    bgl.glLineWidth(1)
    bgl.glDisable(bgl.GL_BLEND)
    bgl.glColor4f(0.0, 0.0, 0.0, 1.0)

def draw_callback_2d(self, context):

    bgl.glEnable(bgl.GL_BLEND)

    # draw text
    draw_typo_2d((1.0, 1.0, 1.0, 1), "Hello Word ")

    bgl.glEnd()
    # restore opengl defaults
    bgl.glLineWidth(1)
    bgl.glDisable(bgl.GL_BLEND)
    bgl.glEnable(bgl.GL_DEPTH_TEST)
    bgl.glColor4f(0.0, 0.0, 0.0, 1.0)


class ModalDrawOperator(bpy.types.Operator):
    bl_idname = "view3d.modal_operator"
    bl_label = "Simple Modal View3D Operator"

    def modal(self, context, event):
        context.area.tag_redraw()

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

        return {'PASS_THROUGH'}
        #return {'RUNNING_MODAL'}

    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_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d, args, 'WINDOW', 'POST_VIEW')
            self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d, args, 'WINDOW', 'POST_PIXEL')

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


def register():
    bpy.utils.register_class(ModalDrawOperator)


def unregister():
    bpy.utils.unregister_class(ModalDrawOperator)

if __name__ == "__main__":
    register()

Documentation: https://www.blender.org/api/blender_python_api_current/bgl.html#module-bgl

$\endgroup$
  • $\begingroup$ Thank you, @poor. But I'm still confused. I've edited my question again.... $\endgroup$ – NGE Aug 28 '16 at 2:49
  • $\begingroup$ @NGE So why is this import bgl ??? $\endgroup$ – user58715 Jul 17 at 14:00
3
$\begingroup$

I think I've found the answer I want:

from bgl import *
import bpy


def draw_line():

    glColor3f(1.0, 0.0, 0.0)
    glLineWidth(2.0)
    glBegin(GL_LINES)
    glVertex3f(0.0, 0.0, 0.0)
    glVertex3f(1.0, 1.0, 1.0)
    glEnd()


handle = bpy.types.SpaceView3D.draw_handler_add(draw_line, (), 'WINDOW', 'POST_VIEW')

draw function needs no argument, so an empty tuple(()) should be added to draw_handler_add as an argument.

$\endgroup$
  • 1
    $\begingroup$ It doesn't need to have parameters, but if at some point you need to pass some data from your operator instance to this drawing callback then you absolutely must use parameters, it's the only way to do that (unless you can access bpy.context, then you can also store data in some custom property). The template "modal draw" example shows how to use parameters anyway. $\endgroup$ – R. Navega Jun 30 '18 at 8:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.