// // コンピュータグラフィックス特論U // 幾何形状データ(Obj形式)の読み込み&描画のサンプルプログラム // // 基本的なヘッダファイルのインクルード #ifdef _WIN32 #include #endif #include #include // 幾何形状データの定義のインクルード #include "obj.h" // バッファ長(サイズは適当) #define BUFFER_LENGTH 1024 #define MAX_VECTOR_SIZE 4096 #define MAX_TRIANGLE_SIZE 4096 #define MAX_MTL_SIZE 32 // // Objファイルの読み込み // Obj * LoadObj( const char * filename ) { FILE * fp; char line[ BUFFER_LENGTH ]; char name[ BUFFER_LENGTH ]; int i, j; Vector vec; int v_no[4], vt_no[4], vn_no[4]; int count; Mtl * curr_mtl = NULL; // ファイルを開く fp = fopen( filename, "r" ); if ( fp == NULL ) return NULL; // Obj構造体を初期化(ひとまず固定サイズの配列を割り当てる) Obj * obj = new Obj(); obj->num_vertices = 0; obj->num_normals = 0; obj->num_tex_coords = 0; obj->vertices = new Vector[ MAX_VECTOR_SIZE ]; obj->normals = new Vector[ MAX_VECTOR_SIZE ]; obj->tex_coords = new Vector[ MAX_VECTOR_SIZE ]; obj->num_triangles = 0; obj->tri_v_no = new int[ MAX_TRIANGLE_SIZE * 3 ]; obj->tri_vn_no = new int[ MAX_TRIANGLE_SIZE * 3 ]; obj->tri_vt_no = new int[ MAX_TRIANGLE_SIZE * 3 ]; obj->tri_material = new Mtl*[ MAX_TRIANGLE_SIZE * 3 ]; obj->num_materials = 0; obj->materials = NULL; // ファイルから1行ずつ読み込み while ( fgets( line, BUFFER_LENGTH, fp ) != NULL ) { // マテリアルの読み込み if ( strncmp( line, "mtllib", 6 ) == 0 ) { // テキストを解析 sscanf( line, "mtllib %s", name ); // 指定されたファイル名のマテリアルデータを読み込み if ( strlen( name ) > 0 ) LoadMtl( name, obj ); } // マテリアルの変更 if ( strncmp( line, "usemtl", 6 ) == 0 ) { // テキストを解析 sscanf( line, "usemtl %s", name ); // 指定された名前のマテリアルデータを探索して記録 for ( i=0; inum_materials; i++ ) { if ( strcmp( name, obj->materials[ i ]->name ) == 0 ) { curr_mtl = obj->materials[ i ]; break; } } } // 頂点データの読み込み if ( line[0] == 'v' ) { // 法線ベクトル(vn) if ( line[1] == 'n' ) { // テキストを解析 sscanf( line, "vn %f %f %f", &vec.x, &vec.y, &vec.z ); // 法線ベクトル配列の末尾に格納 if ( obj->num_normals < MAX_VECTOR_SIZE ) { obj->normals[ obj->num_normals ] = vec; obj->num_normals ++; } } // テクスチャ座標(vt) else if ( line[1] == 't' ) { // テキストを解析 sscanf( line, "vt %f %f %f", &vec.x, &vec.y, &vec.z ); // テクスチャ座標配列の末尾に格納 if ( obj->num_tex_coords < MAX_VECTOR_SIZE ) { obj->tex_coords[ obj->num_tex_coords ] = vec; obj->num_tex_coords ++; } } // 頂点座標(v) else { // テキストを解析 sscanf( line, "v %f %f %f", &vec.x, &vec.y, &vec.z ); // 法線ベクトル配列の末尾に格納 if ( obj->num_vertices < MAX_VECTOR_SIZE ) { obj->vertices[ obj->num_vertices ] = vec; obj->num_vertices ++; } } } // ポリゴンデータの読み込み if ( line[0] == 'f' ) { // 未実装(各自実装) // テキストを解析(三角形・テクスチャ座標なしの場合) count = sscanf( line, "f %i//%i %i//%i %i//%i", &v_no[0], &vn_no[0], &v_no[1], &vn_no[1], &v_no[2], &vn_no[2] ); // 解析に成功したらポリゴンデータを記録 if ( count == 6 ) { i = obj->num_triangles * 3; for ( j=0; j<3; j++ ) { obj->tri_v_no[ i+j ] = v_no[ j ] - 1; // Obj形式ではインデックス番号は1から始まるので、−1して0から始まるようにする obj->tri_vn_no[ i+j ] = vn_no[ j ] - 1; obj->tri_vt_no[ i+j ] = vt_no[ j ] - 1; } obj->tri_material[ obj->num_triangles ] = curr_mtl; obj->num_triangles ++; } // 解析に失敗したら別のフォーマットを試す { // ...... // 未実装(各自実装) } // ポリゴン数が確保した配列の大きさを超えたら強制終了 if ( obj->num_triangles >= MAX_TRIANGLE_SIZE ) break; } }; // 必要な配列を確保しなおす(面倒なのでとりあえず頂点座標配列のみ、後は各自追加) Vector * new_array; new_array = new Vector[ obj->num_vertices ]; memcpy( new_array, obj->vertices, sizeof( Vector ) * obj->num_vertices ); delete[] obj->vertices; obj->vertices = new_array; // ファイルを閉じる fclose( fp ); // 読み込んだオブジェクトデータを返す return obj; } // // Mtlファイルの読み込み // void LoadMtl( const char * filename, Obj * obj ) { FILE * fp; char line[ BUFFER_LENGTH ]; char name[ BUFFER_LENGTH ]; Color color; Mtl * curr_mtl = NULL; // ファイルを開く fp = fopen( filename, "r" ); if ( fp == NULL ) return; // Mtl配列を初期化(ひとまず固定サイズの配列を割り当てる) obj->num_materials = 0; obj->materials = new Mtl*[ MAX_MTL_SIZE ]; // ファイルから1行ずつ読み込み while ( fgets( line, BUFFER_LENGTH, fp ) != NULL ) { // マテリアルデータの追加 if ( strncmp( line, "newmtl", 6 ) == 0 ) { // テキストを解析 sscanf( line, "newmtl %s", name ); if ( strlen( name ) == 0 ) continue; // マテリアルデータの作成 curr_mtl = new Mtl(); curr_mtl->name = new char[ strlen( name ) + 1 ]; strcpy( curr_mtl->name, name ); curr_mtl->kd.r = 0.8f; curr_mtl->kd.g = 0.8f; curr_mtl->kd.b = 0.8f; // マテリアルデータを配列に記録 obj->materials[ obj->num_materials ] = curr_mtl; obj->num_materials ++; } // 反射特性データの読み込み if ( line[0] == 'K' ) { // 拡散反射特性(Kd) if ( line[1] == 'd' ) { // テキストを解析 sscanf( line, "Kd %f %f %f", &color.r, &color.g, &color.b ); // 拡散反射特性(Kd)を記録 if ( curr_mtl ) curr_mtl->kd = color; } } } // ファイルを閉じる fclose( fp ); } // // オブジェクトのスケーリング(スケーリング後の中心の高さを返す) // float ScaleObj( Obj * obj, float max_size ) { if ( !obj || ( obj->num_vertices == 0 ) ) return 0.0; // サイズ計算 Vector min, max; min = max = obj->vertices[ 0 ]; int i; for ( i=0; inum_vertices; i++ ) { const Vector & p = obj->vertices[ i ]; if ( p.x < min.x ) min.x = p.x; if ( p.y < min.y ) min.y = p.y; if ( p.z < min.z ) min.z = p.z; if ( p.x > max.x ) max.x = p.x; if ( p.y > max.y ) max.y = p.y; if ( p.z > max.z ) max.z = p.z; } // スケーリング float size, scale; size = ( ( max.x - min.x ) > ( max.z - min.z ) ) ? ( max.x - min.x ) : ( max.z - min.z ); scale = max_size / size; for ( i=0; inum_vertices; i++ ) { obj->vertices[ i ].x *= scale; obj->vertices[ i ].y *= scale; obj->vertices[ i ].z *= scale; } // スケーリング後の中心の高さを返す float object_y = min.y * scale; return object_y; } // // Obj形状データの描画 // void RenderObj( Obj * obj ) { int i, j, no; Mtl * curr_mtl = NULL; glBegin( GL_TRIANGLES ); for ( i=0; inum_triangles; i++ ) { // マテリアルの切り替え if ( ( obj->num_materials > 0 ) && ( obj->tri_material[ i ] != curr_mtl ) ) { curr_mtl = obj->tri_material[ i ]; glColor3f( curr_mtl->kd.r, curr_mtl->kd.g, curr_mtl->kd.b ); } // 三角面の各頂点データの指定 for ( j=0; j<3; j++ ) { // 法線ベクトル no = obj->tri_vn_no[ i*3 + j ]; const Vector & vn = obj->normals[ no ]; glNormal3f( vn.x, vn.y, vn.z ); // 頂点座標 no = obj->tri_v_no[ i*3 + j ]; const Vector & v = obj->vertices[ no ]; glVertex3f( v.x, v.y, v.z ); } } glEnd(); }