Fig. 9.2: Overview of file operation
Fig 9.2は、ファイルのロード・セーブの手順を示しています。 ロード時にはまずファイルをパースしてディスクリプタのツリーを作ります。 次にディスクリプタのツリーをたどりながら、オブジェクトのツ リーを作ります。 一方、セーブ時には、ディスクリプタツリーは作りません。 オブジェクトツリーをたどりながらオブジェクトからディスクリプタを作り、その場でファイルに書きだしていきます。
ファイルのノードとディスクリプタツリーのノードは1対1に対応しますが、オブジェクトのツリーではそうとは限りません。
ファイルのロードは、FIFileSprやFIFileXのようなFIFileの派生クラスのLoadImp()メソッドが行います。 ファイルパースの実装は、boost::spiritを用いて実装されています。Init()メソッドでパーサの文 法を定義しています。
パーサはFILoadContextをコンテキストとして用いながらパースを進めます。 fieldItsにロード中のデータの型情報をセットしていきます。 ノード名やメンバ名からディスクリプタやメンバの型を知る必要がありますが、ビルド時にSWIGで生 成しているディスクリプタの型情報を??Sdk::RegisterSdk()が登録したものを用いています。 新しいノードが出てくる度にFILoadContext::datasにディスクリプタを用意し、データをロードするとそこに値をセットしていきます。 他のノードへの 参照は、この時点ではノード名の文字列で記録しておきます。
ファイルをすべてロードし終わると、LoadImp()から抜けて、FIFile::Load(FILoadContext*)に戻ってきます。 他のノード(他のディスクリプタ)への参照をノード名の文字列を頼りにポインタでつないでいきます。
オブジェクト生成は、FILoadContext::CreateScene()が、 ディスクリプタツリーを根本からたどりながら順に行います。 ディスクリプタからオブジェクトを生成するのは、そのオブジェクトの先祖オブジェクトです。先祖オブジェクトが生成できない場合 はSDKの生成を試みます。 SDK以外が一番根本にあるファイルをロードするためには、予め先祖オブジェクトを用意しておく必要があります。 FIFile::Load(ObjectIfs& objs, const char* fn)のobjs引数はその役割をします。
生成されたオブジェクトは、親のAddChildObject()ですぐに子として追加されます。
ディスクリプタ間の参照はポインタになっていますが、シーングラフは繋がっていません。ディスクリプタの参照に従って、ディスクリプタから生成されたオブジェクト間に参照を追加します。リンクは、AddChildObject()関数を呼び出すことで行われま す。親子と参照の区別はつかなくなります。あるノードの下に子ノードを書いても、別のところに書いたノードへの参照を書いても同じシーグラフになるわけです。
Frameworkを使うのと簡単です。
virtual void FWMyApp::Init(int argc, char* argv[]){
UTRef<ImportIf> import = GetSdk()->GetFISdk()->CreateImport();
GetSdk()->LoadScene(fileName, import); // ファイルのロード
GetSdk()->SaveScene("save.spr", import);// ファイルのセーブテスト
FISdk単体で使う場合は次のようになります。
int main(){
// ファイルローダで生成できるように、各SDKの型情報を登録
PHSdkIf::RegisterSdk();
GRSdkIf::RegisterSdk();
FWSdkIf::RegisterSdk();
// ファイルのロード
UTRef<FISdkIf> fiSdk = FISdkIf::CreateSdk();
FIFileIf* file = fiSdk->CreateFileFromExt(".spr");
ObjectIfs objs; // ロード用オブジェクトスタック
fwSdk = FWSdkIf::CreateSdk(); // FWSDKを用意
// 子オブジェクト作成用にfwSdkをスタックに積む
objs.push_back(fwSdk);
// FWSDK以下全体をファイルからロード
if (! file->Load(objs, "test.spr") ) {
DSTR << "Error: Cannot open load file. " << std::endl;
exit(-1);
}
// ファイル中のルートノード(複数の可能性あり)がobjsに積まれる。
for(unsigned i=0; i<objs.size(); ++i){
objs[i]->Print(DSTR);
}
...
ファイルセーブは、FIFileがシーングラフをたどりながら、オブジェクトをセーブしていきます。 各オブジェクトの GetDescAddress()か、実装されていなければGetDesc()を呼び出して ディスクリプタを読み出します。 シーング ラフには、あるノードが複数のノードの子ノードになっている場合があるため、2重にセーブしないように2度目以降は参照としてセーブします。
ディスクリプタを取り出したら、ディスクリプタの型情報を利用して、ディスクリプタのメンバを順番にセーブしていきます。 実際にデータをファイルに保存するコードは、FiFileSprなどFiFileの派生クラスにあります。
Frameworkを使うのと簡単です。
virtual void FWMyApp::Save(const char* filename){
UTRef<ImportIf> import = GetSdk()->GetFISdk()->CreateImport();
GetSdk()->SaveScene(filename, import); // filenameにシーンをセーブ
FISdk単体で使う場合は次のようになります。
void save(const char* filename, ImportIf* ex, ObjectIf* rootNode){
// ファイルのセーブ
UTRef<FISdkIf> fiSdk = FISdkIf::CreateSdk();
FIFileIf* file = fiSdk->CreateFileFromExt(".spr");
ObjectIfs objs; // ロード用オブジェクトスタック
objs.push_back(rootNode);
file->SetImport(ex);
file->Save(*objs, filename);
}