2009年06月12日

Blenderにvectexを組み込む(2)

前回サーバにアップロードできなかったvectex機能内臓の改造版Blenderの実行ファイルです。
Linux32bit(Blender_modified_Linux32090611.tar.gz)、Linux64bit(Blender_modified_Linux64090611.tar.gz)、Windows32bit(Blender_modified_win32090611.zip)版の3種類です。

Linux版、Windwos版ともにPythonは2.5を使用しています。Windows版はMinGWでのコンパイルのため、FFMpeg、Quicktimeの機能が入っていません。
ソースコードが前回のものと少しだけ変わっていますので、実行ファイルを作成した際に使用したものをこちら(Blender_modified_src090611.zip)に置いておきます。

※追記(2009/07/09) 実行ファイルを修正版と差し替えました。
修正内容は、ベースのBlenderのバージョンを2.49aに変更、ファイルセレクタの追加、Linux版のPythonのバージョンを2.6に変更、などです。
ソースコードは、古い方もそのまま残します。修正版のソースコードはこちら(Blender_modified_src090709.zip)です。

※追記(2009/07/17) 実行ファイルを修正版と差し替えました。
修正内容は、バンプマッピングのサポート、SVGファイル名の相対パスへの対応、その他、若干のバグ修正、です。
修正版のパッチファイルはこちら(Blender_agg_vectex_patch090717.zip)です。

今回の記事の内容は、前回の記事でBlenderに組み込んだvectexのソースコードを修正して、plugin版とほぼ同じように動くようにするまでの流れを書いてみます。

○CLAMPマクロ
前回の記事でBlenderにvectexのソースコードを組み込んでみましたが、この状態でSconsを使ってコンパイルすると以下のようなエラーが出ます。
pic090611_01.jpg

1071行目付近で構文エラーが起こっているようです。
ソースコードを見てみるととくに問題はなさそうで、どこが悪いのかしばらく悩みました。  
pic090611_02.jpg

1071行目のコードにあるCLAMP()という命令文は、すべて大文字になっていることからも推測できますが、関数ではなくてマクロです。
ここで使用されているマクロはutil.hでテクスチャプラグイン用に用意されているものです。
#define CLAMP(val, low, high) ((val>high)?high:((val<low)?low:val))
そして、Blenderのソースコードを調べてみると、source/blender/blenkernel/BKE_utildefines.hというファイルに同じ名前のマクロが見つかりました。
pic090611_03.jpg

マクロの内容を見てみると、if文を使っていてutil.hに書かれているものとは少し展開のされ方が違っているようです。
若干違ってはいますが、すぐ下のCLAMPIS(a,b,c)というものの方が展開のされ方が近いようですので、コンパイルエラーが起こっている部分のCLAMPマクロをCLAMPISに置き換えてみました。
void vtex_sample(VtexInstance *instance, float *texvec, int power, int interp, double *pr, double *pg, double *pb)
{
        ...

        /* extend: copy the color of edge pixels */
        x = CLAMPIS(x, 0, resolution - 1);
        y = CLAMPIS(y, 0, resolution - 1);
        /* clip: alpha=0 outside edges */
        ...
}
○dxt、dyt
これで、とりあえず最後までSconsのビルドが進むようになりました。
早速、作成されたBlender実行ファイルを動かしてみると、Textureパネルのテクスチャ選択メニューにvectexの項目が追加されていて、vectexのパネルを表示することができます。
しかし、テキスト入力の部分でSVGファイルを指定して読み込もうとすると、フリーズして動かなくなりました。
pic090611_04.jpg

ここから先は、ソースコードを眺めているだけだとなかなか問題の原因を突き止めるのは難しくなります。
ソースコードのあちこちにprintf()などを組み込んで、どこまで正常に動作しているのか確認することでもできますが、デバッガを使うとより簡単にプログラムの動作を調べることができます。
(私が実際にこの部分のソースコードを修正したときには、vectex.hにあるMSGマクロをあちこちに書き込んでコンソールに出力される内容で動作確認しています。その後、さらにややこしい問題に直面し、この方法でのデバッグには限界を感じてデバッガを使用することにしました。)

BlenderをSconsでビルドする場合、
scons BF_DEBUG=1
というようにBF_DEBUGオプションを指定することで、デバッグ用のシンボルを埋め込んだ実行ファイルを出力できます。
pic090611_05.jpg

私の使っているUbuntuはGnomeデスクトップのものでKDE環境ではないのですが、KDbvgというデバッガを使ってみました。
デバッガを起動するとファイルメニューから実行ファイルを読み込むことができますので、デバッグオプションを付けてビルドしたBlenderの実行ファイルを読み込みます。自動的にBlenderの最初に起動する部分のソースコードが表示されますが、必要に応じて他のソースコードも読み込むことができます。
pic090611_06.jpg pic090611_07.jpg

