3dsのローダー部分

今日は3dsのロードプログラムを少し掘り下げて調査してみました。
具体的にはマテリアル情報のロード部分ですね。

実験的に作ったプログラムを公開します。
3dsの各チャンクを見て、オブジェクト単位での頂点や面、ubの総数を読む事とマテリアルの情報を読む事ができます。

/**
 * Test code of loading 3ds file.
 */

//	Pre processor
#include 
#include 
#include 
using namespace std;

enum StudioMaxChunk{
  EDIT3DS           = 0x3D3D,// Start of our actual objects
  AMBIENT_LIGHT     = 0X2100,
  OBJECT            = 0x4000,
  OBJECT_MESH       = 0x4100,
  MESH_VERTICES     = 0x4110,
  MESH_FACES        = 0x4120,
  MESH_TEXTURE_COORD= 0x4140,
  MESH_MATERIAL     = 0x4130,
  MATERIAL          = 0xAFFF,
  MATERIAL_NAME     = 0xA000,
  MATERIAL_DIFFUSE  = 0xA020,
  MATERIAL_TEXMAP   = 0xA200,
  MATERIAL_TEXFILE  = 0xA300,
};

class Chunk
{
public:
  unsigned short m_ID;
  unsigned int m_length;
  unsigned int m_bytesread;

public:
  Chunk();
  void display(void);
  unsigned int readFile(FILE* fp);
  unsigned int skipFile(FILE* fp);

public:
  static unsigned int DATASIZE;
};

unsigned int Chunk::DATASIZE = 6;


Chunk::Chunk()
{
  memset(this,0,Chunk::DATASIZE);
}

void Chunk::display()
{
  cout << "Chunk ID: 0x" << setw(4) << setfill('0') 
	   << setbase(16) << this->m_ID << " ";
  cout << setbase(10) << "length: "<< this->m_length << endl;
}


unsigned int Chunk::readFile(FILE* fp)
{
  this->m_bytesread = 0;
  this->m_bytesread += fread(&(this->m_ID), 1, sizeof(unsigned short), fp);
  this->m_bytesread += fread(&(this->m_length), 1, sizeof(unsigned int), fp);
  
  return (this->m_bytesread);
}

unsigned int Chunk::skipFile(FILE* fp)
{
  fseek(fp,this->m_length-this->m_bytesread,SEEK_CUR);
  return this->m_length-Chunk::DATASIZE;
}

unsigned int getName(FILE* fp, char* name)
{
  unsigned int index = 0;
  fread(&(name[index]), 1, 1, fp);
  while( name[index++] != 0 ){
	fread(&(name[index]), 1, 1, fp);
  }

  return index;
}


void readMesh(FILE* fp, Chunk* head);

void readMeshVertices(FILE* fp, Chunk* head)
{
  unsigned short number_vertices = 0;
  head->m_bytesread += fread(&number_vertices,
							 1,
							 sizeof(unsigned short),
							 fp);
  cout << "   number of vertex = " << number_vertices << endl;
  head->skipFile(fp);
}

void readMeshFaces(FILE* fp, Chunk* head)
{
  unsigned short number_faces = 0;
  head->m_bytesread += fread(&number_faces,
							 1,
							 sizeof(unsigned short),
							 fp);
  cout << "   number of faces = " << number_faces << endl;
  
  //  head->skipFile(fp);
  fseek(fp,number_faces*8,SEEK_CUR);

  // Face chunk have child chunk.
  // I recursively call readMesh();
  head->m_bytesread += number_faces*8;
  readMesh(fp, head);
}

void readMeshMaterial(FILE* fp, Chunk* head)
{
  char buffer[256];
  head->m_bytesread += getName(fp, buffer);
  
  unsigned short number_face;
  head->m_bytesread += fread(&number_face, 1, sizeof(unsigned short), fp);
  cout << "   number of faces in [" 
	   << buffer 
	   <<"]= " 
	   << number_face << endl;
  head->skipFile(fp);
}

void readMeshTextureCoord(FILE* fp, Chunk* head)
{
  unsigned short number_texcoord = 0;
  head->m_bytesread += fread(&number_texcoord,
							 1,
							 sizeof(unsigned short),
							 fp);
  cout << "   number of texcoord = " << number_texcoord << endl;
  head->skipFile(fp);
}

