Blenderのモジュールビルドとインストール(Windows)

* 2019/8 Blender-2.8はEEVEEがデフォルトなので `bpy.ops.render.render()` が落ちる件
* 2019/7 既存の記事とタイトルが被っていたので変更
* 2019/7 リライト

`bpy.pyd` (GUIの無いpythonモジュールとしてのblender)のビルドとインストール方法。

環境

* Windows-10(64bit)
* Python-3.7(64bit)
* Blender-2.80

# 準備

* visualstudio2019
* git
* svn(scoop install sliksvn など)

# 普通にBlenderをビルドする

blenderビルドの細かい手順については

* [[Blender] ソースコードからBlender本体をビルドする](https://qiita.com/nutti/items/2a7d31e224b28142b3d6)

を参照してください。

# モジュールとしてBlenderをビルドする

上記の記事のところまでできたとして、そこから変更する部分を説明します。

CMakeでオプションを以下のように変更する

* WITH_PYTHON_INSTALL=OFF
* WITH_PYTHON_MODULE=ON 

ビルドがうまくいかない原因になるライブラリを取り除きます

* WITH_AUDASPACE=OFF
* WITH_OPENCOLLADA=OFF (リンクエラー xml.lib dllmain libcmt.lib)

変更して、configure して generateし、cmakeの `install` ターゲットを実行する。
buildだけだと(dllのコピーと `2.80` フォルダの生成が為されません)

# ビルドできた `bpy.pyd` と必要なファイルをPython3に対してインストールする。

下記のファイルをビルドフォルダから python37 フォルダにコピーします。

* PYTHON_DIR/lib/site-packages
  * blender.pth
* PYTHON_DIR/lib/site-packages/blender
  * bpy.pyd(本体)
  * dll
  * pdb(デバッグ用)
* PYTHON_DIR/2.80
  * (blenderの実行時に使われるスクリプトなど)

手順を python スクリプトにしました。

```py:install.py
import os
import pathlib
import glob
import shutil

PY_DIR = pathlib.Path('C:/Python37')
BL_DIR = PY_DIR / 'Lib/site-packages/blender'

if __name__ == '__main__':
    os.chdir('blender_build/bin/Release') # install.py実行パスからの相対パス
    #os.chdir('blender_build/bin/RelWithDebInfo') # 実行パスからの相対パス

    with (PY_DIR / 'Lib/site-packages/blender.pth').open('w') as w:
        w.write("blender")

    BL_DIR.mkdir(parents=True, exist_ok=True)

    shutil.copy('bpy.pyd', BL_DIR)
    for f in glob.glob('*.dll'):
        print(f'{f} => {BL_DIR / f}')
        shutil.copy(f, BL_DIR / f)
    for f in glob.glob('*.pdb'):
        print(f'{f} => {BL_DIR / f}')
        shutil.copy(f, BL_DIR / f)
    python37 = BL_DIR / 'python37.dll'
    if python37.exists():
        python37.unlink()

    dst = PY_DIR / '2.80'
    if dst.exists():
        print(f'remove {dst}')
        shutil.rmtree(dst)
    print(f'copy {dst}')
    shutil.copytree('2.80', dst)
```


# 実行する

```py
import bpy
print(bpy)
# <module 'bpy' from 'PYTHON_DIR\\lib\\site-packages\\blender\\bpy.pyd'>
```

これがPythonで実行できれば動くと思う

終了時に、

`Error: Not freed memory blocks: 8, total unfreed memory 0.010223 MB`

が出るけどこれは気にしなくてよいのではないか。

# 使い方

主に、バッチ実行か addon 開発用途になると思う。

* https://docs.blender.org/api/master/info_quickstart.html

## レンダリングする

CYCLES にする。

```py
import bpy
bpy.context.scene.render.engine = 'CYCLES' # デフォルトがEEVEEでOpenGLが無いので落ちる
bpy.ops.render.render()
bpy.data.images['Render Result'].save_render(filepath='image.png')
```

## blendファイルを開いてシーンにアクセスする

```python
import bpy
import pathlib

here = pathlib.Path(__file__).absolute().parent
monkey = here / 'monkey.blend'

# open
bpy.ops.wm.open_mainfile(filepath=str(monkey))

# object
for o in bpy.context.scene.objects:
    print(o)
```

`<bpy_struct, Object("Suzanne")>`

blendファイルを開いてシーンにアクセスできた。

memo

```python
# contextからobjectを得る
o = bpy.context.scene.objects[0]:

# objectからデータブロックを得る
data= o.data
print(isinstance(data, bpy.types.Mesh))
```

* https://docs.blender.org/api/master/bpy.types.Mesh.html

## VSCode でインテリセンスが効くようにする

https://blenderartists.org/t/which-ide-for-python/1100669/21

https://github.com/mutantbob/pycharm-blender

blender-2.80 向けに生成してみる。
さくっとエラー回避。

```diff
--- a/python_api/pypredef_gen.py
+++ b/python_api/pypredef_gen.py
@@ -999,7 +999,7 @@ def bpy2predef(BASEPATH, title):
     structs, funcs, ops, props = rna_info.BuildRNAInfo()
     #open the file:
     filepath = os.path.join(BASEPATH, "bpy.py")
-    file = open(filepath, "w")
+    file = open(filepath, "w", encoding='utf-8')
     fw = file.write
     #Start the file:
     definition = doc2definition(title,"") #skip the leading spaces at the first line...
@@ -1093,9 +1093,9 @@ def rna2predef(BASEPATH):
         import bgl as module
         pymodule2predef(BASEPATH, "bgl", module, "Open GL functions (bgl)")

-    if "aud" in INCLUDE_MODULES:
-        import aud as module
-        pymodule2predef(BASEPATH, "aud", module, "Audio System (aud)")
+    # if "aud" in INCLUDE_MODULES:
+    #     import aud as module
+    #     pymodule2predef(BASEPATH, "aud", module, "Audio System (aud)")

     if "bmesh" in INCLUDE_MODULES:
         import bmesh as module
```

出てきたフォルダをvscodeのユーザー設定に追加。

`"python.autoComplete.extraPaths": ["path_to_pypredef"]`

関数の引数に型注釈を付けて blender の型のインテリセンスが出るようになった。

![typing.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/747b50e9-201d-26ad-8fa4-c1fbb7453e44.jpeg)

# Debugする(C++の方を)

* slnをRelWithDebugInfoでビルドする(Debugだと Python37_d の方にリンクされてやりづらい)
* インストールする
* 適当なプロジェクトをデバッグ設定して `F5` する

![debug.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59664/52e41a9d-28fe-d2b5-6819-661a2aa94bd3.jpeg)

作業ディレクトリは hello.py を配置したフォルダ。

```c++:GHOST_C-api.cpp
// line:610
GHOST_TSuccess GHOST_ActivateOpenGLContext(GHOST_ContextHandle contexthandle)
{
  GHOST_IContext *context = (GHOST_IContext *)contexthandle; // これが nullptr

  return context->activateDrawingContext();
}
```

わかるときもある。