2009年06月02日

Blenderにvectexを組み込む

前回、vectexが使用しているライブラリをSconsを使用したBlenderのビルドの過程に組み込みました。今回は、それらの組み込んだライブラリを使用する形でvectex本体の方をBlenderに組み込んでいきたいと思います。

基本的には、5/6の記事で書いた「プロシージャルテクスチャの追加」を参考にして作業を進めていきます。
修正するBlenderのソースファイルもほとんど同じものとなります。
  1. 変数の定義: DNA_texture_types.h
  2. 描画処理: texture.c
  3. UIの作成: buttons_shading.c
元になるvectexのソースファイルはvectex.c一つだけです。
実際にプログラミングを行っている時はBlender2.48aのソースコードを使っていましたが、つい最近Blender2.49がリリースされましたので、この機会にソースコードをBlender2.49を使ったものに書き直しています。

こちら(Blender_modified_src090602.zip)にソースコードをまとめておきます。
実行ファイルの方はブログのサーバーで使用できる容量の追加申請中のため、次回にアップロードするつもりです。(Seesaaでは2GBまで使用できるのですが、実際には100MB単位で割り当てられます。現在残量が20MBくらいしかありません。)

修正内容がかなり多く、全部をまとめて説明すると長くなりすぎると思いますので、今回と次回の2回に分けることにしました。今回はvectex.cの内容をBlenderの内部に移す部分のみに絞り、次回は追加したソースコードを実際に動くようにするために必要な修正内容を書くつもりです。

○変数の定義(/blender/source/blender/makesdna/DNA_texture_types.h)
Blenderのテクスチャのパネルに表示されるパラメータを保持する変数は、設定した値を.blendファイルに保存したり、IPOカーブで編集できるようにしたり、pythonから操作できるようにしたりといった特別な処理が必要なため、「Tex」という一つの構造体の中にまとめられています。

今回は、vectex.cの84行目から100行目にかけて記述されているCast構造体のメンバ変数全てを、Tex構造体のメンバとして追加します。
それ以外に、vectex.cの38行目から53行目にかけて記述されているTile構造体、Instance構造体という2つの構造体をTex構造体経由で参照できるようにポインタを追加します。
これらの構造体については.blendファイルに保存したりする必要はないので、別の場所に記述した方がいいのかもしれませんが、今回は分かりやすさ優先で同じ場所にまとめてみました。

BlenderのソースコードはほとんどがCで書かれていて、変数や構造体、関数の名前などにvectexで使用されているものをそのまま使うと既存の名前と重複してしまう可能性があるため、頭に「Vtex」というような文字列を追加することにしました。

ヘッダファイルを追加しています。
struct Tex;
struct Image;
struct PreviewImage;
struct ImBuf;
struct VtxTile;
struct VtxInstance;

構造体を2つ追加しています。
typedef struct VtexTile
{
    unsigned char *image;
    unsigned int   age;
    float pad;
} VtexTile;

typedef struct VtexInstance
{
    void         *backend_data;
    int           tile_set_max_power;
    int           tile_memory;
    unsigned int  timestamp;
    unsigned char current_base_color[4];
    char          current_file_name[160];
    struct VtexTile         *tile_set;
} VtexInstance;

Tex構造体に変数を追加します。
typedef struct Tex {
    ID id;
   
    float noisesize, turbul;
    float bright, contrast, rfac, gfac, bfac;
    float filtersize;
    ...   
    /* vectex */

    int vtex_enable, vtex_mipmap, vtex_interpol, vtex_tex_level, vtex_mem_max, vtex_mem;
    unsigned char vtex_base_color[4];
    char vtex_file_name[160];


    float pad1;

    struct VtexInstance *vtex_instance_data;
   
    struct ImageUser iuser;
   
    struct bNodeTree *nodetree;
    struct Ipo *ipo;
    struct Image *ima;
    struct PluginTex *plugin;
    struct ColorBand *coba;
    struct EnvMap *env;
    struct PreviewImage * preview;
   
    char use_nodes;
    char pad[7];
   
} Tex;

vectexのCast構造体には、label_dummyという変数がありますが、これらは必要なくなるため省略しました。
また、テクスチャのファイル名を保持するchar型のfile_name[64]という配列は、より長いファイル名を格納できるようにサイズを64から160に変更してみました。
flat型のpad1という変数は、構造体を8byteのアライメントに合わせるためのダミーデータです。

さらに、このファイルの最後の方にある定数の定義にもいくつか修正を加えます。
/* **************** TEX ********************* */

/* type */
#define TEX_CLOUDS        1
...
#define TEX_DISTNOISE    13
#define TEX_VECTEX        14
こちらはテクスチャタイプの判別用の定数です。
/* **************** vectex ******************* */
#define VTEX_TILE_SIZE       256
#define VTEX_TILE_SIZE_POWER 8
#define VTEX_TILE_BORDER     1
#define VTEX_MAX_TIMESTAMP   5000

/* Special tile age values */
enum {VTEX_UNUSED = 0, VTEX_CONST_COLOR, VTEX_START_VALUE};

これらは、vectexで使用されている定数です。名前の頭に「VTEX_」を加えています。

○描画処理 (/blender/source/blender/render/intern/source/texture.c)
このファイルにvectex.cの関数をすべて組み込んでしまいます。
まず、ヘッダファイルのインクルード文を追加します。
#include "MEM_guardedalloc.h"
#include "vectex.h"

続いて関数です。
ほとんどの部分はコピーアンドペーストでそのまま張り付けただけですが、関数名、変数名の頭に「vtex_」を追加している部分をすべて修正しています。また、Cast構造体のメンバの変数を使っている部分はTex構造体に変更してあります。
あまりに長くなりすぎるので、ブログの記事中には目立つ修正部分のみを取り上げます。

・メモリ処理関数の変更
テクスチャプラグインのvectexではBlenderからエクスポートしている関数を使用していますが、それらをすべてBlenderの内部用の関数名に修正します。
ただし、名前は変わっても実際に呼び出されているのは同じ関数です。

void create_tile_set(Instance *instance)
void create_instance(Cast *cast)
これらの関数ではメモリ確保の部分にテクスチャプラグイン用にエクスポートされているmallocN()を使っている部分があったので、Blenderの内部用のMEM_mallocN()に書き換えました。
void vtex_create_tile_set(VtexInstance *instance)
{
    ...
    instance->tile_set = (VtexTile *) MEM_mallocN(sizeof(VtexTile) * size, "vectex:tileset");
    ...
}

void limit_tile_set(Instance *instance, int maximum)
void destroy_tile_set(Instance *instance)
void destroy_instance(Cast *cast)
int get_tile(Instance *instance, int x, int y, int power)
これらの関数では、メモリの解放の関数を使う部分でmemory_free()をMEM_freeN()に修正しています。
void vtex_limit_tile_set(VtexInstance *instance, int maximum)
{
        ...
        MEM_freeN(instance->tile_set[index].image);
        ...
}
・スレッドのロック
テクスチャプラグインのvectexでマルチスレッドレンダリングに対応するためには、スレッドをロックする関数をsample()という関数内に記述する必要がありました。このスレッドをロックする関数もBlender内部用のものを使用します。
void vtex_sample(VtexInstance *instance, float *texvec, int power, int interp,
            double *pr, double *pg, double *pb)
{
        ...       
        BLI_lock_thread(LOCK_IMAGE);
        int index = vtex_get_tile(instance, x, y, power);
        BLI_unlock_thread(LOCK_IMAGE);
        ...       
}

・vectex()関数
vextex()という関数は元はplugin_tex_doit()という関数名でしたが、Blenderの内部では各プロシージャルテクスチャの名前を使っているためこのように変更しました。

