読者です 読者をやめる 読者になる 読者になる

Crosswalk sdk サンプル - SLTest

今回はcrosswalkのSLTestというサンプルのソースを解説する。
SLTestはdotXSIを前回サンプルのクラスとは違うクラスで読み込み、
ファイルの全データをambientなどの意味が分かる変数をもつインスタンスとして保持する。
今回はその中から3Dモデルのモデリングデータに関するデータを処理している部分のみ抜粋して見ていく

main関数

main関数は4229行目にある(crosswalk4.1のサンプル)。
最初にゲーム開発環境用の使い方があるが、こちらのほうがわかりやすいのでそこを見ていく。

CSLScene	Scene;
{
	//CSLScene	Scene;
			
	Scene.Open( argv[1] );
	Scene.Read();
	ShowGlobalSceneInfo(Scene);
	ShowMaterialLibrary(Scene);
	// Output the ambience of the scene.
	CSIBCColorf l_Ambience = Scene.Ambience()->GetColor();
	_SI_PRINTF("Ambience has RGB color: %f ; %f ; %f\n", l_Ambience.m_fR, l_Ambience.m_fG, l_Ambience.m_fB)

	// List all materials
	for(int i = 0;i < Scene.GetMaterialLibrary()->GetMaterialCount();i ++)
	{
		_SI_PRINTF("Material #%d is named %s\n", i, Scene.GetMaterialLibrary()->GetMaterialList()[i]->Name().GetText() );
	}

	DescribeModelRecursively(Scene.Root() );

	ShowImageLibrary(&Scene);
	Scene.Close();	
}

前回はXSIParserというクラスを使ったが、今回はCSLSceneというクラスを使う。
このサンプルで使用するクラスは全てCSL***というクラス名になっている。
XSIParserとの違いは、それぞれのテンプレート用のクラスを使ったインスタンスを子に持ち、
それぞれの値に変数でアクセスできる点である。
たとえば光源データがあったとしたら、
XSIParserではXMLのように共通テンプレートのタグ名で"ambient"を探して、そこの数値を使用することになるが、
CSL系のクラスではambientという変数で値を使用することができる。


CSLSceneオブジェクトを作ったら、メンバ関数のOpen()でファイルを開きRead()で中身を読み込む。
XSIParserでは読み込んだ時点でファイルを閉じる関数を呼んでいたが、CSLSceneではすべて終わってからCloseする。
これでCSLSceneオブジェクトが全てのデータをインスタンスとして保持した状態になる。

CSL系クラスの構造

CSLSceneはdotXSIファイルの全データをインスタンスとして持つ。
この中には、シーンに関するシーン概要情報、ファイルバージョンなどの情報、
マテリアル、テクスチャ、フォグ、3Dモデルのポリゴンデータなどが含まれている。
今例に挙げたデータはそれぞれ
CSLMaterialLibrary,
CSLImageLibrary,
CSLFog,
CSLModel(実際はポリゴンデータは違うところに入ってるけど、その辺は後述)
というクラスのデータとして保持している。
この中で今回ほしい3Dモデルのデータを持っているのはCSLModelの中なので、それ以外は省略する。

CSLModel解説

CSLModelはモデリングデータの各部位(ボーン)ごとにわかれているようなデータで、
人間のデータを例にすると足元をルートにして、足、胴体、肩、腕などの部位ごとのデータをツリー構造で持っていることが多い。
このデータの中にそれぞれのマテリアル、モーション、テクスチャ、ローカル変換行列、ポリゴンデータを持っている。


CSLModel以下のクラス構造は以下のようになっている。

  • CSLModel
    • CSLTransform(変換行列)
    • CSLGlobalMaterial(マテリアル)
    • CSLCamera(カメラ)
    • CSLLightInfo(光源関連)
    • CSLXSIMesh(3Dデータ構成データ。今回の目的のデータ)
      • CSLXSIShape(頂点座標や法線情報、テクスチャ座標情報を持つ)
      • CSLXSITriangleList(三角形ポリゴンに分割した3Dオブジェクト。Shapeの座標リストの何番を使うか、というインデックスデータ)
      • CSLXSIPolygonList(上記の多角形ポリゴン版。ポリゴンインデックスの最初に何角形かのデータが付加されている)
      • CSLXSITriangleStripList(三角形ポリゴンだがちょっと違う方式。詳しくは"トライアングルストリップ"で検索)
    • CSLModel(前述の体の各部位のデータを子に持つ)

CSLXSIMesh

CSLModelの中でモデリングデータだけほしいので、それにあたるCSLXSIMeshクラスを見てみる。
モデリングデータに関してはCSLMeshというクラスもあるが、dotXSIのバージョン5や6以降であればCSLXSIMeshになっているはず。
CSLXSIMeshの構造は前述のクラス構造で一緒に説明しているが、おおまかにその4つのデータを持つ。

