※この記事ではgitのタグ「v2.74」から生成したブランチ上でコードリーディングしています。

前回は流体シミュレーションの実験のためのプロジェクトを作成しました。
( プロジェクトファイルのダウンロードはこちら
今回はこれをもとに流体シミュレーションのコードリーディングをしていきます。

流体シミュレーションの計算が行われるのはDomainタイプのFluidオブジェクトのBakeボタンを押したときです。このときに何が起きるのか探っていきましょう。

流体計算の関数を探す

まずはBakeボタンを押したときに呼ばれる関数を探したいと思います。

どうやって探すか?UIパーツにマウスカーソルを合わせると、そのUIに関するPythonの命令を表示してくれます。Bakeボタンにマウスカーソルを合わせてみると、
bake_python
bpy.ops.fluid.bake() というPythonの命令が出てきます。

ここで余談になるのですが、Blenderはコマンドラインからバックグラウンドで立ち上げる事も出来ます。Mac Proなどハイスペックなマシンをサーバーとして用意しておき、流体シミュレーションのBakeなど長く時間のかかるものはサーバーで起動したBlender上でPythonコマンドを使って自動で処理させておくような事も可能です。

さて、話を元に戻してbpy.ops.fluid.bake()を起点にC<ー>Pythonのつなぎの部分のコードを追いかけていくと確実にたどり着けそうですが・・・まあ、もっとシンプルに「fluid」や「bake」で検索すればおそらくたどり着けるでしょう。

Xcodeのパターン検索で、「fluid(Any)bake」で検索すると、
30 results in 4 files
案の定、physics_fluid.cといういかにも関係ありそうな名前のファイルが引っかかります。 このphysics_fluid.cのなかで「fluid(Any)bake」は27カ所。その中でも関数の定義だけに絞り込んで関係してそうな名前のものだけピックアップしてみると

static int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain, short do_job)

static int fluid_bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))

static int fluid_bake_exec(bContext *C, wmOperator *op)

となります。

fluid_bake_invoke()もfluid_bake_exec()も両方中でfluidsimBake()を呼んでいるので、fluidsimBake()の先頭部分にブレイクポイントを張ってBakeボタンを押してみる事にします。すると狙い通りブレイクポイントに引っかかりました!

fluidsimBake()の中身を追う

fluidsimBake()の中身は232行に渡っています。その中の多くはパラメータをセットする命令になっているようです。最後の方にジョブをスタートさせたりコールバックの設定をしたりするような部分があります。おそらく別スレッドで非同期に動かすためのジョブのパラメータをセットし、最後にジョブをスタートさせる事で流体シミュレーションを実行させているのではないでしょうか?Bakeが終わるのを待っている間他の操作が同時に出来るので、そう考えると自然な気がします。

ジョブを追う

このような予測のもと、ジョブを中心に追いかけていこうと思います。実際の流体計算にたどり着き、それが別スレッドで実行されていれば予測が的中しているものと見てよさそうです。

fluidsimBake()の最後の方で呼ばれているfluidbake_startjob()がジョブをスタートさせる関数で、これに渡されているジョブがfbと言う名前の付いたFluidBakeJob構造体のポインタです。

typedef struct FluidBakeJob {
    /* from wmJob */
    void *owner;
    short *stop, *do_update;
    float *progress;
    int current_frame;
    elbeemSimulationSettings *settings;
} FluidBakeJob;

最後のelbeemSimulationSettings *settings;にいろいろ詰まってそうです。

fluidbake_startjob()を呼ぶ直前で、

fb->settings = fsset;

というように、fssetというポインタが渡されています。

このelbeemSimulationSettings構造体の中身に

elbeemRunSimulationCallback runsimCallback;

という関数ポインタ(別の場所でelbeemRunSimulationCallbackはtypedefされている)が定義されています。コールバックなのでもし流体計算が別スレッドで処理されていればそのスレッドから呼ばれるので、ブレイクポイントで止めれば計算箇所を突き止められるかもしれません。

fsset->runsimCallback = &runSimulationCallback;

のように、physics_fluid.cの中の

static int runSimulationCallback(void *data, int status, int frame)

がセットされています。この関数の先頭にブレイクポイントを仕掛けてみましょう。すると予想通り別スレッドから呼ばれている事がわかりました。ブレイクポイントに引っかかっているので、どの関数からコールバックが呼ばれているのかもわかります。

「elbeem」ってなんだ??

どうもさっきからelbeemSimulationSettingsやelbeemRunSimulationCallbackの「elbeem」という名前が目につきます。そしてブレイクポイントを張って調べたrunSimulationCallback()もbf_intern_elbeemフォルダの中のソースから呼ばれています。

この「elbeem」って何なんでしょう?Googleで調べてみると
http://elbeem.sourceforge.net
にたどり着きます。

「El'Beem Is an Open-Source free surface fluid simulation library.」Blenderは流体シミュレーションのためにこのライブラリを採用していたのですね!格子ボルツマン法に基づいたライブラリだそうです。これだけで参考書や論文が何本もありそうですね(^^; こ、ここを追いかけるのは ま、またの機会に・・・|)彡サッ


@fmystB