テクスチャプラグインではplugin_tex_doit()関数にint型のstypeという変数が渡されていて、プラグインのパネルのモード切り替えに使用しています。Blenderの内部ではこれに相当する変数は特に用意されていません。
もともとvectexではstypeは使用していませんが、plugin_tex_doti()関数の一番最初にこのstypeを含む行があるのでコメントアウトしています。
    /* stype = 0;  prevent unused variable warning */
テクスチャプラグインの関数とBlenderの内部のテクスチャ関数では計算結果を返すために使用するデータが少し違っています。
テクスチャプラグインではfloat型のresultという配列を使用していますが、Blender内部のテクスチャ関数ではTexResultという構造体を使っています。

result[0]    : texres->tin
result[1]    : texres->tr
result[2]    : texres->tg
result[3]    : texres->tb
result[4]    : texres->ta
static int vectex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, TexResult *texres)
{
        ...
        /* Return a black pixel */
        texres->tin = texres->ta = 1.0;
        texres->tr = texres->tg = texres->tb = 0;
        ...
        /* Return a red pixel */
        texres->tin = texres->tr = texres->ta = 1.0;
        texres->tg = texres->tb = 0;
    ...

    texres->tin = 1.0;
    texres->tr = r;
    texres->tg = g;
    texres->tb = b;
    texres->ta = 1.0;
   
    return (TEX_INT | TEX_RGB);
}
・multitex()関数
この関数の中にテクスチャの処理を行うvectex()関数を呼び出すコードを追加しています。
static int multitex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, TexResult *texres)
{
    ...
    case TEX_VECTEX:
        retval = vectex(tex, texvec, dxt, dyt, osatex, texres);
        break;

    ...
}
実は、色々とあって上記のような修正を加えても、いくつかの関数はこのままではきちんと動きません。
どの関数にどんな修正が必要なのかは、次回の記事で詳しく書くつもりです。
(2009/6/4 追記:一部修正内容が抜けていたのを修正しました。)

○/source/blender/render/intern/SConscript
上記の「/source/blender/render/intern/source/texture.c」にvectex.cのコードを追加したため、コンパイル時に読み込むインクルードファイルを追加するようにビルド設定ファイルにも修正が必要となります。
#!/usr/bin/python
Import ('env')

cflags=''
sources = env.Glob('intern/source/*.c')

incs = 'intern/include #/intern/guardedalloc ../blenlib ../makesdna'
incs += ' extern/include ../blenkernel ../radiosity/extern/include ../imbuf'
incs += ' ../include ../blenloader'
incs += ' #/extern/aggsvg'

defs = []
....

○UI(ユーザーインターフェイス)の作成(/source/blender/src/buttons_shading.c)
・texture_panel_vectex()関数(line:46-768)
vectex用のパネルの設定を行う処理です。テクスチャプラグインでは構造体VarStructの配列varstr[]に各ボタンの設定を記述していました。そのパラメータをほとんどそのままuiDefBut()関数の引数に設定する感じです。ただし、デフォルト値についてはuiDefBut()関数には設定することができないようです。
static void texture_panel_vectex(Tex *tex)
{
    uiBlock *block;
    block= uiNewBlock(&curarea->uiblocks, "texture_panel_vectex", UI_EMBOSS, UI_HELV, curarea->win);
    if(uiNewPanel(curarea, block, "vectex", "Texture", 640, 0, 318, 204)==0) return;
    uiSetButLock(tex->id.lib!=0, ERROR_LIBDATA_MESSAGE);

    uiBlockBeginAlign(block);
    uiDefButI(block, TOG, B_TEXPRV, "Enable", 10, 180, 150, 18, &tex->vtex_enable, 0, 1, 0, 0, "Enable vector texture (disabling frees memory)");
    uiDefButI(block, TOG, B_TEXPRV, "MipMap", 10, 160, 150, 18, &tex->vtex_mipmap, 0, 1, 0, 0, "Uses mipmap averaging");
    uiDefButI(block, TOG, B_TEXPRV, "Interpol", 10, 140, 150, 18, &tex->vtex_interpol, 0, 1, 0, 0, "Interpolates nearby pixels");
    uiDefButI(block, TOG, B_TEXPRV, "Tex Level", 10, 120, 150, 18, &tex->vtex_tex_level, 0, 1, 0, 0, "Displays texture detail as hue");
    uiDefButI(block, NUM, B_TEXPRV, "Mem Max", 10, 100, 150, 18, &tex->vtex_mem_max, 0, 200, 10, 0, "Maximum memory to use (in megabytes, 0=no limit)");
    uiBlockEndAlign(block);
   
    uiBlockBeginAlign(block);
    uiDefButI(block, NUM, B_TEXPRV, "Mem:", 160, 180, 150, 18, &tex->vtex_mem, 0, 999, 10, 0, "Displays current memory being used (in megabytes)");
    uiDefBut(block, LABEL, 0, "Base Color", 160, 160, 150, 18, 0, 0.0, 0.0, 0, 0, "Base texture color");
    uiDefBut(block, COL|CHA, B_TEXPRV, "", 160, 140, 150, 18, &tex->vtex_base_color, 0, 0, 0, 0, "Base texture color");
    uiDefBut(block, LABEL, 0, "File Name", 160, 120, 150, 18, 0, 0.0, 0.0, 0, 0, "SVG file name");
    uiDefBut(block, TEX, B_TEXPRV, "", 160, 100, 150, 18, &tex->vtex_file_name, 0.0, 159.0, 0, 0, "SVG file name");
    uiBlockEndAlign(block);
}

・texture_panel_texture()関数(line:1727)
Textureパネルでテクスチャを選択するためのメニューに「vectex」の項目を追加します。
static void texture_panel_texture(MTex *actmtex, Material *ma, World *wrld, Lamp *la, bNode *node, Brush *br, SculptData *sd)
{
        ...
        if( !tex->use_nodes ) {
            sprintf(textypes, "Texture Type %%t|None %%x%d|Image %%x%d|EnvMap %%x%d|Clouds %%x%d|Marble %%x%d|Stucci %%x%d|Wood %%x%d|Magic %%x%d|Blend %%x%d|Noise %%x%d|Plugin %%x%d|Musgrave %%x%d|Voronoi %%x%d|DistortedNoise %%x%d|Vectex %%x%d", 0, TEX_IMAGE, TEX_ENVMAP, TEX_CLOUDS, TEX_MARBLE, TEX_STUCCI, TEX_WOOD, TEX_MAGIC, TEX_BLEND, TEX_NOISE, TEX_PLUGIN, TEX_MUSGRAVE, TEX_VORONOI, TEX_DISTNOISE, TEX_VECTEX);
            uiDefBut(block, LABEL, 0, "Texture Type",        160, 150, 140, 20, 0, 0.0, 0.0, 0, 0, "");
            uiDefButS(block, MENU, B_TEXTYPE, textypes,    160, 125, 140, 25, &tex->type, 0,0,0,0, "Select texture type");
        }
        ...
}
・texture_panels()関数(line:4677-4679)
Textureパネルでvectexが選択されたときに、vectexのパネルを表示するための処理をする関数texture_panel_vectex()を呼び出す部分です。
void texture_panels()
{
    ...
            switch(tex->type) {
            case TEX_IMAGE:
                texture_panel_image(&tex->ima, &tex->iuser);
                texture_panel_image_map(tex, mtex);
                break;
            ...
            case TEX_VECTEX:
                texture_panel_vectex(tex);
                break;

            }
        }
    }
}
これでvextex.cのソースコードをBlenderに組み込む作業自体は完了しましたが、このままコンパイルしてもエラーが出てしまって動きません。
さらに、そのエラーを修正してコンパイルが完了するようになっても、それだけではvectexの機能を使えるようになりません。
次回、あれこれ必要な修正の内容について、記事に書きたいと思います。
posted by mato at 21:57| Comment(0) | Blender Vectex | このブログの読者になる | 更新情報をチェックする