デバッガを使用すると無限ループなどでフリーズするような場合でも、ソースコードのどの部分で処理が止まっているのかを調べることができます。
デバッガからBlenderを起動して、vectexを使用するまで通常通りに操作します。フリーズしたら、デバッガの「ブレーク」という機能を使用してプログラムを一時停止します。このとき表示される部分がフリーズを起こしている部分になります。
pic090611_08.jpg

vectex()関数の中のfor()文で変数powerの値を変更している部分でフリーズが起こっていることが分かりました。
そして、その原因はvectex()関数の引数として読み込んでいるdxt[]、dyt[]の値が正しく得られていないことにあるようです。

ソースコード、texture.cの中でvectex()関数のすぐ下にはplugintex()という関数があります。
テクスチャプラグインのvectexを実行しているときには、このplugintex()という関数が呼ばれているはずなので、この関数の中でdxt[]、dyt[]がどのように処理されているのかを調べると、原因について何か分かるかもしれません。
pic090611_09.jpg

plugintex()のソースコードを見てみると、この部分でosatexという変数の値に応じてpluginTexDoit()関数にdxt、dytの値をそのまま渡すか、代わりに0を渡すかを切り替えているようです。
dxy、dytはfloat値の配列(ポインタと同じようなもの)ですから、osatexが真(!=0)のときアドレスを渡し、偽(=0)のときはアドレスが無効であることを伝えるために0を渡しているようです。
今回作成したvectex()関数でも、同じようにosatexの値に応じてdxt、dytの値を変更するコードを追加することで、テクスチャプラグインのvectexと同じように「if (dxt && dyt)」の部分を動作させることができるはずです。
static int vectex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, TexResult *texres)
{
    ...
    if (osatex)
    {
    }
    else
    {
        dxt = 0;
        dyt = 0;
    }

    ...
}
ということで、こんな感じのコードを追加しました。もう少し違う書き方もできるはずですが、とりあえずなるべく元々あるソースコードには変更を加えずに、分かりやすくすることを第一に考えていますので、これはこれでいいことにします。
このようにosatexという変数が必要になったため、vectex()関数の引数に「int osatex」を加えてあります。
pic090611_10.jpg pic090611_11.jpg

○osatex
これで、テクスチャプレビューにSVG画像が表示され、普通にレンダリングもできるようになりました。
ところが、色々とカメラ設定を変えてレンダリングしてみると、ちょっと問題があることに気が付きます。
vectex付属のサンプルファイルtest-animated.blendをレンダリングしてみると、アニメーションの最後の方で本来なら虎の目の部分にカメラが近づいたときに「Blender Vector Texture!」という文字がはっきり表示されるはずですが、画像がぼけたようになり文字がまったく読めません。
pic090611_19.jpg pic090611_20.jpg

デバッガで調べてみると、vectex()関数の中ではosatexの値が常に0でそれ以外の値になることがありません。
plugintex()関数の中でのosatexの値の変化を見てみると、テクスチャプラグインを読み込んでテクスチャプレビューを表示するときにはosatex=0となりますが、レンダリングを実行するとosatex=512などになり、0以外の値になっています。
pic090611_14.jpg

ところが、vecetx()関数の中でのosatexの値はレンダリング時でも0のままです。
grepコマンドを使っていろいろと調べた結果、TEX_PLUGINとOSAが関係していそうなコードが/source/blender/blenkernel/intern/material.cに見つかりました。
pic090611_15.jpg

ELEM3()というのはマクロのようです。
grepコマンドで調べると、/source/blender/blenkernel/BKE_utildefines.hに定義が見つかります。
pic090611_16.jpg

似たようなマクロがたくさんありますが、一番最初の引数が2番目以降のもののいずれかと同じかどうかを調べるためのもののようです。
先ほどのmaterial.cの中のコードは、テクスチャのタイプがイメージかプラグインか環境マップのときにma->texcoに512とのビットマスクのORを設定するというような内容で、テクスチャプラグインでosatex=512となっているのはこのコードが行っているようです。
もしそうであれば、このコードのテクスチャタイプにTEX_VECTEXを追加することで、vectex()関数にosatexが渡されるときにテクスチャプラグインを使っているときと同じ値が入るようになりそうです。
static void do_init_render_material(Material *ma, int r_mode, float *amb)
{
            ...
            if(r_mode & R_OSA) {

                if ELEM4(mtex->tex->type, TEX_IMAGE, TEX_PLUGIN, TEX_ENVMAP, TEX_VECTEX) ma->texco |= TEXCO_OSA;
            }
            ...
}

この修正を行ってビルドしたBlenderでは、レンダリング時にosatex=512という値が得られるようになりました。
test-animated.blendをレンダリングしてみると、虎の目の中に置かれているテキストがはっきりとレンダリングされるようになっています。
pic090611_17.jpg pic090611_18.jpg

