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; }