2010-06-13 23:49:49

COLLADA DOMを使う-その2

テーマ:プログラミング
どうもこんにちは!タケです。

いきなりお詫びです。前回、大事なことを書き忘れていました。
それは…開発環境について。

データ作成環境にはBlender 2.49bを使用しています。
dae出力は添付のdae1.4 exporterですが、使用するに当たってkumaryuさんのパッチを2.49b向けに適用しています。

上記は2.49aに添付されているexporter向けです。2.49bの配布ファイルには別の修正が加わっていますので2.49bにkumaryuさんの修正を加えたものを用意しました。ここから取得することができます。

プログラム環境はVisual C++ 2008 Express Editionです。
違うバージョンを使用している方は、適時読み替えていただければ、と思います。

さて、前回の作業でCOLLADA DOMを使用する準備ができました。
これから、データを読み進めていきましょう。

ドキュメントをざっと見てわかったのですが、結局XMLパーサーを使用して自分でやるよりかはすこしマシ、という程度のようです。
COLLADAのデータ構成を理解しておくことが前提になりますので、COLLADAリファレンスに目を通しておく必要があります。

まあ、最低限理解しておいて、あとは必要になったら随時覚えていけばいいかと。

サンプルとして、立方体に適当にテクスチャを張ったものをdae出力してみました。
画像ではわかりませんが、1面だけ三角形ポリゴンになっています。

$ゲーム屋さんの隠れ家-サンプルをレンダリングしたもの


サンプルファイルはこちら、blender用のファイルと、テクスチャ、daeファイルをまとめてあります。
立方体のみCOLLADAに出力しています。

今回からメッシュの読み取りを進めていきたいと思います。

超簡単な概要説明。

ジオメトリの要素は <geometry> です。
メッシュをCOLLADAの要素は <mesh> です。
子要素として、三角形リストなら、 <trianngles> 、四角形なら <polygons> があります。
そしてそれぞれの要素の中に、 <input> 要素があり、データを定義しています。
<p> 要素の中では <input> 要素で参照される座標等のデータへのindexが記述されています。

たとえばこの部分を1行ずつ読んでいきましょう。

<triangles count="2" material="cube_jpg" >
<input offset="0" semantic="VERTEX" source="#Cube_007-Vertex"/ >
<input offset="1" semantic="NORMAL" source="#Cube_007-Normals"/ >
<input offset="2" semantic="TEXCOORD" source="#Cube_007-UV"/ >
<p >1 5 20 5 5 21 2 5 22 5 6 23 6 6 24 2 6 25 </p >
</triangles >


まず、triagle要素が三角形ポリゴンを現します
そしてcount="2"が三角形ポリゴンが2個あることを表し、material="cube_jpg"がマテリアルとして"cube_jpg"を参照することを表します。
input offset="0" semantic="VERTEX" source="#Cube_007-Vertex" が後続のp要素の最初の値が"Cube_007-Vertex"というidの頂点を指していることをあらわします。
次の行は"Cube_007-Normals"を参照する、法線です。
その次の行、テクスチャ座標も同様。
そして、pは上記のオフセット順のインデックスになります。
input要素のオフセットが0~2までの3つでしたので、この要素の中身も3つで一組になります。

値を順番に見ると
1 ... 頂点1
5 ... 法線1
20 ... テクスチャ座標1
となりこれで、ひとつの頂点を表します。
次以降も同じ様にまとめると、以下のようになります。

1 5 20 頂点1の頂点、法線、テクスチャ座標のインデックス
5 5 21 頂点2
2 5 22 頂点3
5 6 23 頂点4
6 6 24 頂点5
2 6 25 頂点6


そして、最初に読んだとおり三角形ポリゴンが2個ですので、頂点1,2,3で表される三角形と、頂点4,5,6で表される三角形となります。

実際の頂点はCube_007_Vertexで定義されるデータを参照します。
法線、とテクスチャ座標も同じようにinput要素のsource属性に記述される名前のデータを参照します。

では、実際にCOLLADA DOMを使ってポリゴンのインデックス情報を取り出してみましょう。

今回のソースです。コンソールプログラムです。