CSLXSIShapeはポリゴンを構成する頂点の座標、法線、テクスチャ座標などの実際の数値を小数の配列で保持していて、
それ以外のCSLXSITriangleList,CSLXSIPolygonList,CSLXSITriangleStripListはその頂点データの何番目を使用してポリゴンを書くかという
データを整数で保持している。

頂点データ処理部分

クラス構造の説明からソースの説明に戻り、DescribeXSIMeshという関数を説明する。
処理は大まかにCSLXSIMeshのシェイプとインデックスを読み込む処理に分かれていて、
1番最初にDescribeShapeという関数でシェイプデータを読んでいる。

void DescribeXSIShape(CSLXSIShape* in_pXSIShape)
{
	SI_Int i;
	CSLXSISubComponentAttributeList* l_pAttributeList;

	_SI_PRINTF("Shape NbOfVertices %d\n", in_pXSIShape->GetVertexPositionList()->GetCount());

	_SI_PRINTF("Shape NbOfNormalArray %d\n", in_pXSIShape->GetNormalListCount() );

	l_pAttributeList = in_pXSIShape->GetFirstNormalList();
	i = 0;

	while (l_pAttributeList)
	{
		_SI_PRINTF("Shape NormalArray #%d NbOfNormals %d\n", i, l_pAttributeList->GetCount() );
		i++;

		l_pAttributeList = in_pXSIShape->GetNextNormalList();
	}

シェイプの処理を順番に見ていくと、
頂点座標の座標数の表示、
法線データリスト数(複数個ある場合もある)の表示、
全法線データリストのそれぞれのデータ数表示

この後、頂点カラーリスト、テクスチャ座標リストに関して法線リストと同じ処理をしている。
これでシェイプ処理部分完了。

次はインデックス処理部分。
下記は多角形ポリゴンインデックスリストの表示部分。

	_SI_PRINTF("XSI Mesh NbOfXSIPolyLists %d\n", in_pXSIMesh->GetXSIPolygonListCount() );

	int p;

	for (p=0;p<in_pXSIMesh->GetXSIPolygonListCount();p++)
	{
		CSLXSIPolygonList*	l_pXSIPolyList = in_pXSIMesh->XSIPolygonLists ()[p];

		// Output the material of the XSI polygon list.
		if(l_pXSIPolyList->GetMaterial() != NULL)
		{
			_SI_PRINTF("\tXSIPolyList Material %s\n", l_pXSIPolyList->GetMaterial()->GetName() );
		}

		SI_Int l_nXSIPolyCount = l_pXSIPolyList->GetPolygonCount();
		_SI_PRINTF("\tXSIPolyList NbOfXSIPolys %d\n", l_nXSIPolyCount );

		SI_Int l_nPolygonNodeCount = l_pXSIPolyList->GetCount();
		_SI_PRINTF("\tXSIPolyList NbOfPolygonNode %d\n", l_nPolygonNodeCount );

		_SI_PRINTF ( "\tXSIPolygonNodeCountList:" );
		for (int np=0;np<l_nXSIPolyCount;np++)
		{
			_SI_PRINTF ( "%d,",l_pXSIPolyList->GetPolygonNodeCountArray()->ArrayPtr()[np] );

		}
		_SI_PRINTF ( "\n" );

		// output polynode attribute
		DescribeXSISubComponentList(l_pXSIPolyList);
	}

ひとつのCSLXSIMeshの中には複数のポリゴンインデックスリストがあるので、まずそのリスト数の表示。
その後、各ポリゴンリストに関して、
マテリアルの表示、
リストに含まれる多角形ポリゴン数の表示、
リストに含まれる多角形ポリゴンの合計頂点数、
多角形ポリゴンの各角数の表示を行う。
最後にDescribeXSISubComponentListが呼ばれているが、
この中で座標インデックスを含む、法線、カラー、テクスチャ座標、その他の頂点に付加するデータ用インデックスリストを表示している。


この後は三角形インデックスリストに関して、同様の処理を行っている。

まとめ

dotXSIファイルにはたくさんのデータが入っていて、そのデータをdirectXやOpenGLで再現するには、
非常に多くのデータを使用する必要があるが、
たとえばテクスチャはプログラム側で決めうち、マテリアルも適当に設定などルールを決めて、
家などの動かないオブジェクトを表示する分には今回説明したCSLXSIMeshのデータを見れば事足りる。
ちょっと3Dデータをプログラムで使いたい場合にはこのCSLSceneを使ってコンバートするといいでしょう。