LoginSignup
6
8

More than 1 year has passed since last update.

Houdini(Python 3)でBlender Python API

Last updated at Posted at 2020-10-25

※Blender 2.93以降ではPython 3.9が使用されていてこの記事の方法では動かなくなっています。
Blender 2.93: Python API

Houdini 18.5でPython 3がProduction Buildsに

Production Buildsの項目にHoudini 18.5 - Python 3が追加されました。
Python3が使えるようになったのでPython3系にしかないライブラリが使えるようになったはず!
なので今回はPython3.7で使用できるbpy(Blender Python)をHoudiniに入れてみます。

使用したのは以下のWindows版のHoudiniです。
houdini-py3-18.5.351-win64-vc141.exe

bpyをビルド

Python Moduleとしてのbpyは現状配布されていないので以下を参考にソースコードからビルドする必要があります。
Building Blender as a Python Module

ビルド環境を整備

Blenderをソースコードからビルドする日本語記事は検索すればすぐ見つかると思いますのでより詳しい情報が必要な場合はそちらを探してみてください。

  • Visual Studio 2019
  • CMake
  • Git
  • Subversion

あたりがインストールされていれば多分大丈夫なはず・・・?

ソースコードを落としてくる

https://developer.blender.org/diffusion/B/ を参考にソースコードを落としてきます。
githubのミラーからだとzipで落とせると思います。

ビルドする

ソースコードを展開すると以下の用のディレクトリ構成になっていると思います。
blender_build.PNG

このディレクトリ(make.batがある)でコマンドプロンプトを開いて

make bpy

を実行するとビルドに必要な他のライブラリをSubversionで落としてきてビルドされます
(開始してすぐに必要な他のライブラリを落として良いか聞かれるので y を押して進めます)。
初回はすごく時間がかかります。

ビルド成果物をHoudiniで読めるように配置

ビルド成果物は
(blenderのソースコードのディレクトリ)..\build_windows_Bpy_x64_vc16_Release\bin\Release
にあります。

bpy.pyd, libfftw3-3.dll

この2つはHoudiniのpythonが読めるディレクトリに配置します。
必要なdllはblenderのバージョンが変わると増減するかもしれないです。
ここでは以下に配置しました。
C:\Users\(ユーザー名)\Documents\houdini18.5\python3.7libs

37libs.PNG

2.90(ビルドしたblenderのバージョン)フォルダ

このフォルダはインストールしたHoudiniのexeがあるディレクトリに配置します。
環境変数を設定すると別の場所でも良くなるかもしれません(未検証)。
ここでは以下に配置しました。
C:\Program Files\Side Effects Software\Houdini 18.5.351\bin
bin.PNG

Blender Python API を使う

import bpyの確認

やっと使用準備が整いました。Python ShellやPython(SOP)などでimport bpyがエラーなく実行できることを確認します。ビルド成果物や配置場所に問題があるとエラーを出したりHoudiniが落ちたりします。

python_shell.PNG

.blendファイルを読み込む

Python(SOP)で.blendファイルを読み込む処理を作成します。

テストデータの用意

キューブとスザンヌを配置した.blendを用意しました。
スザンヌは半分に切ってMirror, Subdivisionモディファイアを設定しています。
test_data.PNG

Python(SOP)でPythonコードを書く

Primitiveアトリビュートでname
Vertexアトリビュートで N, uv
を読み込むコードを書きました。

パラメータは以下の通りです。

Label Name 説明
Blender File blend_file 読み込む.blendの指定
Object Name objects 読み込むオブジェクトの指定
Apply Modifier apply_modifier モディファイアを適用するかの指定

houdini.PNG

import os
import bpy
from bpy_extras.io_utils import axis_conversion

node = hou.pwd()
geo = node.geometry()

# 設定するアトリビュートを準備 
name_attrib = geo.addAttrib(hou.attribType.Prim, 'name', '')
normal_attrib = geo.addAttrib(hou.attribType.Vertex, 'N', (0.0, 0.0, 0.0))
uv_attrib = geo.addAttrib(hou.attribType.Vertex, 'uv', (0.0, 0.0, 0.0))

# パラメータの読み取り 
blend_file = node.parm('blend_file').evalAsString()
apply_modifier = node.parm('apply_modifier').evalAsInt()
object_names = [ s.strip() for s in node.parm('objects').evalAsString().split() ]

