.MQOを自力で読み込んでみる2(読み込み編)

今回は読み込み編という事で、実際に .MQO ファイルを読み込んでみます。
前回の解析編で描画に使うデータがわかりましたのでそれを自作のクラスと
構造体にガンガン詰め込んでみます。
ファイルの読み込みには C++ の fstream を使おうかな?と思ったんですが、
効率があまりよくないので GLmetaseq の旧版を参考に C言語の fscanf_s と
fgets を使う事にしました。
GLmetaseq の旧版ではディスプレイリストにガンガン登録して、それをサクッと表示
していますが、私的にはディスプレイリストは使わない方向で行きます。
何故かというと、ワイヤーフレーム表示とか将来的にはスキニングとかもやりたいからです。

#include <string>
#include <vector>

using namespace std;
//3つのベクトル
struct Vector3f{
 float x;
 float y;
 float z;
};
//4つのベクトル
struct Vector4f{
 float x;
 float y;
 float z;
 float w;
};
//4つの反射
struct Reflection4{
 float diffuse;
 float ambient;
 float emission;
 float specular;
};
//4つのカラー
struct Color4{
 float r;
 float g;
 float b;
 float a;
};
//UV座標
struct UV{
 float u;//u値
 float v;//v値
};
//面情報構造体
struct TRIANGLE{
 int MaterialID;//マテリアルNo.
 int Index[3];//インデックス
 UV uv[3];//UV情報
}tri;
struct QUADRILATERAL{
 int MaterialID;//マテリアルNo.
 int Index[4];//インデックス
 UV uv[4];//UV情報
}quad;
//オブジェクト構造体
struct OBJECT{
 string Name;//オブジェクト名
 vector<Vector3f> Vertex;//頂点データ
 vector<TRIANGLE> Triangle;//3角面データ
 vector<QUADRILATERAL> Quadrilateral;//4角面データ
}obj;
//マテリアル構造体
struct MATERIAL{
 int MaterialID;//ID
 string MaterialName;//マテリアル名
 Color4 Color;//カラー
 Reflection4 ReflectionColor;//反射
 float Power;//shiness
 string TextureName;//テクスチャ名
}mtl;
//モデルクラス
class MODEL{
protected:
 FILE* fp;//ファイルポインタ
 char buf[255];//読み込み用バッファ
 string str;
 void Vertex_Set();//頂点情報セット
 void Material_Set();//マテリアル情報セット
 void Face_Set();//面情報セット
 string Split(string* str,char str1,char str2);//文字列分離
public:
 MODEL();
 MODEL(char* FileName);
 vector<MATERIAL> Material;//マテリアル
 vector<OBJECT> Object;//オブジェクトデータ
 bool MQO_Load(char* FileName);//ロード
 void PrintParam();
};
MODEL::MODEL(){
}
MODEL::MODEL(char* FileName){
 MQO_Load(FileName);
}
//文字列分離
string MODEL::Split(string *str, char str1, char str2){
 string::size_type start = str->find(str1);
 string::size_type end = str->rfind(str2);
 return str->substr(start+1,end-start-1);
}
//パラメータをプリント
void MODEL::PrintParam(){
 printf("%s\n",Object[0].Name.c_str());
 for(int i=0;i<(signed)Object[0].Vertex.size();i++){
  printf("%.4f %.4f %.4f\n",Object[0].Vertex[i].x,Object[0].Vertex[i].y,Object[0].Vertex[i].z);
 }
 printf("%.5f\n",Object[0].Triangle[0].uv[0].u);
 
 printf("%s\n",Material[0].MaterialName.c_str());
 printf("%f %f %f %f\n",Material[0].Color.r, Material[0].Color.g, Material[0].Color.b, Material[0].Color.a);
 printf("%f\n",  Material[0].ReflectionColor.diffuse);
 printf("%f\n",  Material[0].ReflectionColor.ambient);
 printf("%f\n",  Material[0].ReflectionColor.emission);
 printf("%f\n",  Material[0].ReflectionColor.specular);
 printf("%s\n", Material[0].TextureName.c_str());
 printf("%d\n", Object.size());
 printf("読み込み成功\n");
}
//マテリアルセット
void MODEL::Material_Set(){
 Material.push_back(mtl);
 fscanf_s(fp, "%d", &Material[Material.size()-1].MaterialID);//マテリアルナンバー格納
 fscanf_s(fp, "%s", buf,255);//"{"を読み飛ばす
 fscanf_s(fp, "%s", buf,255);//マテリアル名格納
 str=buf;
 Material[Material.size()-1].MaterialName=Split(&str,'\"','\"');//""を除去
 fgets(buf,255,fp);
 char* buf2;
 if((buf2 = strstr(buf,"col(")) != NULL ){//マテリアルカラー格納
  sscanf_s(buf2,"col(%f %f %f %f)",  &Material[Material.size()-1].Color.r, &Material[Material.size()-1].Color.g,
   &Material[Material.size()-1].Color.b, &Material[Material.size()-1].Color.a);
 }
 if((buf2 = strstr(buf,"dif(")) != NULL ){//ディフューズ格納
  sscanf_s(buf2,"dif(%f)",  &Material[Material.size()-1].ReflectionColor.diffuse);
 }
 if((buf2 = strstr(buf,"amb(")) != NULL ){//アンビエント格納
  sscanf_s(buf2,"amb(%f)",  &Material[Material.size()-1].ReflectionColor.ambient);
 }
 if((buf2 = strstr(buf,"emi(")) != NULL ){//エミッション格納
  sscanf_s(buf2,"emi(%f)",  &Material[Material.size()-1].ReflectionColor.emission);
 }
 if((buf2 = strstr(buf,"spc(")) != NULL ){//スペキュラー格納
  sscanf_s(buf2,"spc(%f)",  &Material[Material.size()-1].ReflectionColor.specular);
 }
 if((buf2 = strstr(buf,"power(")) != NULL ){//shiness格納
  sscanf_s(buf2,"power(%f)",  &Material[Material.size()-1].Power);
 }
 if((buf2 = strstr(buf,"tex(")) != NULL ){//テクスチャ名格納
  sscanf_s(buf2,"tex(%[^)])",buf,255);
  str=buf;
  Material[Material.size()-1].TextureName=Split(&str,'\"','\"');//""を除去
 }
}
//頂点情報セット
void MODEL::Vertex_Set(){
 int Vertex_Max;
 Vector3f v;
 fscanf_s(fp, "%d", &Vertex_Max);//頂点数取得
 fscanf_s(fp, "%s", buf,255);//"{"を読み飛ばす
 for(int i=0;i<Vertex_Max;i++){
  fscanf_s(fp,"%f %f %f",&v.x,&v.y,&v.z);
  Object[Object.size()-1].Vertex.push_back(v);
 }
 fscanf_s(fp, "%s", buf,255);//"}"を読み飛ばす
 fscanf_s(fp, "%s", buf,255);
}
//面情報セット
void MODEL::Face_Set(){
 int Face_Max;
 int Face;
 char* buf2;
 fscanf_s(fp, "%d", &Face_Max);//面数取得
 fscanf_s(fp, "%s", buf,255);//"{"を読み飛ばす
 for(int i=0;i<Face_Max;i++){
  fscanf_s(fp, "%d", &Face);
  if(Face==3){
   Object[Object.size()-1].Triangle.push_back(tri);
   fgets(buf,255,fp);
   if((buf2 = strstr(buf,"V(")) != NULL ){
    sscanf_s(buf2,"V(%d %d %d)",  &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].Index[0],
     &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].Index[1],
     &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].Index[2]);
   }
   if((buf2 = strstr(buf,"M(")) != NULL ){
    sscanf_s(buf2,"M(%d)",  &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].MaterialID);
   }
   if((buf2 = strstr(buf,"UV(")) != NULL ){
    sscanf_s(buf2,"UV(%f %f %f %f %f %f)",  &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].uv[0].u,
     &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].uv[0].v,
     &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].uv[1].u,
     &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].uv[1].v,
     &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].uv[2].u,
     &Object[Object.size()-1].Triangle[Object[Object.size()-1].Triangle.size()-1].uv[2].v);
   }
  }
  if(Face==4){
   Object[Object.size()-1].Quadrilateral.push_back(quad);
   fgets(buf,255,fp);
   if((buf2 = strstr(buf,"V(")) != NULL ){
    sscanf_s(buf2,"V(%d %d %d %d)",  &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].Index[0],
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].Index[1],
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].Index[2],
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].Index[3]);
   }
   if((buf2 = strstr(buf,"M(")) != NULL ){
    sscanf_s(buf2,"M(%d)",  &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].MaterialID);
   }
   if((buf2 = strstr(buf,"UV(")) != NULL ){
    sscanf_s(buf2,"UV(%f %f %f %f %f %f %f %f)",  &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].uv[0].u,
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].uv[0].v,
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].uv[1].u,
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].uv[1].v,
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].uv[2].u,
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].uv[2].v,
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].uv[3].u,
     &Object[Object.size()-1].Quadrilateral[Object[Object.size()-1].Quadrilateral.size()-1].uv[3].v);
   }
  }
 }
 fscanf_s(fp, "%s", buf,255);//"}"を読み飛ばす
 fscanf_s(fp, "%s", buf,255);
}
//メタセコイアファイル読み込み
bool MODEL::MQO_Load(char* FileName){
 if(fopen_s(&fp,FileName, "r")!=0){return false;}
 while (!feof(fp)) {
  fscanf_s(fp, "%s", buf,255);
  if (!strcmp(buf,"Material")){Material_Set();}
  if (!strcmp(buf,"Object")){
   Object.push_back(obj);
   fscanf_s(fp, "%s", buf,255);
   Object[Object.size()-1].Name=buf;
   while (!feof(fp)) {
    fscanf_s(fp, "%s", buf,255);
    if (!strcmp(buf,"vertex")){Vertex_Set();}
    if (!strcmp(buf,"face")){Face_Set();}
    if (!strcmp(buf,"}"))break;
   }
  }
 }
 fclose(fp);
 return true;
}
MODEL *model;
void main()
{
 model=new MODEL("sample.mqo");
 model->PrintParam();
 getchar();
}

処理内容ですが、マテリアルチャンクとオブジェクトチャンクを発見したら
それぞれのパラメータを各種変数に詰め込んでいるだけです。
どのようなデータでも読み込めるように動的配列を使用しています。
ワイヤーフレーム表示をしたいので四角ポリゴンはそのまま読み込みます。
もし、描画速度を最適化したいのであれば、メタセコイア上であらかじめ
全ての面を三角ポリゴンにしておけばDirectXと同じ最適化になると
思います。
その場合でもプログラムの方は一切の変更は要りません。
読み込むだけでは読み込みが成功したのかわからないので取りあえず、
適当にパラメータと読み込みが成功した旨を表示しています。
まだ不備はありますが、読み込み編としてはこれで完成としておきます。
次回は、このデータを元に画面に3Dモデルを表示してみたいと思います。

 

 

 

 

 

 

 

最終更新:2010年06月13日 14:20