2009年05月21日

Blenderにライブラリ(AGG、Expat)を組み込む

今回からBlender本体にSVGテクスチャプラグインvectexの機能を組み込む作業に取りかかります。まずは、vectexが使用している2つのライブラリ、AGGとExpatをBlenderのコードから利用できるようにしてみます。

Blenderには様々なライブラリが使用されています。
分かりやすいところではJpeg、PNGなどの画像ファイルを扱うためのライブラリや、動画の処理をするFFmpegなどが使われています。
Blenderのソースコードの中に入っていて静的にリンクされているものと、システムにインストールされているDLL、シェアードライブラリに動的にリンクされるものがあります。

ライブラリを組み込む場合、このどちらの方法でリンクするかによって必要な処理が若干変わってきますが、今回の場合は、
  1. AGGはもともとDLL、シェアードライブラリとして使うことを想定したライブラリではないので、静的リンクで使用する。
  2. Expatは動的リンクを使うことが可能だが、もともとvectexで静的リンクで使用しているので、同じように静的リンクで使用する。
という感じで、どちらのライブラリもソースコードをBlenderの一部に組み込んで、Blenderのビルド時にまとめてコンパイルするようにします。

vectexを組み込む改造版BlenderのビルドにはSconsを使用する方針です。
とりあえずはSconsの設定ファイルだけを修正していき、CMakeなどの設定ファイルについては余裕ができたら少しずつ対応していくつもりです。

ちなみに今回もLinuxのみに限定して話を進めていきます。
実は、vectexのプラグインについては、Windows上のSconsではまだコンパイルに成功していません。BlenderについてはなんとかMinGW上のSconsでビルドできていますので、改造版BlenderがLinuxで動作できるようになったらWindowsにも対応できると思います。

使用するBlenderのバージョンは今のところ2.48aですが、2.49がリリースされた時点でそちらに移行していくつもりです。
今回作成または修正した部分のソースコードをこちら(src090521.zip)にまとめました。

○ライブラリのソースコードを組み込む
Sconsでビルドを行うための設定ファイルの修正を始める前に、まずAGG、ExpatのソースコードをBlenderのソースディレクトリのどこに置くのかを決める必要があります。

Blenderのトップディレクトリにはextern、internというディレクトリがあり、その中にいくつかのライブラリが置かれています。
どのようなものがどちらに置かれるべきなのか詳しいことはよく分からないのですが、とりあえずexternディレクトリの下に置くことにしてみます。後で別の場所に置くべきだと分かれば、変更するかもしれません。
pic090521_01.jpg

vectexが使用するライブラリ自体はAGG、Expatの2つなのですが、AGG SVG Viewerの部分のソースコードもライブラリのように扱ってしまった方が構成がすっきりするように思いますので、今回はこの部分をaggsvgというディレクトリにまとめることにします。

ついでにvectex本体のソースコードファイル3つのうちvectex_agg.cpp、vectex.hの2つも、ここに入れてしまうことにします。
vectex_agg.cppは、その拡張子を見て分かる通り「C++」のソースコードですが、Blenderのソースコードは外部のライブラリやゲームエンジン以外は基本的に「C」のソースコードで書かれています。C++のコードとCのコードが混在している場合、ビルド時のコンパイルオプションなどが若干面倒になる可能性もあるため、ちょっと不自然な感じもしますが、今回はこのような感じにファイルを配置することにしました。

ソースコードの配置については、後はvextex.cのソースコードをBlenderのプロシージャルテクスチャをまとめてあるソースファイル「blender/source/blender/render/intern/source/texture.c」の中にまるごと組み込むだけとなります。
pic090521_02.jpg

○Sconsのビルド用設定ファイル

BlenderをSconsでビルドするための設定ファイルは、トップディレクトリにあるSConscriptファイル以外にもいくつか重要なファイルがあります。
Blenderのトップディレクトリにある「config」フォルダには、ビルドを行う各OSに対応するための「.py」(Python)ファイルがあります。また、同じくトップディレクトリにある「tools」フォルダにもいくつかの「.py」ファイルがあって、こちらもSconsのビルドの時に使用されます。

新規にライブラリを追加するために修正が必要となるのは、「config」フォルダ内のファイル(今回はLinux限定なのでlinux2-config.pyのみ)、「tools」フォルダ内のBlender.pyとbtools.pyです。
pic090521_04.jpg pic090521_05.jpg

具体的にどこをどう修正すればいいかについては、Blenderトップディレクトリにある「doc」フォルダのblender-scons-dev.txtというファイルに詳しく書かれています。
pic090521_03.jpg

○config/linux2-config.py
本来はすべてのプラットフォームでビルドできるようにするためconfigフォルダ内のすべてのXXX-config.pyファイルを修正するはずなのですが、今回はとりあえずLinux以外については後回しとしてlinux2-config.pyのみを修正することにします。
...
#for vectex SVG texture
WITH_BF_VECTEX = True
BF_AGGSVG = '#extern/aggsvg'
BF_AGGSVG_INC = '${BF_AGGSVG}/include'
BF_AGGSVG_LIB = 'extern_aggsvg'
BF_AGGSVG_LIBPATH = '${BF_AGGSVG}/lib'

BF_AGG = '#extern/agg'
BF_AGG_INC = '${BF_AGG}/include'
BF_AGG_LIB = 'extern_agg'
BF_AGG_LIBPATH = '${BF_AGG}/lib'

BF_EXPAT = '#extern/expat'
BF_EXPAT_INC = '${BF_EXPAT}/include'
BF_EXPAT_LIB = 'extern_expat'
BF_EXPAT_LIBPATH = '${BF_EXPAT}/lib'

...
AGG、EXPAT、AGGSVGのそれぞれについて、ライブラリのベースとなる場所、インクルードファイルの場所、ライブラリ名、ライブラリファイルの場所を指定するためのSconsのオプションを追加しています。
また、今回追加するライブラリをビルド時にオン/オフ切り替えできるようにするためのオプションとしてWITH_BF_VECTEXというものも追加しました。
他のライブラリを見ると、それぞれのライブラリ毎にWITH_BF_XXXオプションを付けているようですが、今回はどれかひとつだけをオフにできるようにしても意味がなさそうなので、ひとつにまとめてしまいました。

○tools/btools.py