説明のために、整形していますので
実際のソースはここからファイルを落とせます。


//! input要素のsemantic属性
enum eINPUTSOURCE {
eINPUTSOURCE_VERTEX, //! < 頂点
eINPUTSOURCE_NORMAL, //! < 法線
eINPUTSOURCE_TEXCOORD, //! < テクスチャ座標
eINPUTSOURCE_MAX, //! < 属性数
};

static const int TRIANGLE_VERTEX_NUM = 3;//! < 三角形ポリゴンの頂点数


int _tmain(int argc, _TCHAR* argv[])
{
DAE *pDae=new DAE;

int error;
error = pDae-> load("file:///c:/test/cube.dae");


///////////////////////////////////////////////////////////
//*0


// ジオメトリの数を取得
int gcount = (int)(pDae-> getDatabase()-> getElementCount(NULL, "geometry", NULL));
// 存在するジオメトリ分ループさせる
for(int i=0; i <gcount; i++) {
// 現在のGeometryの取得
domGeometry *pGeometry;
error = pDae-> getDatabase()->
getElement(reinterpret_cast <daeElement **> (&pGeometry),
i,NULL, "geometry");

// mesh要素を取得
domMesh *pMesh = pGeometry-> getMesh();

// triangles要素を取得
domTriangles_Array tri;
tri = pMesh-> getTriangles_array();
int triarraynum = tri.getCount(); //triangles要素の数



///////////////////////////////////////////////////////////
//*1


for(int it=0;it <triarraynum;it++) {
/triangles要素を取得
domTriangles* pTri = tri.get(it);
//三角形の数
domUint trinum = pTri-> getCount();
//input要素の配列を取得
domInputLocalOffset_Array inputarray= pTri-> getInput_array();
//配列に保存されている数を取得
int inputnum = inputarray.getCount();

//属性情報を保存する配列を確保
eINPUTSOURCE *pOfsDat = new eINPUTSOURCE[inputnum];
//source情報を保存する配列を確保
daeElementRef *pRefDat = new daeElementRef[inputnum];
//属性ごとのindex情報へのポインタ配列
int (*pIndexList)[eINPUTSOURCE_MAX] =
new int[TRIANGLE_VERTEX_NUM*static_cast <int> (trinum)][eINPUTSOURCE_MAX];


///////////////////////////////////////////////////////////
//*2


// input要素を取得して覚えておく
{
for(int i=0; i <inputnum; i++) {
domInputLocalOffset *input = inputarray.get(i);
domUint ofs = input-> getOffset(); //offset属性を取得
daeString pSemantic = input-> getSemantic(); //semantic属性を取得
domURIFragmentType source = input-> getSource(); //source属性を取得
if(_strcmpi(pSemantic,"vertex")==0) { //頂点
pOfsDat[ofs]=eINPUTSOURCE_VERTEX;
} else if(_strcmpi(pSemantic,"normal")==0) { //法線
pOfsDat[ofs]=eINPUTSOURCE_NORMAL;
} else if(_strcmpi(pSemantic,"texcoord")==0) { //テクスチャ座標
pOfsDat[ofs]=eINPUTSOURCE_TEXCOORD;
}
pRefDat[ofs] = source.getElement(); //source情報
}
}


///////////////////////////////////////////////////////////
//*3


// p要素をとりだして、input要素の属性順に保存
{
// p要素を取得
domPRef p = pTri-> getP();
domListOfUInts plist=p.cast()-> getValue();
int c=0;
for(int i=0; i <trinum*TRIANGLE_VERTEX_NUM; i++) {
for(int o=0; o <inputnum; o++) {
domUint v = plist.get(c);
//属性ごとのindexを保存
pIndexList[i][pOfsDat[o]] = static_cast <int> (v);
++c;
}
}
}


///////////////////////////////////////////////////////////
//*4


//確認のためのデバッグ出力
{
for(int i=0; i <trinum; i++) {
printf("三角形 %d\n", i);
for(int v=0; v <TRIANGLE_VERTEX_NUM; v++) {
printf("頂点 %d : ", v);
for(int o=0;o <inputnum; o++) {
int d=pIndexList[i*TRIANGLE_VERTEX_NUM+v][pOfsDat[o]];
switch(pOfsDat[o]) {
case eINPUTSOURCE_VERTEX: // 頂点
printf("頂点 %d,",d);
break;
case eINPUTSOURCE_NORMAL: // 法線
printf("法線 %d,",d);
break;
case eINPUTSOURCE_TEXCOORD: // テクスチャ座標
printf("テクスチャ座標 %d,",d);
break;
}
}
printf("\n");
}
}
}


///////////////////////////////////////////////////////////
//*5


//ここ以降、indexから実際の値を取り出していく処理を書いていく


///////////////////////////////////////////////////////////
//*6


      //必要の無くなったポインタを開放
delete [] pOfsDat;
delete [] pRefDat;
delete [] pIndexList;
}

}


///////////////////////////////////////////////////////////
//*7

//解放
pDae-> cleanup();
delete pDae;



///////////////////////////////////////////////////////////
//*8


//デバッガから実行したときにキー入力待ちさせる
printf("\npress enter\n");
getchar();

return 0;
}