if len(blend_file)>0:
    # .blendを開く
    bpy.ops.wm.open_mainfile(filepath=blend_file)
    # 名前が設定されてなければすべてのオブジェクトを読む 
    if len(object_names)==0:
        object_names = bpy.data.objects.keys()
else:
    # blenderの初期ファイルを開いてスザンヌを出す
    bpy.ops.wm.read_homefile(app_template='')
    bpy.ops.mesh.primitive_monkey_add()
    object_names = ['Suzanne']

depsgraph = bpy.context.evaluated_depsgraph_get()
# blenderの軸の向きをHoudiniの軸の向きに変換する行列
axis_conv_mat = axis_conversion(
    from_forward='-Y', from_up='Z',  
    to_forward='Z', to_up='Y'
    ).to_4x4()

# 指定された名前のオブジェクトを開く 
for obj_name in object_names:
    obj = bpy.data.objects[obj_name]
    if obj.type!='MESH':
        continue

    # モディファイアを必要なら適用
    ob_for_convert = obj.evaluated_get(depsgraph) if apply_modifier else obj.original

    # オブジェクトからメッシュを取り出す
    try:
        me = ob_for_convert.to_mesh()
    except:
        me = None
    if me is None:
        continue

    # 軸変換とオブジェクトのトランスフォームをメッシュに適用
    me.transform( axis_conv_mat @ obj.matrix_world )

    # マイナスのスケールがかかっている場合の対応
    if obj.matrix_world.determinant() < 0.0:
        me.flip_normals()

    # Vertex Normalを計算する
    me.calc_normals_split()

    # UVのデータを取得
    uv_layer = me.uv_layers.active.data[:] if len(me.uv_layers) > 0 else None

    # ポイントを作成 
    points = [ hou.Vector3(v.co) for v in me.vertices ]
    pt_list = geo.createPoints(points)

    # blenderとhoudiniでポリゴンの頂点順が異なるので変換する 
    loop_indices_list = list()
    for mpoly in me.polygons:
        count = len(mpoly.loop_indices)
        loop_indices_list.append( [ mpoly.loop_indices[(count-i)%count] for i in range(0, count) ] )

    for loop_indices in loop_indices_list:
        poly = geo.createPolygon()
        poly.setAttribValue(name_attrib, obj_name)
        for i in loop_indices:
            # ポリゴンを作る 
            v = poly.addVertex( pt_list[ me.loops[i].vertex_index ] )
            # N attribute 
            v.setAttribValue(normal_attrib,  me.loops[i].normal)
            # uv attribute 
            if uv_layer:
                uv = uv_layer[i].uv
                v.setAttribValue(uv_attrib, (uv[0], uv[1], 0.0))

Object Nameのパラメータは以下のようにMenu Scriptで▽から選べるようにしました。

import os
import bpy

name_list = list()

node = hou.pwd()
blend_file = node.parm('blend_file').evalAsString()
# ファイルの存在チェック 
if not os.path.exists(blend_file):
    return name_list

# Meshのオブジェクトの名前を列挙
objects = bpy.data.objects
for obj in objects:
    if obj.type == 'MESH':
        name_list.extend( [obj.name]*2 )

return name_list

コードにコメントを書いているので詳細な説明は省略します。
Blender Python APIでメッシュ情報を取得するコードは
(Blenderインストールディレクトリ)\(Blenderバージョン)\scripts\addons
以下でio_で始まるものがImporter/Exporterのアドオンなのでそちらが参考になります。
↑のコードはio_scene_objを参考に書きました。

その他

bpyで.blendファイルを読み込んだ際に以下のようなログが出力されます。
これを外から止める方法は分かりませんでした。
houdini_console.png

どうしても邪魔だという時はbpyをビルドする際にこのログ出力コードをコメントアウトすれば出力されなくなります。

source\blender\blenkernel\intern\blendfile.c
int BKE_blendfile_read(bContext *C,
                       const char *filepath,
                       const struct BlendFileReadParams *params,
                       ReportList *reports)
{
  BlendFileData *bfd;
  bool success = false;

  /* Don't print startup file loading. */
  if (params->is_startup == false) {
    printf("Read blend: %s\n", filepath);
  }
  ...
}

6
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
8