このファイルにはライブラリをビルドするためのオプションを全て列挙している記述が2ヶ所ありますので、今回追加したオプションの分を追加します。
def validate_arguments(args, bc):
    opts_list = [
            ...
            'WITH_BF_VECTEX', 'BF_AGGSVG', 'BF_AGGSVG_INC', 'AGGSVG_LIB', 'AGGSVG_LIBPATH',
            'BF_AGG', 'BF_AGG_INC', 'BF_AGG_LIB', 'BF_AGG_LIBPATH',
            'BF_EXPAT', 'BF_EXPAT_INC', 'BF_EXPAT_LIB', 'BF_EXPAT_LIBPATH',

            ...
def read_opts(cfg, args):
    localopts = Options.Options(cfg, args)
    localopts.AddOptions(
        ...
        (BoolOption('WITH_BF_VECTEX', 'Use vectex if tue', True)),
        ('BF_AGGSVG', 'AGGSVG base path', ''),
        ('BF_AGGSVG_INC', 'AGGSVG include path', ''),
        ('BF_AGGSVG_LIB', 'AGGSVG library', ''),
        ('BF_AGGSVG_LIBPATH', 'AGGSVG library path', ''),
   
        ('BF_AGG', 'AGG base path', ''),
        ('BF_AGG_INC', 'AGG include path', ''),
        ('BF_AGG_LIB', 'AGG library', ''),
        ('BF_AGG_LIBPATH', 'AGG library path', ''),
   
        ('BF_EXPAT', 'EXPAT base path', ''),
        ('BF_EXPAT_INC', 'EXPAT include path', ''),
        ('BF_EXPAT_LIB', 'EXPAT library', ''),
        ('BF_EXPAT_LIBPATH', 'EXPAT library path', ''),

        ...
○tools/Blender.py
このファイルでは、ビルド時のライブラリオプションに関する記述を修正します。
こちらはライブラリの場所の記述です。
## TODO: static linking
def setup_staticlibs(lenv):
    statlibs = [
        #here libs for static linking
    ]
    ...
    if lenv['WITH_BF_VECTEX']:
        libincs += Split(lenv['BF_AGGSVG_LIBPATH'])
        libincs += Split(lenv['BF_AGG_LIBPATH'])
        libincs += Split(lenv['BF_EXPAT_LIBPATH'])

    ...
こちらはライブラリ名です。
def setup_syslibs(lenv):
    syslibs = [
       
        lenv['BF_JPEG_LIB'],
        lenv['BF_PNG_LIB'],
        lenv['BF_ZLIB_LIB']
        ]
    ...
    if lenv['WITH_BF_VECTEX']:
        syslibs += Split(lenv['BF_AGGSVG_LIB'])
        syslibs += Split(lenv['BF_AGG_LIB'])
        syslibs += Split(lenv['BF_EXPAT_LIB'])

    ...
○extern/Sconscript
Sconsの全体に関係する設定ファイルの他に、実際にライブラリのソースファイルを置いた場所にあるSConscriptファイルを修正する必要があります。
WITH_BF_VECTEXがオンのとき、aggsvg、agg、expatのそれぞれの場所に置いたSconscriptファイルを実行します。
#!/usr/bin/python

Import('env')

SConscript(['glew/SConscript'])
...
if env['WITH_BF_VECTEX']:
    SConscript(['aggsvg/SConscript'])
    SConscript(['agg/SConscript'])
    SConscript(['expat/SConscript'])

...
○extern/agg/SConscript
前回の記事でテクスチャプラグインとしてvectexをSconsでコンパイルしたときのSConscriptファイルとはかなり違っています。externフォルダにある他のライブラリのSConscriptを参考にして書き直しました。
#!/usr/bin/python
import sys
import os

Import('env')

sources = env.Glob('src/*.cpp') + env.Glob('gpc/*.c') + env.Glob('src/ctrl/*.cpp')
incs = 'include include/ctrl gpc'
defs = ''
cflags = '-fPIC -c -O3'

env.BlenderLib ( libname='extern_agg',
        sources=sources, includes=Split(incs),
        defines=Split(defs),
        libtype=['blender', 'common'],
        priority=[10, 20], compileflags = Split(cflags))
今回は、各ライブラリのトップディレクトリのSConscriptだけで完結するようにしました。
また、最終的にライブラリを作成する部分はenv.Library()ではなくて、tools/Blender.pyの中にあるBlenderLIb()という関数を使っています。
この関数には、ライブラリ名、ソースファイル、インクルードファイルの場所、コンパイルオプションなどの他に、libtype、priorityという引数を渡しています。
この2つについては、今ひとつよくわからなかったのですが、リンク時に問題が起こった場合に調整するために使用するもので、通常は指定する必要はないようです。

○extern/expat/SConscript
#!/usr/bin/python
import sys
import os

Import('env')

sources = ['lib/xmlparse.c','lib/xmlrole.c','lib/xmltok.c',
    'xmlwf/xmlwf.c','xmlwf/xmlfile.c','xmlwf/codepage.c','xmlwf/unixfilemap.c']
incs = '. lib xmlwf'
defs = ''
cflags = '-fPIC -Wall -Wmissing-prototypes -Wstrict-prototypes -fexceptions -DHAVE_EXPAT_CONFIG_H -c'

env.BlenderLib ( libname='extern_expat',
        sources=sources, includes=Split(incs),
        defines=Split(defs),
        libtype=['blender', 'common'],
        priority=[10, 20], compileflags = Split(cflags))
○extern/aggsvg/SConscript
インクルードファイルの指定に#/intern/guardedallocという指定を追加してあります。
これは、テクスチャプラグインとしてvectexが使用していたメモリ処理関連の関数をBlenderの内部用のものに変更したために、そのメモリ処理の関数のライブラリ用のヘッダファイルの場所を追加しています。
#!/usr/bin/python
import sys
import os

Import('env')

sources = env.Glob('*.cpp')
incs = '. ../agg/include ../expat/lib #/intern/guardedalloc'
defs = ''
cflags = '-fPIC -shared -O -ansi'

env.BlenderLib ( libname='extern_aggsvg',
        sources=sources, includes=Split(incs),
        defines=Split(defs),
        libtype=['blender', 'common'],
        priority=[10, 20], compileflags = Split(cflags))
○extern/aggsvg/vectex.h
テクスチャプラグイン用にエクスポートされているメモリ処理関数を使わなくするために、以下の部分をコメントアウトしてあります。
...
/*
EXT void *memory_alloc(int len, char *str);
EXT void memory_free(void *ptr);

*/

...
○extern/aggsvg/vectex_agg.cpp
Blender内部用のメモリ処理関数が書かれたヘッダファイルをインクルードしています。
vectex.h、vectex.cファイルに書かれているmemory_alloc()を使うのを止めて、MEM_malocN()関数を使うようにしました。
この関数は、テクスチャプラグイン用にutil.hでエクスポートされているmallocN()という関数の本体(/source/blender/blendpluginapi/intern/pluginapi.c)で使用されているのと同じものを使っています。
...
#include "MEM_guardedalloc.h"
...
    /*

    unsigned char          *buf = (unsigned char *)memory_alloc(width * height * 3, "vectex:tile");
    */
    unsigned char          *buf = (unsigned char *)MEM_mallocN(width * height * 3, "vectex:tile");

...

○ビルド
以上のファイルを使って実際にビルドを行うためには、この他にBlender/externディレクトリにagg、expat、aggsvgというフォルダを作成し、vectexのソースコードのフォルダから対応するソースコードをコピーしておく必要があります。
aggsvgディレクトリには、vectexソースコードのトップディレクトリにある「.h」「.cpp」ファイルをすべてコピーします。
pic090521_07.jpg
aggディレクトリには、vectexソースコードのagg-2.5フォルダの中身をまるごとコピーします。
expatディレクトリには、vectexソースコードのexpat-1.95.8フォルダの中身をまるごとコピーします。
pic090521_06.jpg pic090521_08.jpg
これで、Blenderにvectexで使用しているライブラリを組み込む作業が完了しました。

この状態で、とりあえずビルドを実行することができます。
vectexの本体のソースコードはまったく組み込んでいませんので、ビルドの結果できあがるのは普通のBlenderです。
ライブラリのビルドの結果は、Blenderのソースコードディレクトリの外部「build/linux2/lib」にlibextern_agg.a、libextern_expat.a、libextern_aggsvg.aという3つのファイルが作成されていることで確認できます。
pic090521_09.jpg pic090521_10.jpg

次回からは、vectex.cのソースコードをBlenderに組み込んでいく予定です。
posted by mato at 01:20| Comment(0) | Blender Vectex | このブログの読者になる | 更新情報をチェックする

2009年05月13日

Sconsでvectexをコンパイルする

前回Blenderへのプロシージャルテクスチャの追加方法を確認できたので、続いて本番のvectexの方に取りかかりたいところですが、その前にもう少し調べておきたいことがありました。
Blenderにvectexを組み込むためには、vectexで使用しているAgg、ExpatなどのライブラリをBlenderのビルドシステムから利用できるようにする必要があります。

BlenderはビルドのためのツールとしてGnu Make、Scons、CMakeの3種類の方法を用意しています。(詳しくは、こちらのページをご参照ください。)

私自身はLinuxでのビルドにSconsを使い、WindowsではCMake + Visual C++を主に使っています。CMakeを使ってAnjuta、KDevelopなどのLinuxのIDEを使う方法、WindowsのMingw + Sconsというような方法でもビルドできることを確認しています。

vectex自体はGnu Makeでコンパイルするようになっていますので、Gnu Makeのビルドシステムを使うのが一番楽にvectexの組み込みができそうですが、残念ながらこの方法ではWindowsでコンパイルするのが難しいようです。
可能であれば3種類のすべてのビルドシステムに対応できるようにしたいところですが、とりあえずはSconsを使ってビルドを行うことを前提にvectexの組み込みを進めようと思います。

とは言ったものの、これまでLinuxでのプログラミングにはMakefileを少し書いたことがある程度で、他の方法は試したことがありません。
いきなりBlenderのSconsの設定ファイルを読むのは難しそうですので、もう少し基本的なところから少しずつ慣れていく方がよさそうです。

ということで、まずはvectexをSconsを使ってコンパイルしてみることにしました。(今回は、とりあえずLinux上のみに限定しておきます。)
作成したSconsの設定ファイルをこちら(scons_file090513.zip)にまとめてあります。

○Sconsによるビルドの基本
SconsはPythonを使ったビルドシステムです。

Sconsでビルドを行うには「SConstruct」というファイルを用意し、そのファイルが置かれているディレクトリに移動して、コマンドラインから「scons」と実行します。
それぞれ、Gnu Makeを使う場合の「Makefile」、「make」コマンドと同じような感じで使います。

詳しい使い方は、scons.orgのユーザーズガイドマニュアルをご参照ください。

Makefileの場合は、コマンドラインで入力するコマンドをそのままテキストファイルに記述する感じですが、SConscriptはかなり特殊な記述になります。

伝統的なhello worldで説明すると、
hello.c
#include <stdio.h>
int main(int argc, char** argv)
{
    printf("Hello world.\n");
}
というソースファイルに対して、

Makefile
hello: hello.c
    gcc -o hello hello.c

SConstruct
Program('hello','hello.c')
という感じになります。
pic090512_01.jpg pic090512_02.jpg

○vectexのMakefileをSConstructに置き換える
(single_file)
Agg、ExpatについてはとりあえずそれぞれのMakefileを使ってコンパイルを行うことにして、作成済みのlibagg.a、libexpat.aファイルを使用する形でSConstructファイルを作ってみました。
env = Environment()
env.Append(CCFLAGS = '-fPIC -shared -O -ansi')
env.Append(CPPPATH = ['.', 'blender','agg-2.5/include','expat-1.95.8/lib'])
env.Append(LIBS = ['m', 'c'])
env.Append(LIBS=File('agg-2.5/src/libagg.a'))
env.Append(LIBS=File('expat-1.95.8/.libs/libexpat.a'))
env.LoadableModule('vectex.so', ['vectex.c',  'vectex_agg.cpp','agg_svg_parser.cpp','agg_svg_path_renderer.cpp','agg_svg_path_tokenizer.cpp'])

先ほどのhello worldのときのProgram('hello','hello.c')に当たる部分は一番最後の行です。
普通の実行ファイルを作成する場合はProgram()というメソッドを使いましたが、BlenderのテクスチャプラグインのようなDLL、Shared Library形式のファイルを作成するにはSharedLibrary()またはLoadableModule()というメソッドを使用します。
複数のソースファイルから一つの実行ファイルを作成するような場合、

Program('ターゲット名',['ソースファイル1','ソースファイル2' ... ,'ソースファイルn'])

というようにソースファイルをリストでまとめて指定します。
ヘッダファイルについては自動的に処理されるのでファイル名を指定する必要はありません。

それ以外の部分は、コンパイル時のオプション指定、インクルードファイルの場所の指定、使用するライブラリの指定を行っています。
ライブラリの指定は本来ならば、

env.Append(LIBS = ['m', 'c', 'agg', 'expat'])
env.Append(LIBPATH = ['agg-2.5/src','expat-1.95.8/.libs'])


というような形でライブラリ名、ライブラリの場所をそれぞれまとめて指定するべきだと思うのですが、今回はExpatのライブラリファイルの置かれている場所にスタティックリンク用、ダイナミックリンク用の両方のファイルがあり、ファイルの名前を直接指定する方法を使っています。

Environment()というのはちょっと説明しづらいですが、env1、env2というように複数の環境を用意して、それぞれにデバッグ用、リリース用の設定を行うというような使い方ができるようです。
後述するように、サブディレクトリのコンパイルを行う際にトップディレクトリの設定を引き継ぐというような使い方もできます。

このSConstructファイルをvectexのトップディレクトリに置いて、コマンドラインから「scons」と実行することでlibvectex.soというファイルが作成されます。
vectex付属のMakefileを使うとvectex.soというファイル名でプラグインが作成されますが、sconsではShared Library形式でファイルを作成する場合、Linux上では自動的に名前の頭に「lib」が付くようになっています。
付かないように設定することもできるようなのですが、このままでも特に問題ないと思いますのでそのままにしています。
pic090512_03.jpg pic090512_04.jpg

○Agg、ExpatのSconsでのコンパイル(multi_file)
vectex付属のMakefileではAgg、ExpatのコンパイルについてはそれぞれのMakefileを呼び出す形で行っています。
Blenderのソースコードに含まれているFFmpegやOpenJPEGなどのライブラリを見てみると、SConscriptファイルが置かれていてSconsを使ってコンパイルを行うようになっています。
Blenderにこれらのライブラリを組み込むのであれば、Sconsでコンパイルできるようにした方がよさそうです。

SconsではトップディレクトリにあるSConstructというファイルとは別に、サブディレクトリにSConscriptというファイルを置いて、階層的にビルド設定ファイルを分離することができます。
この機能を使用してAgg、ExpatをSconsでコンパイルできるようにしてみました。
SConstruct
env = Environment()
SConscript(['agg-2.5/SConscript','expat-1.95.8/SConscript'])
env.Append(CCFLAGS = '-fPIC -shared -O -ansi')
env.Append(CPPPATH = ['.', 'blender','agg-2.5/include','expat-1.95.8/lib'])
env.Append(LIBS = ['m', 'c'])
env.Append(LIBS=File('agg-2.5/libagg.a'))
env.Append(LIBS=File('expat-1.95.8/libexpat.a'))
env.LoadableModule('vectex.so', ['vectex.c',  'vectex_agg.cpp','agg_svg_parser.cpp','agg_svg_path_renderer.cpp','agg_svg_path_tokenizer.cpp'])

先ほどと違っているのは、2行目
SConscript(['agg-2.5/SConscript','expat-1.95.8/SConscript'])

と6、7行目です。
env.Append(LIBS=File('agg-2.5/libagg.a'))
env.Append(LIBS=File('expat-1.95.8/libexpat.a'))

2行目では、Agg、Expatのディレクトリに置いたSConscriptを呼んで、それぞれのライブラリファイルを作成しています。
6、7行目は、Agg、Expatで作成されるライブラリファイルの場所を変更しているため、ライブラリファイルの指定をそれに合わせています。

○AggのSConscript
Agg-2.5/SConscript
env = Environment()
env.Append(CCFLAGS = '-fPIC -c -O3')
env.Append(CPPPATH = ['#agg-2.5/include'])
Export('env')
objs = []
for subdir in ['src', 'gpc', 'src/ctrl']:
    o = SConscript('%s/SConscript' % subdir)
    objs.append(o)
env.Library('agg', objs)
Aggのソースファイルは「src」「gpc」「src/ctrl」という別のサブディレクトリの中に置かれています。
そのため、それらのサブディレクトリにも、それぞれSConscriptファイルを置いて、AggのトップディレクトリにあるSConscriptファイルから呼び出すようにしました。

Export()を使用すると、envで設定したコンパイルオプション、インクルードファイルの場所、使用するライブラリなどの情報を呼び出される側のSConscriptに渡すことができます。
vectexトップディレクトリにあるSConstructとAgg、ExpatのトップディレクトリのSConscriptのenvの間では設定内容は引き継がれていません。

「CPPPATH =」でインクルードファイルの指定をしている部分を見ると、パスの頭に「#」が付けてあります。
このように「#」を付けてパスを指定すると、その部分はSconsのトップディレクトリ(SConstructのある場所)からの相対パスとして処理されます。
これによりサブディレクトリ内でのコンパイルを行っているときでも、問題なくインクルードファイルを読み込むことができるようになります。

最後の行がhello worldのProgram()に対応する部分で、スタティックライブラリを作成するLibrary()メソッドを使用しています。
SharedLibrary()と同様に、作成されるライブラリファイルの名前は、Linuxでは自動的にlibagg.aというように頭に「lib」が追加されます。

Aggの元のMakefileではsrcディレクトリにライブラリファイルが作成されるようになっていますが、今回作成したSconscriptファイルではAggのトップディレクトリにライブラリファイルlibagg.aが作成されるようになります。


「src」「gpc」「src/ctrl」に置くSConscriptファイルは基本的には同じ内容のものですが、「gpc」にあるソースファイルは拡張子が「.cpp」ではなくて「.c」となっているため、その部分だけ違っています。

Agg-2.5/src/SConscript
Agg-2.5/src/ctrl/SConscript
Import('env')
obj = env.Object(Glob('*.cpp'))
Return('obj')
Agg-2.5/gpc/SConscript
Import('env')
obj = env.Object(Glob('*.c'))
Return('obj')
Import()で、envに設定された情報をAggトップディレクトリのSConscriptファイルから受け取っています。
また、Return()を使用することで、コンパイルして作成されるオブジェクトファイルのリストをAggトップディレクトリのSConscriptファイルに返しています。

○ExpatのSConscript
expat-1.95.8/SConscript
env = Environment()
env.Append(CCFLAGS = '-fPIC -Wall -Wmissing-prototypes -Wstrict-prototypes -fexceptions -DHAVE_EXPAT_CONFIG_H -c')
env.Append(CPPPATH = ['#expat-1.95.8','#expat-1.95.8/lib','#expat-1.95.8/xmlwf'])
Export('env')
objs = []
for subdir in ['lib', 'xmlwf']:
    o = SConscript('%s/SConscript' % subdir)
    objs.append(o)
env.Library('expat', objs)
Aggのものとほとんど同じです。
コンパイルのオプションは、元のMakefileからそのままコピーしています。

expat-1.95.8/lib/SConscript
Import('env')
obj = env.Object(['xmlparse.c','xmlrole.c','xmltok.c'])
Return('obj')
expat-1.95.8/xmlwf/SConscript
Import('env')
obj = env.Object(['xmlwf.c','xmlfile.c','codepage.c','unixfilemap.c'])
Return('obj')
Aggのサブディレクトリでは、Grob()メソッドを使用して同じ拡張子のファイルをまとめて処理していましたが、expatでは「.c」ファイルをインクルードファイルとして使うというようなことが行われているため、実際にオブジェクトファイルを出力するファイルだけを列挙するようにしました。
posted by mato at 00:45| Comment(0) | Blender | このブログの読者になる | 更新情報をチェックする

2009年05月06日

Blenderへの新しいプロシージャルテクスチャの追加

vectexをマルチスレッドレンダリングに対応させる過程で、Blenderのテクスチャプラグインのプログラミングについて学ぶことができました。今後、vectexをさらに改造していく予定ですが、それに合わせてBlenderのソースコードの中身についても、少しずつ勉強していきたいと思っています。

今後進めていく作業内容としては
・vectexをBlender本体に組み込む
・vectexで使用可能なSVGの機能を強化する
の2つを目標としていきたいと考えています。

テクスチャプラグインの形態のままでSVGの機能を強化し、その後Blenderに組み込むか、現状のままBlenderに組み込んでおいて、後からSVG機能を強化するか、どちらがいいのか悩むところです。
vectexのSVG機能を強化する方法については、
・今のままAGGを使用し続ける。
・別のSVGライブラリに乗り換える。
このどちらかの対応が考えられますが、どちらにしてもかなり苦労しそうな予感がします。

現状のままでvectexをBlenderに組み込むことは、使用しているライブラリのAGG、expatなどをうまくBlenderのビルドシステムに対応させられるかという点に若干不安を感じますが、こちらの方が難易度は低そうに思います。
ということで、まずはvectexをBlenderに組み込む作業を行い、その過程でもう少し経験値を高めてから、後でSVG機能を強化するという方向で行こうと思います。

この作業を行うにあたって、とても参考になりそうな情報(Doc/Adding A Procedural Texture)がBlender.orgにありました。

ここで言うプロシージャルテクスチャというのは、Blenderの「Texture」パネルの「Texture Type」で選択して使用する組み込みテクスチャのことです。
このチュートリアルを最後まで完成させるとジュリアフラクタルテクスチャという新しいテクスチャを使えるようになるというものですが、プログラミングの内容はテクスチャプラグイン作成のためのチュートリアルにかなり近いことを行っていると予想できます。

今回はこのチュートリアルを参考にして、以前書いた「テクスチャプラグインを作ってみる」という記事で作成した非常に簡単なテクスチャをBlenderに組み込んでみたいと思います。
pic090505_02.jpg pic090505_03.jpg

以下、プロシージャルテクスチャを追加するための作業を進めていくわけですが、今回は全部で3つのソースファイルを修正することになります。
  1. 変数の定義: DNA_texture_types.h
  2. 描画処理: texture.c
  3. UIの作成: buttons_shading.c
それぞれ、ソースコードの役割が違うため、置かれている場所も全く違っています。
今回使用しているのはBlender2.48aのソースコードです。修正した部分のソースコードはこちら(src090505.zip)になります。

○変数の定義 (/blender/source/blender/makesdna/DNA_texture_types.h)
Tex構造体の中に変数を追加します。(line:130-176)
typedef struct Tex {
    ID id;
   
    float noisesize, turbul;
    float bright, contrast, rfac, gfac, bfac;
    float filtersize;
    ...
    float test_val;
    ...
    float pad;
    ...
    struct ColorBand *coba;
    struct EnvMap *env;
   
    short fradur[4][2];
   
} Tex;
この構造体には、今回作成するテクスチャも含めて、全てのプロシージャルテクスチャが使用する変数がまとめて置かれています。
今回、新たに追加するのは境界線の位置を調整するための数値を保持するfloat型変数一つだけとなります。

    float test_val;
   
この一行だけを追加した状態で一旦ビルドを実行してみると、エラーが出てしまいます。
pic090505_04.jpg

これについては元のチュートリアルできちんと解説されています。
詳しいことはよく分からないのですが、この構造体は8byteのアライメントにしたがう必要があるので他に4byte何かのデータを追加してサイズを合わせるように、ということらしいです。

ということで、

    float pad;

という一行を追加しました。
これでエラーが解消され、コンパイルできるようになります。
pic090505_05.jpg

このファイルには、もう一ヶ所修正が必要な場所があります。
各テクスチャを区別するための番号として定数を割り当てている場所があります。(Line:197-210)
...
#define TEX_VORONOI        12
#define TEX_DISTNOISE    13
#define TEX_TEST        14

/* musgrave stype */
#define TEX_MFRACTAL        0
#define TEX_RIDGEDMF        1
...
最後が13で終わっているので、それに続けて「14」を割り当てます。

○描画処理 (/blender/source/blender/render/intern/source/texture.c)
こちらのソースコードには、色々なテクスチャの関数本体が置かれています。
適当な場所に今回作成するテクスチャのコードを追加します。
static int test_tex(Tex *tex, float *texvec, TexResult *texres)
{
    if (texvec[0] <= tex->test_val) {
            texres->tin = 0;
    } else {
        texres->tin = 1;
    }

    return TEX_INT;
}
この関数は、テクスチャプラグインでのplugin_tex_doit()関数にかなり似ています。

テクスチャプラグインのresult[]配列に相当するのがTexResultという構造体で、この構造体の中に計算結果を入れることでレンダラにデータを渡しています。
テクスチャプラグインのときと同じように、この関数の戻り値は強度、色、ノーマルのうちどの値を使用しているかを指定するために使われています。

ここで行われている計算の中身ですが、先ほどTex構造体の中に作成しておいたtest_valという変数には、後に作成するパネルの数値スライダの値が入ります。
texvec[0]には、レンダラが現在計算している位置のテクスチャ座標のX値が渡されますので、その値が数値スライダの指している位置よりも小さい(左)ならテクスチャのINT(ENSITY)値を0(黒)にし、大きい(右)なら1(白)にします。

最後に、この計算ではテクスチャの強度(INT)のみを使用していますので、関数の戻り値としてTEX_INTを返しています。

このファイルにも、修正箇所がもう一つあります。
Blenderのバージョンアップによってさらに修正箇所が増える可能性もありますので、元のチュートリアルでは「magic」などの既存のテクスチャの名前でファイル内を検索してみるようにと書かれています。

multitex()関数(line:1133-1233)
static int multitex(Tex *tex, float *texvec, float *dxt, float *dyt, int osatex, TexResult *texres)
{
    float tmpvec[3];
    int retval=0; /* return value, int:0, col:1, nor:2, everything:3 */

    texres->talpha= 0;    /* is set when image texture returns alpha (considered premul) */
   
    switch(tex->type) {
   
    case 0:
        texres->tin= 0.0f;
        return 0;
    case TEX_CLOUDS:
        retval= clouds(tex, texvec, texres);
        break;
    case TEX_WOOD:
        retval= wood(tex, texvec, texres);
        break;
    ...
    case TEX_TEST:

        retval = test_tex(tex, texvec, texres);
        break;

    ....
}

この関数の中では、DNA_texture_types.hの中で定義されているTEX_XXXという定数に対応して、テクスチャの描画を行うというような処理が書かれています。
他のテクスチャを参考にして、TEX_TESTの場合にtest_tex()を呼び出すコードを追加しています。

○UI(ユーザーインターフェイス)の作成 (/blender/source/blender/src/buttons_shading.c)
このソースファイルで、パネル上に表示されるボタンなどを設定します。
今回は数値スライダが一つだけの簡単なパネルになります。
static void texture_panel_test_tex(Tex *tex)
{
    uiBlock *block;
   
    block = uiNewBlock(&curarea->uiblocks, "texture_panel_test_tex", UI_EMBOSS, UI_HELV, curarea->win);
    if(uiNewPanel(curarea, block, "Test Tex", "Texture", 640, 0, 318, 204)==0) return;
    uiSetButLock(tex->id.lib!=0,  ERROR_LIBDATA_MESSAGE);

    uiBlockBeginAlign(block);
   
    uiDefButF(block, NUM, B_TEXPRV, "value: ", 10, 90, 150, 19, &tex->test_val, -1.0, 1.0, 10, 0, "Test Value");
   
    uiBlockEndAlign(block);
}

新規に作成したtexture_panel_test_tex()という関数の中でパネルに表示されるボタンなどを作成しています。
1から4行目まではパネルそのものを作成するための記述で、Magicなどの他のテクスチャのソースコードとほとんど共通していますが、パネルに表示される名前等は今回作成しているテストテクスチャ用に修正しています。

5から7行目までがパネルに表示されるボタンのための記述です。
テクスチャプラグインではVarStructという構造体でボタンを作成していましたが、ここではuiDefButF()という関数でボタンの詳細を指定しています。
元のチュートリアルにこの関数の詳しい説明が書かれています。(Blenderのソースコードの「doc」フォルダの中にあるinterface_API.txtから抜粋しているようです)


このファイルには、全部で3ヶ所修正が必要になります。
ここから先は、元のチュートリアルには書かれていませんが、これらの部分を修正しないと「texture」パネルから今回作成した新しいテクスチャを選択することができません。
ここでも「magic」などの既存のテクスチャの名前で検索して、修正が必要な場所を探す方法が有効です。

void texture_panels()関数(line:1576-1702)
static void texture_panel_texture(MTex *actmtex, Material *ma, World *wrld, Lamp *la, bNode *node, Brush *br, SculptData *sd)
{
        ...
        /* newnoise: all texture types as menu, not enough room for more buttons.
         * Can widen panel, but looks ugly when other panels overlap it */
       
        sprintf(textypes, "Texture Type %%t|None %%x%d|Image %%x%d|EnvMap %%x%d|Clouds %%x%d|Marble %%x%d|Stucci %%x%d|Wood %%x%d|Magic %%x%d|Blend %%x%d|Noise %%x%d|Plugin %%x%d|Musgrave %%x%d|Voronoi %%x%d|DistortedNoise %%x%d|TestTex %%x%d", 0, TEX_IMAGE, TEX_ENVMAP, TEX_CLOUDS, TEX_MARBLE, TEX_STUCCI, TEX_WOOD, TEX_MAGIC, TEX_BLEND, TEX_NOISE, TEX_PLUGIN, TEX_MUSGRAVE, TEX_VORONOI, TEX_DISTNOISE, TEX_TEST);
        uiDefBut(block, LABEL, 0, "Texture Type",        160, 150, 140, 20, 0, 0.0, 0.0, 0, 0, "");
        uiDefButS(block, MENU, B_TEXTYPE, textypes,    160, 125, 140, 25, &tex->type, 0,0,0,0, "Select texture type");
        ...
}

この関数の中にある非常に長いsprintf()関数の記述を修正します。
これで、「texture」パネルのポップアップメニューの項目に「TestTex」という新しい項目が追加されます。

texture_panels()関数(line:4508-4616)
void texture_panels()
{
            ...
            case TEX_DISTNOISE:
                texture_panel_distnoise(tex);
                break;
                /* newnoise: voronoi */
            case TEX_VORONOI:
                texture_panel_voronoi(tex);
                break;
            case TEX_TEST:
                texture_panel_test_tex(tex);
                break;

            ...
}
こちらは「texture」パネルで選択された項目に対応するパネルを表示する処理のようです。

以上で必要な修正が完了しました。
新しいファイルを追加したりしていないので、SConsなどのビルドの環境には影響していないはずです。UBUNTU 8.10 amd64の環境では、SConsでビルドすると普通にコンパイルできました。
posted by mato at 00:08| Comment(0) | Blender | このブログの読者になる | 更新情報をチェックする

2009年04月30日

テクスチャプラグインをWindowsでコンパイルする(2)

BlenderのテクスチャプラグインをWindowsでコンパイルする場合、テクスチャプラグインの公式ページともいうべきBlender Plugin Repositoyに書かれている方法では、現在のところうまくいきません。

あちこち探してみた結果、MicrosoftのVisual C++ 2008 Express Editionを使用する方法が見つかり、こちらの記事(3月15日)にその手順を書いています。

また、プラグインリポジトリにあるテクスチャプラグインの一部はこの方法ではうまくコンパイルできないものがあるため、LCCというコンパイラを使ってコンパイルする方法をこちらの記事(3月28日)に書きました。この記事を書いた時点では、Blenderからエクスポートしている関数をプラグインで使用している場合は、コンパイルに失敗していました。

その後、LCCコンパイラのマニュアルを調べてみたところ、エクスポートされている関数を利用するための方法がわかりました。今回の記事の手順で、LCCコンパイラでもプラグインリポジトリのすべてのテクスチャプラグインをコンパイルできるようになります。
具体的には、Microsoftのコンパイラを使用した場合のように、インポートライブラリ(plugin.lib)を作成しておいて、リンク時にそのファイルをコマンドの引数に追加する、という感じになります。

WindowsでGCCコンパイラを使用するための環境MinGWでもコンパイルできるようになりましたので、合わせてその手順をご紹介したいと思います。
(以下の内容は、自分で試した範囲でなんとか動作可能なプラグインをコンパイルできた、という状況で書いています。このため、不必要な手順が混じっていたり、本来必要な手順が抜けていたりするかもしれません。あくまで、参考程度のものとして見ていただければと思います。)

○LCCでのテクスチャプラグインのコンパイル
以前の記事と同じように、以下のような状況を想定してコンパイルを行います。
  1. Blenderの「plugins」フォルダを「C:\blender」というフォルダにコピーします。
  2. コンパイルの対象は、「texture」フォルダにあらかじめ入っているclouds2.cとします。
  3. tex.defファイルを「include」フォルダに置きます。
  4. 「include」フォルダ内にあるplugin.h、util.hの内容を一部修正します。
tex.defファイルの内容、plugin.h、util.hの修正内容については、3月28日の記事で書いた通りです。

・インポートライブラリ(.lib)の作成
Microsoftのコンパイラではインポートライブラリを作成する際に、plugin.defというファイルからplugin.libを作成しました。plugin.defはBlenderの「plugins」-「include」フォルダに入っています。

LCCでは、plugin.expというファイルを使用してpluginlcc.libを作成します。(本来はこれもplugin.libという名前でいいのですが、以下の手順でMicrosoftのコンパイラ用のものと同じ名前になると非常に紛らわしいので、このような名前にしてみました)

plugin.expファイルは普通のテキストファイルで、内容はplugin.defと似ています。
blender.exe
_mallocN
_callocN
_freeN
_mallocT
_callocT
_freeT
_hnoise
_hnoisep
_turbulence
_turbulence1
_allocImBuf
_dupImBuf
_freeImBuf
_converttocmap
_saveiff
_loadiffmem
_loadifffile
_loadiffname
_testiffname
_onehalf
_onethird
_halflace
_half_x
_half_y
_double_x
_double_y
_double_fast_x
_double_fast_y
_ispic
_dit2
_dit0
_scaleImBuf
_scalefastImBuf
_scalefieldImBuf
_scalefastfieldImBuf
_de_interlace
_interlace
_gamwarp

このファイルはplugin.DEFファイルを修正して手作業で作成することもできますが、Microsoftのコンパイラ用にインポートライブラリを作成してあれば、それを元に作成することもできます。

ちなみに、LCC用のpluginlcc.libとMicrosoft用のplugin.libは同じ拡張子ですが互換性がありません。同様に、Microsoftのコンパイラ用のplugin.libを作成する際にplugin.expというファイルが作成されますが、LCCで使用するものとは中身は全く別のものです。

Microsoftのコンパイラ用plugin.libからplugin.expを作成します。あらかじめコマンドを実行する場所にplugin.libをコピーしておきます。今回は「C:\blender\plugins\texture」です。
pic090430_01.jpg

コマンドプロンプトを起動し、以下のコマンドを実行します。

cd C:\blender\plugins\texture
pedump /EXP plugin.lib >plugin.exp

ここで、作成したplugin.expファイルをテキストエディタで開いてみると、一番最初の行が「blender.DLL」となっています。この部分はBlenderの実行ファイルの名前になっている必要がありますので「blender.exe」に修正します。
pic090430_02.jpg pic090430_03.jpg

plugin.expからpluginlcc.libを作成するには、以下のようにします。

buildlib plugin.exp pluginlcc.lib

以上でコンパイルのために必要なファイルがそろいました。
pic090430_04.jpg

コンパイルのために実行するコマンドは以下のようになります。

lcc -Ic:\blender\plugins\include clouds2.c
lcclnk -dll -nounderscores clouds2.obj c:\blender\plugins\include\tex.def pluginlcc.lib


一つ目のコマンドでclouds2.objというオブジェクトファイルを作成し、2つ目のコマンドはリンクを行ってDLLファイルを作成します。
pic090430_05.jpg pic090430_06.jpg
pic090430_07.jpg


○MinGWでのテクスチャプラグインのコンパイル
LCCの場合と同じく、MinGWもファイルをインストールするだけでなく、Windowsの環境変数「PATH」にMinGWの実行ファイルの入っている場所(通常は、C:\MinGW\bin)を追加しておく必要があります。

LCCの場合と同じように、以下のような状況を想定してコンパイルを行います。
  1. Blenderの「plugins」フォルダを「C:\blender」というフォルダにコピーします。
  2. コンパイルの対象は、「texture」フォルダにあらかじめ入っているclouds2.cとします。
  3. tex.defファイルを「include」フォルダに置きます。
MinGWでのコンパイルでは、tex.defファイルの内容を一部修正する必要があります。LCCで使用しているものをそのまま使うと、コンパイル時に以下のようにエラーが表示されます。
Cannot export LibMain@12: symbol not defined
collect2: ld returned 1 exit status
pic090430_16.jpg

とりあえず、問題のある部分を削除してしまえば、コンパイルが完了するようになるようです。
LibMain@12
この2行目の部分を削除します。
pic090430_08.jpg pic090430_09.jpg

・インポートライブラリの作成
MinGWでは、インポートライブラリは拡張子「.lib」ではなく、「.a」となります。さらに、ファイル名の頭が「lib」となるようにします。コマンドプロンプトを起動し、「plugins」-「include」フォルダに移動します。

cd \blender\plugins\include

以下のコマンドを実行し、libplugin.aファイルを作成します。

dlltool --input-def plugin.def --output-lib libplugin.a --dllname blender.exe

以上でコンパイルを行う準備の完了です。MinGWでのコンパイルでは、plugin.h、util.hを修正する必要はないようです。
pic090430_10.jpg

「plugins」-「texture」フォルダに移動します。

cd \blender\plugins\texture

以下のコマンドでコンパイルを行います。

gcc -I..\include -c clouds2.c
gcc -shared -o clouds2.dll ..\include\tex.def clouds2.o -L..\include -lplugin

一つ目のコマンドでオブジェクトファイルを作成し、2つ目のコマンドでリンクを行いDLLファイルを作成します。ちなみに、「-L」でリンクに使用するインポートライブラリの場所を指定し、「-l」でインポートライブラリの名前を指定します。このとき頭の「lib」と拡張子「.a」は省略します。
pic090430_11.jpg pic090430_12.jpg

ただし、このようにしてできたDLLファイルを使用する場合、初回にBlenderから読み込む際にエラーが起こりますが、Blenderを終了せずにそのまま再度プラグインをロードすると、2回目以降は普通に読み込めるようになります。
pic090430_14.jpg pic090430_15.jpg

Microsoftのコンパイラ、LCCコンパイラでは、このようなことは起こらないようですので、気になるようでしたらそれらのコンパイラを使うようにしてください。
posted by mato at 23:34| Comment(0) | Blender | このブログの読者になる | 更新情報をチェックする
×

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