順にソースを見て行きましょう。
説明順に緑色のテキストで番号を振ってあります。

*0
geometry要素を探して個数を得て、その分ループさせます。
今回のサンプルではgeometry要素は1つだけですが、複数オブジェクトが含まれているdaeファイルでは、要素が複数になります。

geometryはdomGeometryになります。
getElementの2番目の引数が取得するgeometryのインデックスです。
geometry要素を取得したら、次はmeshです。
meshを取得したら、trianglesを取得します。これも複数の可能性があるので配列です。
domTriangles_Arrayです。そして配列の数だけループするようにします。

*1
triangles要素はdomTrianglesです。domTriangles_Arrayから、get関数で取得し、三角形ポリゴンの数を覚えておきます。
これに続く、input要素を取得します。これも配列です。domInputLocalOffset_Arrayがそうです。
input要素の数も取得しておきます。


*2
input要素のsemantic属性ごとに、p要素を振り分ける準備をします。
まずはsemantic属性情報を保存する配列(pOfsDat)とsource属性を保存する配列(pRefDat)を確保します。
そしてinput要素の数だけループしてdomInputLocalOffsetからoffset,semantic,source属性の値をそれぞれ取得して覚えておきます。
これで、p要素からそれぞれのデータを取得する準備ができました。

*3
domTrianglesからgetP関数で、p要素への参照(domPRef)を取得します。
domPRefをcast関数でdomPへのポインタに変換し、getValueでp要素の中身を取り出します。
p要素の中身はdomListOfUInts、domUIntのリストです。
三角形ポリゴンに必要な頂点数分ループしつつ、input要素のsematic属性ごとに値をget関数で取り出してpIndexListに格納していきます。

*4
確認のためのデバッグ出力です。
三角形ごとに、頂点、法線、テクスチャ座標のインデックスを出力します。

*5
コメントどおり、pIndexListに格納したindexから実際の値を取り出す処理を追加してゆく予定です。

*6
三角形を1つ処理した時点で、必要のなくなったポインタを開放しています。

*7
COLLADA DOMの開放処理です。

*8
Visual C++でコンソールプログラムをデバッグ実行すると、終了した時点でウインドウが閉じられてしまうため、キー入力待ちを入れています。
デバッグなしで実行したときやコマンドプロンプトから実行したときは、入力待ちが2回になってしまいますが…。


おつかれさまでした。今回の内容はここまでです。

次回以降、実際の値を取得する部分に進みます。



AD
いいね!した人  |  コメント(0)  |  リブログ(0)

タケさんの読者になろう

ブログの更新情報が受け取れて、アクセスが簡単になります

コメント

[コメントをする]

コメント投稿

AD

ブログをはじめる

たくさんの芸能人・有名人が
書いているAmebaブログを
無料で簡単にはじめることができます。

公式トップブロガーへ応募

多くの方にご紹介したいブログを
執筆する方を「公式トップブロガー」
として認定しております。

芸能人・有名人ブログを開設

Amebaブログでは、芸能人・有名人ブログを
ご希望される著名人の方/事務所様を
随時募集しております。