[Blender] スクリプトの内部からOpenGLを利用する

Blenderのアドオンを開発していると独自にUIを作りたいと思ったことはありませんか？
UIを作るとなるとBlender内で画像などを表示する必要があるのですが、幸いなことにBlender APIには、OpenGLにアクセスするためのAPIが公開されています。
このAPIと以下の記事で紹介しているマウスイベントをあわせて使えばBlender専用のUIに従わず独自のUIを構築することができます。

[[Blender] Blenderスクリプトでマウスやキーボードのイベントを扱う方法](http://qiita.com/nutti/items/aeb2cd47d704d2368f19)

ただしAPIが用意されているとは言っても、OpenGLの全ての機能に対してAPIが用意されているわけではありません。
そのため、できることがBlenderが用意しているAPIの範囲内に限られてしまいますが、アドオンのUIくらいの簡単な描画処理ならば、用意されているAPIだけでも十分事足りると思います。

この記事では、View3Dに四角形を描くサンプルを紹介し、BlenderからOpenGLを利用する方法を説明します。

# サンプルコード

```py3:opengl_on_blender.py

import bpy
import bgl    # OpenGLをBlender内部から利用するために必要


bl_info = {
    "name": "Tutorial: OpenGL on Blender",
    "author": "Nutti",
    "version": (1, 0),
    "blender": (2, 74, 0),
    "location": "View3D > Tutorial: OpenGL on Blender",
    "description": "Tutorial: Use Blender's OpenGL API.",
    "warning": "",
    "support": "COMMUNITY",
    "wiki_url": "",
    "tracker_url": "",
    "category": "3D View"
}


class RectRenderer(bpy.types.Operator):
    """四角形を描画する"""
    
    bl_idname = "view3d.rect_renderer"
    bl_label = "Rect renderer"

    __handle = None    # 描画関数
    
    # 「View3D」領域の描画関数を登録
    @staticmethod
    def handle_add():
        RectRenderer.__handle = bpy.types.SpaceView3D.draw_handler_add(
            RectRenderer.render_rect,
            (), 'WINDOW', 'POST_PIXEL')

    # 「View3D」領域の描画関数を登録解除
    @staticmethod
    def handle_remove():
        if RectRenderer.__handle is not None:
            bpy.types.SpaceView3D.draw_handler_remove(
                RectRenderer.__handle, 'WINDOW')
            RectRenderer.__handle = None
    
    # 「View3D」領域の描画関数本体
    @staticmethod
    def render_rect():
        # 描画領域の作成
        positions = [
            [10.0, 10.0],     # 左下
            [10.0, 600.0],    # 左上
            [600.0, 600.0],   # 右上
            [600.0, 10.0]     # 右下
            ]

        # OpenGLによる四角形の描画
        bgl.glEnable(bgl.GL_BLEND)    # アルファブレンドの有効化
        bgl.glBegin(bgl.GL_QUADS)    # 四角形の描画を開始
        bgl.glColor4f(0.7, 0.5, 0.3, 0.6)    # 描画する四角形の色を指定
        for (v1, v2) in positions:
            bgl.glVertex2f(v1, v2)    # 頂点の登録
        bgl.glEnd()    # 四角形の描画を終了


# スクリプトインストール時の処理
def register():
    bpy.utils.register_module(__name__)
    RectRenderer.handle_add()


# スクリプトアンインストール時の処理
def unregister():
    bpy.utils.unregister_module(__name__)
    RectRenderer.handle_remove()


if __name__ == "__main__":
    register()
```

# 使い方

1. [Blender Wikiページ](http://wiki.blender.org/index.php/Doc:JA/2.6/Manual/Extensions/Python/Add-Ons)を参考にプラグインをインストールします。
2. View3Dに四角形が表示されていることを確認します。

![20150722.jpg](https://qiita-image-store.s3.amazonaws.com/0/38888/b734beea-a89a-5489-7ce3-836a0284fed3.jpeg)

# サンプルコードの解説

Blenderスクリプトの基本的な解説は以下の記事で紹介しているので、ここでは新しく追加された要素の解説に絞ります。
[\[Blender\] Blenderプラグインの作り方](http://qiita.com/nutti/items/a836391723bd28cd3e4c)

## bglモジュールのインポート

Blenderが用意しているOpenGLのAPIを利用するためには、 ```bgl``` モジュールをインポートする必要があります。

```py3
import bgl
```

## 「View3D」描画関数の登録

スクリプトをインストールする時に、描画関数を登録するため ```RectRenderer.handle_add()``` を呼びます。
これにより、スクリプトのインストール後に四角形が描画されるようになります。

```py3
# スクリプトインストール時の処理
def register():
    bpy.utils.register_module(__name__)
    RectRenderer.handle_add()
```

```RectRenderer.handle_add()``` は、静的メソッドとして実装します。
```bpy.types.SpaceView3D.draw_handler_add()``` は、「View3D」領域に対する描画関数を登録する関数です。
第1引数に **描画関数本体** 、第2引数に **第1引数に指定した描画関数の引数に渡すパラメータ** を指定します。
登録した描画関数を解除するときのために、 ```bpy.types.SpaceView3D.draw_handler_add()``` の戻り値である **ハンドラ** を、変数 ```__handle``` に保存します。

```py3
    # 「View3D」領域の描画関数を登録
    @staticmethod
    def handle_add():
        RectRenderer.__handle = bpy.types.SpaceView3D.draw_handler_add(
            RectRenderer.render_rect,
            (), 'WINDOW', 'POST_PIXEL')
```

## 「View3D」描画関数の登録解除

スクリプトのアンインストール時には、描画関数の登録を解除するために ```RectRenderer.handle_remove()``` を呼びます。
スクリプトがアンインストールされた後に、四角形が描画されないようにします。

```py3
# スクリプトアンインストール時の処理
def unregister():
    bpy.utils.unregister_module(__name__)
    RectRenderer.handle_remove()
```

```RectRenderer.handle_remove()``` は、静的メソッドとして実装します。
```bpy.types.SpaceView3D.draw_handler_remove()``` は、「View3D」領域に対して登録した描画関数を解除する関数です。
第1引数に ```bpy.types.SpaceView3D.draw_handler_add()``` の戻り値である **ハンドラ** を渡します。

```py3
    # 「View3D」領域の描画関数を登録解除
    @staticmethod
    def handle_remove():
        if RectRenderer.__handle is not None:
            bpy.types.SpaceView3D.draw_handler_remove(
                RectRenderer.__handle, 'WINDOW')
            RectRenderer.__handle = None
```

## 「View3D」描画関数本体

描画関数本体には描画処理を追加します。
OpenGLを利用したことがある方であればすぐ分かると思いますが、C言語でのOpenGLの使い方と非常に良く似ています。
ただし ```bgl``` モジュールを利用するため、 ```bgl.``` を関数の先頭につけることに注意してください。
C言語でのOpenGLの使い方はWeb上にたくさん情報がありますので、OpenGLの具体的な使い方はここでは説明しません。
今回示したサンプルについては、ソースコード内に具体的な処理内容をコメントとして記載しているので、確認してみてください。
なお、Blenderで公開されているOpenGLのAPIは、以下のURLから確認することが出来ます。
http://www.blender.org/api/blender_python_api_2_60_4/bgl.html

```py3
    # 「View3D」領域の描画関数本体
    @staticmethod
    def render_rect():
        # 描画領域の作成
        positions = [
            [10.0, 10.0],     # 左下
            [10.0, 600.0],    # 左上
            [600.0, 600.0],   # 右上
            [600.0, 10.0]     # 右下
            ]

        # OpenGLによる四角形の描画
        bgl.glEnable(bgl.GL_BLEND)    # アルファブレンドの有効化
        bgl.glBegin(bgl.GL_QUADS)    # 四角形の描画を開始
        bgl.glColor4f(0.7, 0.5, 0.3, 0.6)    # 描画する四角形の色を指定
        for (v1, v2) in positions:
            bgl.glVertex2f(v1, v2)    # 頂点の登録
        bgl.glEnd()    # 四角形の描画を終了
```