void MainWindow::createActions() { newObjFileAction_ = new QAction("Load OBJ Model", this); newObjFileAction_->setShortcut(QKeySequence::New); newObjFileAction_->setIcon(QIcon(":/assets/images/obj.png")); newObjFileAction_->setStatusTip("Loads OBJ Model into the Main View"); connect(newObjFileAction_, SIGNAL(triggered()), this, SLOT(openObjFile())); exitAction_ = new QAction("Exit", this); exitAction_->setShortcut(QKeySequence::Quit); exitAction_->setStatusTip("Exit the application"); connect(exitAction_, SIGNAL(triggered()), this, SLOT(close())); }
void MeshBase::loadDataFromObj( const std::string& filename ) { FILE* file = openObjFile(filename); int vertices_index = 0; int normals_index = 0; int colors_index = 0; int texture_coordinates_index = 0; int triangles_index = 0; float* vertices = m_vertex_data; float* normals = m_normal_data; unsigned char* colors = m_color_data; float* texture_coordinates = m_texture_coordinate_data; // 0 as a stride signals compact data int v_stride = m_vertex_stride == 0 ? 3 : m_vertex_stride; int n_stride = m_normal_stride == 0 ? 3 : m_normal_stride; int c_stride = m_color_stride == 0 ? 3 : m_color_stride; int t_stride = m_texture_coordinate_stride == 0 ? 2 : m_texture_coordinate_stride; bool is_loading_curr_group = false; std::string curr_group_name = default_group_name; std::string curr_group_base_name = default_group_name; MeshGroup* curr_group_data = 0; MeshGroupMap::const_iterator default_group_iter = m_mesh_groups.find(default_group_name); if( default_group_iter == m_mesh_groups.end() ) { is_loading_curr_group = false; curr_group_data = 0; } else { is_loading_curr_group = true; curr_group_data = &m_mesh_groups[default_group_name]; } std::string curr_material_name = default_material_name; int curr_material_number = 0; // Init. the current group triangle index to 0 for each group, so the current // triangle index of each can be tracked. std::map<std::string, int> groups_triangles_index; forEachGroup( GroupCurrentIndexInitFunctor(groups_triangles_index) ); // For improved speed, so that we don't need a lookup in groups_triangles_index // for every single face--only when the group changes. int* curr_group_triangles_index = &groups_triangles_index[default_group_name]; bool uses_vertices = ( m_num_vertices > 0 && vertices != 0 ); bool uses_normals = ( m_num_normals > 0 && normals != 0 ); bool uses_colors = ( m_num_colors > 0 && colors != 0 ); bool uses_texture_coordinates = ( m_num_texture_coordinates > 0 && texture_coordinates != 0 ); int v[3], n[3], t[3]; float f[3]; char buf[2048]; while(fscanf(file, "%s", buf) != EOF) { switch(buf[0]) { case '#': /* comment */ /* eat up rest of line */ fgets(buf, sizeof(buf), file); break; case 'v': /* v, vn, vt */ switch(buf[1]) { case '\0': /* vertex */ if( !uses_colors ) { fscanf( file, "%f %f %f", &f[0], &f[1], &f[2] ); if( uses_vertices ) { for( int i = 0; i < 3; ++i ) { vertices[v_stride*vertices_index + i] = f[i]; } ++vertices_index; } } else { int c[3]; fscanf( file, "%f %f %f %d %d %d", &f[0], &f[1], &f[2], &c[0], &c[1], &c[2] ); if( uses_vertices ) { for( int i = 0; i < 3; ++i ) { vertices[v_stride*vertices_index + i] = f[i]; } ++vertices_index; } if( uses_colors ) { for( int i = 0; i < 3; ++i ) { colors[c_stride*colors_index + i] = static_cast<unsigned char>( c[i] ); ++colors_index; } } } break; case 'n': /* normal */ fscanf( file, "%f %f %f", &f[0], &f[1], &f[2] ); if( uses_normals ) { for( int i = 0; i < 3; ++i ) { normals[n_stride*normals_index + i] = f[i]; } ++normals_index; } break; case 't': /* texcoord */ fscanf( file, "%f %f", &f[0], &f[1] ); if( uses_texture_coordinates ) { for( int i = 0; i < 2; ++i ) { texture_coordinates[t_stride*texture_coordinates_index + i] = f[i]; } ++texture_coordinates_index; } break; } break; case 'u': /* "usemtl <name>" */ fgets(buf, sizeof(buf), file); sscanf(buf, "%s %s", buf, buf); curr_material_name = buf; curr_material_number = m_material_numbers_by_name.at(curr_material_name); curr_group_name = groupMaterialName( curr_group_base_name, curr_material_name ); // Set up a valid current group only if the new group hasn't been excluded from loading if( m_mesh_groups.find(curr_group_name) != m_mesh_groups.end() ) { is_loading_curr_group = true; curr_group_data = &m_mesh_groups[curr_group_name]; curr_group_triangles_index = &groups_triangles_index[curr_group_name]; curr_group_data->material_number = curr_material_number; } else { is_loading_curr_group = false; curr_group_data = 0; curr_group_triangles_index = 0; } break; case 'o': /* "o <object name>" */ /* eat up rest of line */ fgets(buf, sizeof(buf), file); break; case 'g': /* "g <group name>" */ /* eat up rest of line */ fgets(buf, sizeof(buf), file); sscanf(buf, "%s", buf); curr_group_base_name = buf; curr_group_name = groupMaterialName( curr_group_base_name, curr_material_name ); // Set up a valid current group only if the new group hasn't been excluded from loading if( m_mesh_groups.find( curr_group_name ) != m_mesh_groups.end() ) { is_loading_curr_group = true; curr_group_data = &m_mesh_groups[curr_group_name]; curr_group_triangles_index = &groups_triangles_index[curr_group_name]; curr_group_data->material_number = curr_material_number; } else { is_loading_curr_group = false; curr_group_data = 0; curr_group_triangles_index = 0; } break; case 'm': /* "mtllib <material library name>" */ fgets(buf, sizeof(buf), file); sscanf(buf, "%s %s", buf, buf); { std::string dir = directoryOfFilePath( filename ); std::stringstream ss_material_library_name; ss_material_library_name << dir << m_material_library_name; loadMaterials( ss_material_library_name.str() ); } break; case 'f': /* face */ #define NEWEST_INDEX(indices, vertex_offset) \ curr_group_data->indices[3*(*curr_group_triangles_index) + (vertex_offset)] #define PREVIOUS_INDEX(indices, vertex_offset) \ curr_group_data->indices[3*(*curr_group_triangles_index - 1) + (vertex_offset)] for (int i = 0; i < 3; ++i) { v[i] = n[i] = t[i] = 0; } fscanf(file, "%s", buf); /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */ if( strstr(buf, "//" )) { /* v//n */ sscanf(buf, "%d//%d", &v[0], &n[0]); fscanf(file, "%d//%d", &v[1], &n[1]); fscanf(file, "%d//%d", &v[2], &n[2]); // We still need to advance through the file, but don't store // what is parsed for groups we've been told not to load if( is_loading_curr_group ) { if( uses_vertices ) { for (int i = 0; i < 3; ++i) { NEWEST_INDEX(vertex_indices, i) = (v[i] >= 0) ? v[i] - 1 : (vertices_index + v[i]); } } if( uses_normals ) { for (int i = 0; i < 3; ++i) { NEWEST_INDEX(normal_indices, i) = n[i] - 1; } } if( uses_texture_coordinates ) { for (int i = 0; i < 3; ++i) { NEWEST_INDEX(texture_coordinate_indices, i) = MESH_ATTRIBUTE_NOT_PROVIDED; } } ++(*curr_group_triangles_index); ++triangles_index; } // Load face as a triangle fan when there are more than three indices while(fscanf(file, "%d//%d", &v[0], &n[0]) > 0) { if( is_loading_curr_group ) { if( uses_vertices ) { NEWEST_INDEX(vertex_indices, 0) = PREVIOUS_INDEX(vertex_indices, 0); NEWEST_INDEX(vertex_indices, 1) = PREVIOUS_INDEX(vertex_indices, 2); NEWEST_INDEX(vertex_indices, 2) = (v[0] >= 0) ? v[0] - 1: (vertices_index + v[0]); } if( uses_normals ) { NEWEST_INDEX(normal_indices, 0) = PREVIOUS_INDEX(normal_indices, 0); NEWEST_INDEX(normal_indices, 1) = PREVIOUS_INDEX(normal_indices, 2); NEWEST_INDEX(normal_indices, 2) = n[0] - 1; } if( uses_texture_coordinates ) { for (int i = 0; i < 3; ++i) { NEWEST_INDEX(texture_coordinate_indices, i) = MESH_ATTRIBUTE_NOT_PROVIDED; } } ++(*curr_group_triangles_index); ++triangles_index; } } } else if( sscanf(buf, "%d/%d/%d", &v[0], &t[0], &n[0] ) == 3) { /* v/t/n */ fscanf(file, "%d/%d/%d", &v[1], &t[1], &n[1]); fscanf(file, "%d/%d/%d", &v[2], &t[2], &n[2]); if( is_loading_curr_group ) { if( uses_vertices ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(vertex_indices, i) = (v[i] >= 0) ? v[i] - 1 : (vertices_index + v[i]); } } if( uses_normals ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(normal_indices, i) = n[i] - 1; } } if( uses_texture_coordinates ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(texture_coordinate_indices, i) = t[i] - 1; } } ++(*curr_group_triangles_index); ++triangles_index; } // Load face as a triangle fan when there are more than three indices while(fscanf(file, "%d/%d/%d", &v[0], &t[0], &n[0]) > 0) { if( is_loading_curr_group ) { if( uses_vertices ) { NEWEST_INDEX(vertex_indices, 0) = PREVIOUS_INDEX(vertex_indices, 0); NEWEST_INDEX(vertex_indices, 1) = PREVIOUS_INDEX(vertex_indices, 2); NEWEST_INDEX(vertex_indices, 2) = (v[0] >= 0) ? v[0] - 1 : (vertices_index + v[0]); } if( uses_normals ) { NEWEST_INDEX(normal_indices, 0) = PREVIOUS_INDEX(normal_indices, 0); NEWEST_INDEX(normal_indices, 1) = PREVIOUS_INDEX(normal_indices, 2); NEWEST_INDEX(normal_indices, 2) = n[0] - 1; } if( uses_texture_coordinates ) { NEWEST_INDEX(texture_coordinate_indices, 0) = PREVIOUS_INDEX(texture_coordinate_indices, 0); NEWEST_INDEX(texture_coordinate_indices, 1) = PREVIOUS_INDEX(texture_coordinate_indices, 2); NEWEST_INDEX(texture_coordinate_indices, 2) = t[0] - 1; } ++(*curr_group_triangles_index); ++triangles_index; } } } else if( sscanf(buf, "%d/%d", &v[0], &t[0] ) == 2) { /* v/t */ fscanf(file, "%d/%d", &v[1], &t[1]); fscanf(file, "%d/%d", &v[2], &t[2]); if( is_loading_curr_group ) { if( uses_vertices ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(vertex_indices, i) = (v[i] >= 0) ? v[i] - 1 : (vertices_index + v[i]); } } if( uses_normals ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(normal_indices, i) = MESH_ATTRIBUTE_NOT_PROVIDED; } } if( uses_texture_coordinates ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(texture_coordinate_indices, i) = t[i] - 1; } } ++(*curr_group_triangles_index); ++triangles_index; } // Load face as triangle fan when more than three indices while(fscanf(file, "%d/%d", &v[0], &t[0]) > 0) { if( is_loading_curr_group ) { if( uses_vertices ) { NEWEST_INDEX(vertex_indices, 0) = PREVIOUS_INDEX(vertex_indices, 0); NEWEST_INDEX(vertex_indices, 1) = PREVIOUS_INDEX(vertex_indices, 2); NEWEST_INDEX(vertex_indices, 2) = (v[0] >= 0) ? v[0] - 1 : (vertices_index + v[0]); } if( uses_normals ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(normal_indices, i) = MESH_ATTRIBUTE_NOT_PROVIDED; } } if( uses_texture_coordinates ) { NEWEST_INDEX(texture_coordinate_indices, 0) = PREVIOUS_INDEX(texture_coordinate_indices, 0); NEWEST_INDEX(texture_coordinate_indices, 1) = PREVIOUS_INDEX(texture_coordinate_indices, 2); NEWEST_INDEX(texture_coordinate_indices, 2) = t[0] - 1; } ++(*curr_group_triangles_index); ++triangles_index; } } } else { /* v */ sscanf(buf, "%d", &v[0]); fscanf(file, "%d", &v[1]); fscanf(file, "%d", &v[2]); if( is_loading_curr_group ) { if( uses_vertices ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(vertex_indices, i) = (v[i] >= 0) ? v[i] - 1 : (vertices_index + v[i]); } } if( uses_normals ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(normal_indices, i) = MESH_ATTRIBUTE_NOT_PROVIDED; } } if( uses_texture_coordinates ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(texture_coordinate_indices, i) = MESH_ATTRIBUTE_NOT_PROVIDED; } } ++(*curr_group_triangles_index); ++triangles_index; } // Load face with more than three indices as a triangle fan while(fscanf(file, "%d", &v[0]) > 0) { if( is_loading_curr_group ) { if( uses_vertices ) { NEWEST_INDEX(vertex_indices, 0) = PREVIOUS_INDEX(vertex_indices, 0); NEWEST_INDEX(vertex_indices, 1) = PREVIOUS_INDEX(vertex_indices, 2); NEWEST_INDEX(vertex_indices, 2) = (v[0] >= 0) ? v[0] - 1 : (vertices_index + v[0]); } if( uses_normals ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(normal_indices, i) = MESH_ATTRIBUTE_NOT_PROVIDED; } } if( uses_texture_coordinates ) { for( int i = 0; i < 3; ++i ) { NEWEST_INDEX(texture_coordinate_indices, i) = MESH_ATTRIBUTE_NOT_PROVIDED; } } ++(*curr_group_triangles_index); triangles_index++; } } } #undef PREVIOUS_INDEX #undef NEWEST_INDEX break; default: /* eat up rest of line */ fgets(buf, sizeof(buf), file); break; } } #if 0 /* announce the memory requirements */ printf(" Memory: %d bytes\n", vertices_index * 3*sizeof(float) + numnormals * 3*sizeof(float) * (numnormals ? 1 : 0) + numtexcoords * 3*sizeof(float) * (numtexcoords ? 1 : 0) + numtriangles * sizeof(GLMtriangle)); #endif // It happens that in a .obj, all the color indices are identical to the // vertex indices, so we simply copy them if( uses_colors ) { forEachGroup( VertexIndexToColorIndexCopyFunctor() ); } }
// TODO: It sure would be nice to clean up or refactor the old GLM-style // parsing paragraphs into helper functions, or even a class in its own right. void MeshBase::loadInfoFromObj(const std::string& filename) { FILE* file = openObjFile(filename); m_num_vertices = 0; m_num_normals = 0; m_num_colors = 0; m_num_texture_coordinates = 0; m_num_triangles = 0; // Make a default group std::string curr_group_name = default_group_name; MeshGroup* curr_group = &getOrAddGroup( curr_group_name ); std::string curr_group_base_name = curr_group_name; int material_count = 0; std::string curr_material_name = default_material_name; m_material_numbers_by_name[curr_material_name] = material_count; ++material_count; int v, n, t; char buf[2048]; while(fscanf(file, "%s", buf) != EOF) { switch(buf[0]) { case '#': /* comment */ /* eat up rest of line */ fgets(buf, sizeof(buf), file); break; case 'v': /* v, vn, vt */ switch(buf[1]) { case '\0': { /* vertex */ /* eat up rest of line */ fgets(buf, sizeof(buf), file); float vx,vy,vz; int val = -1; sscanf(buf,"%f %f %f %d",&vx, &vy, &vz, &val); if( val >= 0 ) { ++m_num_colors; } ++m_num_vertices; break; } case 'n': /* normal */ /* eat up rest of line */ fgets(buf, sizeof(buf), file); ++m_num_normals; break; case 't': /* texcoord */ /* eat up rest of line */ fgets(buf, sizeof(buf), file); ++m_num_texture_coordinates; break; default: printf("meshLoaderLoadInfoFromObj(): Unknown token \"%s\".\n", buf); /* Could error out here, but we'll just skip it for now.*/ break; } break; case 'm': /* "mtllib <name>" */ fgets(buf, sizeof(buf), file); sscanf(buf, "%s %s", buf, buf); m_material_library_name = buf; break; case 'u': /* "usemtl <name>" */ /* We need to create groups with their own materials */ fgets(buf, sizeof(buf), file); sscanf(buf, "%s %s", buf, buf); curr_material_name = buf; if( m_material_numbers_by_name.find(curr_material_name) == m_material_numbers_by_name.end() ) { m_material_numbers_by_name[curr_material_name] = material_count; ++material_count; } curr_group = &getOrAddGroup( groupMaterialName(curr_group_base_name, curr_material_name) ); break; case 'o': /* "o <object name>" */ /* eat up rest of line */ fgets(buf, sizeof(buf), file); break; case 'g': /* "g <group name>" */ fgets(buf, sizeof(buf), file); sscanf(buf, "%s", buf); curr_group_base_name = buf; curr_group = &getOrAddGroup( groupMaterialName(curr_group_base_name, curr_material_name) ); break; case 'f': /* face */ v = n = t = 0; fscanf(file, "%s", buf); /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */ if( strstr(buf, "//" )) { /* v//n */ sscanf(buf, "%d//%d", &v, &n); fscanf(file, "%d//%d", &v, &n); fscanf(file, "%d//%d", &v, &n); ++m_num_triangles; ++curr_group->num_triangles; while(fscanf(file, "%d//%d", &v, &n) > 0) { ++m_num_triangles; ++curr_group->num_triangles; } } else if( sscanf(buf, "%d/%d/%d", &v, &t, &n ) == 3) { /* v/t/n */ fscanf(file, "%d/%d/%d", &v, &t, &n); fscanf(file, "%d/%d/%d", &v, &t, &n); ++m_num_triangles; ++curr_group->num_triangles; while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) { ++m_num_triangles; ++curr_group->num_triangles; } } else if( sscanf(buf, "%d/%d", &v, &t ) == 2) { /* v/t */ fscanf(file, "%d/%d", &v, &t); fscanf(file, "%d/%d", &v, &t); ++m_num_triangles; ++curr_group->num_triangles; while(fscanf(file, "%d/%d", &v, &t) > 0) { ++m_num_triangles; ++curr_group->num_triangles; } } else { /* v */ fscanf(file, "%d", &v); fscanf(file, "%d", &v); ++m_num_triangles; ++curr_group->num_triangles; while(fscanf(file, "%d", &v) > 0) { ++m_num_triangles; ++curr_group->num_triangles; } } break; default: /* eat up rest of line */ fgets(buf, sizeof(buf), file); break; } } // Prune out groups with 0 triangles; forEachGroup( PruneEmptyGroupsFunctor(m_mesh_groups) ); }