○.blendファイルの読み込み
元のvectex.cのソースコードには、plugin_instance_init()という関数が使われています。
これは、Blenderのテクスチャプラグインが用意している関数のうちの一つで、vectexではこの関数の中に.blendファイルを開いたときに無効なInstance構造体へのポインタがメモリアクセス違反を起こすのを防ぐコードが書かれています。
pic090611_21.jpg
これがないとvectexを使って作成された.blendファイルを開いた時、すぐにBlenderが落ちるという状況になります。
plugin_instance_init()に相当する機能はBlenderの内部テクスチャには用意されていませんので、自力で同じような処理を作成する必要があります。

Blenderで.blendファイルを開くときの処理が書かれているのは、
/source/blender/blenloader/intern/readfile.c
というファイルです。このファイルにdirect_link_texture()という関数があり、.blendファイルを開いたときにその中でテクスチャが使われていた場合に、必要な処理を記述できるようになっています。
テクスチャプラグインのplugin_instance_init()関数も、この部分で実行されるようになっています。
static void direct_link_texture(FileData *fd, Tex *tex)
{
    ...
    if(tex->vtex_instance_data){
        tex->vtex_instance_data= NULL;

    }
    ...
}
このような感じでInstance構造体へのポインタをクリアするコードを追加しました。

○デフォルト値の設定
Textureパネルで新規に作成するテクスチャタイプを選択すると、作成されるパネルの数値データにはあらかじめ設定されたデフォルト値が入っています。
pic090611_22.jpg pic090611_23.jpg

テクスチャプラグインにはボタンの設定を行うVarStructという構造体にデフォルト値を指定できるようになっています。しかし、Blenderの内部のボタンを作成するuiDefBut()という関数にはデフォルト値を指定する機能はありません。

Blenderのソースコードの中でTex構造体のメンバ変数にデフォルト値を設定している場所を探してみると、default_tex()という関数として用意されています。この部分にvectexのためのコードを以下の用に追加しました。
/source/blender/blenkernel/intern/texture.c
void default_tex(Tex *tex)
{
    ...
    tex->vn_distm = 0;
    tex->vn_coltype = 0;
    /* vectex */
    tex->vtex_enable = 1;
    tex->vtex_mipmap = 1;
    tex->vtex_interpol = 1;
    tex->vtex_tex_level = 0;
    tex->vtex_mem_max = 20;
    tex->vtex_mem = 0;

    ...
}
ただ、ここにvectex用の変数の設定を書いても、Textureパネルからvectexを選択したときに表示されるパネルには結果が反映されません。
この関数に加えた変更は、テクスチャプレビューのパネルにある「Default Vars」というボタンを押すと、vectexパネルの状態をデフォルト状態に変わることで確認できます。
pic090611_24.jpg

Blenderにもとから用意されているプロシージャルテクスチャのパネルは、あらかじめ設定してあるデフォルト値がセットされた状態で開きますが、その設定がどこで行われているのかちょっと悩みました。
grepコマンドで調べると、上記のdefault_tex()関数と、/source/blender/blenloader/intern/readfile.cのdo_versions()という関数の中でTex構造体のメンバ変数に値を設定しているコードが見つかります。

今回Tex構造体に追加したvectex用の変数を、do_version()関数で最新のバージョン2.49よりも読み込んだデータのバージョンが小さい場合にデフォルト値をセットするように記述しました。
static void do_versions(FileData *fd, Library *lib, Main *main)
{
    ...
    if (main->versionfile < 249) {
        Scene *sce;
        Tex *tex;
        for (sce= main->scene.first; sce; sce= sce->id.next)
            sce->r.renderer= 0;

        for(tex= main->tex.first; tex; tex= tex->id.next) {
            /* vectex */
            tex->vtex_enable = 1;
            tex->vtex_mipmap = 1;
            tex->vtex_interpol = 1;
            tex->vtex_tex_level = 0;
            tex->vtex_mem_max = 20;
            tex->vtex_mem = 0;
        }       
    }
    ...
}
これで、Blender249より前に作成された.blendファイルを読み込んだとき、あるいはまったくファイルを読み込まずにBlender実行ファイルに組み込まれたデータを読み込む場合に、デフォルト値が設定された状態でvectexのパネルが開くようになります。

もし、今後2.49よりも新しいバージョンにvectexを組み込む場合は、このdo_version()の記述はそのままにして、代わりにBlender実行ファイルに内臓されているB.blendファイルのデータ(/source/blender/src/B.blend.c)をvectexのデフォルト値が設定されたものと入れ替えることで対応するようにします。
たとえばVer2.50でvectexを組み込む場合、上記の変更をさらに修正して(main->versionfile < 250)のときにvectexのデフォルト値を設定するというコードを書いてしまうと、バージョン2.49としてvectexを使用して保存したファイルを読み込んだときに、保存されているデータの設定値をすべてデフォルト値で上書きしてしまうことになり、正しくデータを読み込めなくなります。
posted by mato at 02:53| Comment(0) | Blender Vectex | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。