void readMesh(FILE* fp, Chunk* head)
{
  while( head->m_bytesread < head->m_length ){
	Chunk tmpChunk;
	tmpChunk.readFile(fp);
	cout << "   ";
	tmpChunk.display();

	switch( tmpChunk.m_ID ){
	case MESH_VERTICES:
	  readMeshVertices(fp, &tmpChunk);
	  break;
	case MESH_FACES:
	  readMeshFaces(fp, &tmpChunk);
	  break;
	case MESH_MATERIAL:
	  readMeshMaterial(fp, &tmpChunk);
	  break;
	case MESH_TEXTURE_COORD:
	  readMeshTextureCoord(fp, &tmpChunk);
	  break;
	default:
	  tmpChunk.skipFile(fp);
	}
	head->m_bytesread += tmpChunk.m_length;
  }
}

void readObject(FILE* fp, Chunk* head)
{
  char buffer[256];
  // object must have name
  head->m_bytesread += getName(fp, buffer);
  cout << "   name=[" << buffer << "]" << endl;

  while( head->m_bytesread < head->m_length ){
	Chunk tmpChunk;
	tmpChunk.readFile(fp);
	cout << "   ";
	tmpChunk.display();

	switch( tmpChunk.m_ID ){
	case OBJECT_MESH:
	  readMesh(fp, &tmpChunk);
	  break;
	default:
	  tmpChunk.skipFile(fp);
	}
	head->m_bytesread += tmpChunk.m_length;
  }
}


void readDiffuse(FILE* fp, Chunk* head)
{
  Chunk tmpChunk;
  unsigned char r,g,b;
  tmpChunk.readFile(fp);
  fread(&r,1,sizeof(unsigned char),fp);
  fread(&g,1,sizeof(unsigned char),fp);
  fread(&b,1,sizeof(unsigned char),fp);
  cout << "   [rgb]";
  cout << setw(2) << setbase(16) 
	   << (unsigned int)r << ":" 
	   << setw(2) << setbase(16) 
	   << (unsigned int)g << ":" 
	   << setw(2) << setbase(16) 
	   << (unsigned int)b << endl;
  head->m_bytesread += (6+3);
}

void readMaterial(FILE* fp, Chunk* head)
{
  char buffer[256];
  while( head->m_bytesread < head->m_length ){
	Chunk tmpChunk;
	tmpChunk.readFile(fp);
	cout << "   ";
	tmpChunk.display();
	switch( tmpChunk.m_ID ){
	case MATERIAL_NAME:
	  tmpChunk.m_bytesread += getName(fp,buffer);
	  cout << "   name=[" << buffer << "]" << endl;
	  break;
	case MATERIAL_DIFFUSE:
	  readDiffuse(fp, &tmpChunk);
	  break;
	case MATERIAL_TEXMAP:
	  // MATERIAL can calls itself.
	  // This term is sign of recursive.
	  readMaterial(fp, &tmpChunk);
	  break;
	case MATERIAL_TEXFILE:
	  tmpChunk.m_bytesread += getName(fp,buffer);
	  cout << "   texfile=[" << buffer << "]" << endl;
	  break;
	default:
	  tmpChunk.skipFile(fp);
	}
	head->m_bytesread += tmpChunk.m_length;
  }
}


void readEdit3ds(FILE* fp, Chunk* head)
{
  unsigned int readsize = head->m_bytesread;

  while( readsize < head->m_length ){
	Chunk tmpChunk;
	readsize += tmpChunk.readFile(fp);
	cout << "  ";
	tmpChunk.display();
	switch( tmpChunk.m_ID ){
	case OBJECT:
	  readObject(fp, &tmpChunk);
	  break;
	case MATERIAL:
	  readMaterial(fp, &tmpChunk);
	  break;
	default:
	  tmpChunk.skipFile(fp);
	}
	readsize += tmpChunk.m_length;
  }
}


void read3ds(const string& filename)
  throw (string)
{
  FILE* fp = fopen(filename.c_str(),"rb");
  unsigned int current_reading_size = 0;
  Chunk fileChunk;


  if( fp == NULL ) throw string("Can not open file");

  
  
  // First, This is first chunk read,it's stored id 0x4d4d and total file size.
  current_reading_size += fileChunk.readFile(fp);
  fileChunk.display();

  while( current_reading_size < fileChunk.m_length ){
	Chunk chunk;
	current_reading_size += chunk.readFile(fp);
	cout << " ";
	chunk.display();
	switch( chunk.m_ID ){
	case EDIT3DS:
	  readEdit3ds(fp, &chunk);
	  current_reading_size += (chunk.m_length-Chunk::DATASIZE);
	  break;
	default:
	  current_reading_size += chunk.skipFile(fp);
	}
  }
  
  fclose(fp);
}

int main(int argc, char* argv[])
{
  
  if( argc > 1 ){
	string name(argv[1]);
	if(string::npos == name.rfind("3ds") && string::npos == name.rfind("3DS")){
	  return 1;
	}
	try{
	  read3ds(name);
	}catch(string message){
	  cout << message << endl;
	}
  }
  return 0;
}