Mesh Oriented datABase  (version 5.4.1)
Array-based unstructured mesh datastructure
ReadOBJ.cpp
Go to the documentation of this file.
00001 /**
00002  * MOAB, a Mesh-Oriented datABase, is a software component for creating,
00003  * storing and accessing finite element mesh data.
00004  *
00005  * Copyright 2004 Sandia Corporation.  Under the terms of Contract
00006  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government
00007  * retains certain rights in this software.
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  */
00015 
00016 #include "ReadOBJ.hpp"
00017 #include <iostream>
00018 #include <sstream>
00019 #include <fstream>
00020 #include <vector>
00021 #include <cstdlib>
00022 #include <map>
00023 #include <cassert>
00024 #include <cmath>
00025 
00026 #include "moab/Core.hpp"
00027 #include "moab/Interface.hpp"
00028 #include "moab/ReadUtilIface.hpp"
00029 #include "Internals.hpp"
00030 #include "moab/Range.hpp"
00031 #include "moab/CartVect.hpp"
00032 #include "moab/FileOptions.hpp"
00033 #include "FileTokenizer.hpp"
00034 #include "MBTagConventions.hpp"
00035 #include "moab/CN.hpp"
00036 #include "moab/GeomTopoTool.hpp"
00037 
00038 namespace moab
00039 {
00040 
00041 ReaderIface* ReadOBJ::factory( Interface* iface )
00042 {
00043     return new ReadOBJ( iface );
00044 }
00045 
00046 // Subset of starting tokens currently supported
00047 const char* ReadOBJ::delimiters = " ";
00048 const char* object_start_token  = "o";
00049 const char* group_start_token   = "g";
00050 const char* vertex_start_token  = "v";
00051 const char* face_start_token    = "f";
00052 
00053 #define OBJ_AMBIGUOUS "AMBIGUOUS"
00054 #define OBJ_UNDEFINED "UNDEFINED"
00055 
00056 // Name of geometric entities
00057 const char* const geom_name[] = { "Vertex\0", "Curve\0", "Surface\0", "Volume\0" };
00058 
00059 // Geometric Categories
00060 const char geom_category[][CATEGORY_TAG_SIZE] = { "Vertex\0", "Curve\0", "Surface\0", "Volume\0", "Group\0" };
00061 
00062 // Constructor
00063 ReadOBJ::ReadOBJ( Interface* impl )
00064     : MBI( impl ), geom_tag( 0 ), id_tag( 0 ), name_tag( 0 ), category_tag( 0 ), faceting_tol_tag( 0 ),
00065       geometry_resabs_tag( 0 ), obj_name_tag( 0 )
00066 {
00067     assert( NULL != impl );
00068     MBI->query_interface( readMeshIface );
00069     myGeomTool = new GeomTopoTool( impl );
00070     assert( NULL != readMeshIface );
00071 
00072     // Get all handles
00073     int negone = -1;
00074     ErrorCode rval;
00075     rval = MBI->tag_get_handle( GEOM_DIMENSION_TAG_NAME, 1, MB_TYPE_INTEGER, geom_tag, MB_TAG_SPARSE | MB_TAG_CREAT,
00076                                 &negone );MB_CHK_ERR_RET( rval );
00077 
00078     id_tag = MBI->globalId_tag();
00079 
00080     rval = MBI->tag_get_handle( NAME_TAG_NAME, NAME_TAG_SIZE, MB_TYPE_OPAQUE, name_tag, MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_ERR_RET( rval );
00081 
00082     rval = MBI->tag_get_handle( CATEGORY_TAG_NAME, CATEGORY_TAG_SIZE, MB_TYPE_OPAQUE, category_tag,
00083                                 MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_ERR_RET( rval );
00084 
00085     rval = MBI->tag_get_handle( "OBJECT_NAME", 32, MB_TYPE_OPAQUE, obj_name_tag, MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_ERR_RET( rval );
00086 
00087     rval = MBI->tag_get_handle( "FACETING_TOL", 1, MB_TYPE_DOUBLE, faceting_tol_tag, MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_ERR_RET( rval );
00088 
00089     rval =
00090         MBI->tag_get_handle( "GEOMETRY_RESABS", 1, MB_TYPE_DOUBLE, geometry_resabs_tag, MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_ERR_RET( rval );
00091 }
00092 
00093 // Destructor
00094 ReadOBJ::~ReadOBJ()
00095 {
00096     if( readMeshIface )
00097     {
00098         MBI->release_interface( readMeshIface );
00099         readMeshIface = 0;
00100     }
00101 
00102     delete myGeomTool;
00103 }
00104 
00105 ErrorCode ReadOBJ::read_tag_values( const char* /*file_name*/,
00106                                     const char* /*tag_name*/,
00107                                     const FileOptions& /*opts*/,
00108                                     std::vector< int >& /*tag_values_out*/,
00109                                     const SubsetList* /*subset_list*/ )
00110 {
00111     return MB_NOT_IMPLEMENTED;
00112 }
00113 
00114 // Load the file as called by the Interface function
00115 ErrorCode ReadOBJ::load_file( const char* filename,
00116                               const EntityHandle*,
00117                               const FileOptions&,
00118                               const ReaderIface::SubsetList* subset_list,
00119                               const Tag* /*file_id_tag*/ )
00120 {
00121     ErrorCode rval;
00122     int ignored = 0;   // Number of lines not beginning with o, v, or f
00123     std::string line;  // The current line being read
00124     EntityHandle vert_meshset;
00125     EntityHandle curr_meshset;  // Current object meshset
00126     std::string object_name;
00127     std::vector< EntityHandle > vertex_list;
00128     int object_id = 0, group_id = 0;  // ID number for each volume/surface
00129     int num_groups;
00130 
00131     // At this time, there is no support for reading a subset of the file
00132     if( subset_list )
00133     {
00134         MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for OBJ." );
00135     }
00136 
00137     std::ifstream input_file( filename );  // Filestream for OBJ file
00138 
00139     // Check that the file can be read
00140     if( !input_file.good() )
00141     {
00142         std::cout << "Problems reading file = " << filename << std::endl;
00143         return MB_FILE_DOES_NOT_EXIST;
00144     }
00145 
00146     // If the file can be read
00147     if( input_file.is_open() )
00148     {
00149 
00150         // create meshset for global vertices
00151         rval = MBI->create_meshset( MESHSET_SET, vert_meshset );MB_CHK_SET_ERR( rval, "Failed to create global vert meshset." );
00152 
00153         while( std::getline( input_file, line ) )
00154         {
00155             // Skip blank lines in file
00156             if( line.length() == 0 ) continue;
00157 
00158             // Tokenize the line
00159             std::vector< std::string > tokens;
00160             tokenize( line, tokens, delimiters );
00161 
00162             // Each group and object line must have a name, token size is at least 2
00163             // Each vertex and face line should have token size of at least 4
00164             if( tokens.size() < 2 ) continue;
00165 
00166             switch( get_keyword( tokens ) )
00167             {
00168                 // Object line
00169                 case object_start: {
00170                     object_id++;
00171                     object_name = tokens[1];  // Get name of object
00172 
00173                     // Create new meshset for object
00174                     rval = create_new_object( object_name, object_id, curr_meshset );MB_CHK_ERR( rval );
00175                     break;
00176                 }
00177 
00178                 // Group line
00179                 case group_start: {
00180                     group_id++;
00181                     num_groups             = tokens.size() - 1;
00182                     std::string group_name = "Group";
00183                     for( int i = 0; i < num_groups; i++ )
00184                     {
00185                         group_name = group_name + '_' + tokens[i + 1];
00186                     }
00187 
00188                     // Create new meshset for group
00189                     rval = create_new_group( group_name, group_id, curr_meshset );MB_CHK_ERR( rval );
00190                     break;
00191                 }
00192 
00193                 // Vertex line
00194                 case vertex_start: {
00195                     // Read vertex and return EH
00196                     EntityHandle new_vertex_eh;
00197                     rval = create_new_vertex( tokens, new_vertex_eh );MB_CHK_ERR( rval );
00198 
00199                     // Add new vertex EH to list
00200                     vertex_list.push_back( new_vertex_eh );
00201 
00202                     // Add new vertex EH to the meshset
00203                     MBI->add_entities( vert_meshset, &new_vertex_eh, 1 );MB_CHK_SET_ERR( rval, "Failed to add vertex to global meshset." );
00204                     break;
00205                 }
00206 
00207                 // Face line
00208                 case face_start: {
00209                     // Faces in .obj file can have 2, 3, or 4 vertices. If the face has
00210                     // 3 vertices, the EH will be immediately added to the meshset.
00211                     // If 4, face is split into triangles.  Anything else is ignored.
00212                     EntityHandle new_face_eh;
00213 
00214                     if( tokens.size() == 4 )
00215                     {
00216                         rval = create_new_face( tokens, vertex_list, new_face_eh );MB_CHK_ERR( rval );
00217 
00218                         if( rval == MB_SUCCESS )
00219                         {
00220                             // Add new face EH to the meshset
00221                             MBI->add_entities( curr_meshset, &new_face_eh, 1 );
00222                         }
00223                     }
00224 
00225                     else if( tokens.size() == 5 )
00226                     {
00227                         // Split_quad fxn will create 2 new triangles from 1 quad
00228                         Range new_faces_eh;
00229                         rval = split_quad( tokens, vertex_list, new_faces_eh );MB_CHK_ERR( rval );
00230 
00231                         // Add new faces created by split quad to meshset
00232                         if( rval == MB_SUCCESS )
00233                         {
00234                             MBI->add_entities( curr_meshset, new_faces_eh );
00235                         }
00236                     }
00237 
00238                     else
00239                     {
00240                         std::cout << "Neither tri nor a quad: " << line << std::endl;
00241                     }
00242 
00243                     break;
00244                 }
00245 
00246                 case valid_unsupported: {
00247                     // First token is not recognized as a supported character
00248                     ++ignored;
00249                     break;
00250                 }
00251 
00252                 default: {
00253                     MB_SET_ERR( MB_FAILURE, "Invalid/unrecognized line" );
00254                 }
00255             }
00256         }
00257     }
00258 
00259     // If no object lines are read (those beginning w/ 'o'), file is not obj type
00260     if( object_id == 0 && group_id == 0 )
00261     {
00262         MB_SET_ERR( MB_FAILURE, "This is not an obj file. " );
00263     }
00264 
00265     std::cout << "There were " << ignored << " ignored lines in this file." << std::endl;
00266 
00267     input_file.close();
00268 
00269     return MB_SUCCESS;
00270 }
00271 
00272 /* The tokenize function will split an input line
00273  * into a vector of strings based upon the delimiter
00274  */
00275 void ReadOBJ::tokenize( const std::string& str, std::vector< std::string >& tokens, const char* delimiters2 )
00276 {
00277     tokens.clear();
00278 
00279     std::string::size_type next_token_end, next_token_start = str.find_first_not_of( delimiters2, 0 );
00280 
00281     while( std::string::npos != next_token_start )
00282     {
00283         next_token_end = str.find_first_of( delimiters2, next_token_start );
00284         if( std::string::npos == next_token_end )
00285         {
00286             tokens.push_back( str.substr( next_token_start ) );
00287             next_token_start = std::string::npos;
00288         }
00289         else
00290         {
00291             tokens.push_back( str.substr( next_token_start, next_token_end - next_token_start ) );
00292             next_token_start = str.find_first_not_of( delimiters2, next_token_end );
00293         }
00294     }
00295 }
00296 
00297 keyword_type ReadOBJ::get_keyword( std::vector< std::string > tokens )
00298 {
00299     std::map< std::string, keyword_type > keywords;
00300 
00301     // currently supported
00302     keywords["o"] = object_start;
00303     keywords["g"] = group_start;
00304     keywords["f"] = face_start;
00305     keywords["v"] = vertex_start;
00306 
00307     // not currently supported, will be ignored
00308     keywords["vn"]         = valid_unsupported;
00309     keywords["vt"]         = valid_unsupported;
00310     keywords["vp"]         = valid_unsupported;
00311     keywords["s"]          = valid_unsupported;
00312     keywords["mtllib"]     = valid_unsupported;
00313     keywords["usemtl"]     = valid_unsupported;
00314     keywords["#"]          = valid_unsupported;
00315     keywords["cstype"]     = valid_unsupported;
00316     keywords["deg"]        = valid_unsupported;
00317     keywords["bmat"]       = valid_unsupported;
00318     keywords["step"]       = valid_unsupported;
00319     keywords["p"]          = valid_unsupported;
00320     keywords["l"]          = valid_unsupported;
00321     keywords["curv"]       = valid_unsupported;
00322     keywords["curv2"]      = valid_unsupported;
00323     keywords["surf"]       = valid_unsupported;
00324     keywords["parm"]       = valid_unsupported;
00325     keywords["trim"]       = valid_unsupported;
00326     keywords["hole"]       = valid_unsupported;
00327     keywords["scrv"]       = valid_unsupported;
00328     keywords["sp"]         = valid_unsupported;
00329     keywords["end"]        = valid_unsupported;
00330     keywords["mg"]         = valid_unsupported;
00331     keywords["bevel"]      = valid_unsupported;
00332     keywords["c_interp"]   = valid_unsupported;
00333     keywords["d_interp"]   = valid_unsupported;
00334     keywords["lod"]        = valid_unsupported;
00335     keywords["shadow_obj"] = valid_unsupported;
00336     keywords["trace_obj"]  = valid_unsupported;
00337     keywords["ctech"]      = valid_unsupported;
00338     keywords["stech"]      = valid_unsupported;
00339 
00340     return keywords[match( tokens[0], keywords )];
00341 }
00342 
00343 template < typename T >
00344 std::string ReadOBJ::match( const std::string& token, std::map< std::string, T >& tokenList )
00345 {
00346     // Initialize with no match and obj_undefined as return string
00347     std::string best_match = OBJ_UNDEFINED;
00348 
00349     // Search the map
00350     for( typename std::map< std::string, T >::iterator thisToken = tokenList.begin(); thisToken != tokenList.end();
00351          ++thisToken )
00352     {
00353         // If a perfect match break the loop (assume keyword list is unambiguous)
00354         if( token == ( *thisToken ).first )
00355         {
00356             best_match = token;
00357             break;
00358         }
00359     }
00360 
00361     // Possible return values: OBJ_UNDEFINED, keyword from list
00362     return best_match;
00363 }
00364 
00365 /*
00366  * The create_new_object function starts a new meshset for each object
00367  * that will contain all faces that make up the object.
00368  */
00369 ErrorCode ReadOBJ::create_new_object( std::string object_name, int curr_object, EntityHandle& object_meshset )
00370 {
00371     ErrorCode rval;
00372 
00373     // Create meshset to store object
00374     // This is also referred to as the surface meshset
00375     rval = MBI->create_meshset( MESHSET_SET, object_meshset );MB_CHK_SET_ERR( rval, "Failed to generate object mesh set." );
00376 
00377     // Set surface meshset tags
00378     rval = MBI->tag_set_data( name_tag, &object_meshset, 1, object_name.c_str() );MB_CHK_SET_ERR( rval, "Failed to set mesh set name tag." );
00379 
00380     rval = MBI->tag_set_data( id_tag, &object_meshset, 1, &( curr_object ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." );
00381 
00382     int dim = 2;
00383     rval    = MBI->tag_set_data( geom_tag, &object_meshset, 1, &( dim ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set dim tag." );
00384 
00385     rval = MBI->tag_set_data( category_tag, &object_meshset, 1, geom_category[2] );MB_CHK_SET_ERR( rval, "Failed to set mesh set category tag." );
00386 
00387     /* Create volume entity set corresponding to surface
00388        The volume meshset will have one child--
00389        the meshset of the surface that bounds the object.
00390      */
00391     EntityHandle vol_meshset;
00392     rval = MBI->create_meshset( MESHSET_SET, vol_meshset );MB_CHK_SET_ERR( rval, "Failed to create volume mesh set." );
00393 
00394     rval = MBI->add_parent_child( vol_meshset, object_meshset );MB_CHK_SET_ERR( rval, "Failed to add object mesh set as child of volume mesh set." );
00395 
00396     /* Set volume meshset tags
00397        The volume meshset is tagged with the same name as the surface meshset
00398        for each object because of the direct relation between these entities.
00399      */
00400     rval = MBI->tag_set_data( obj_name_tag, &vol_meshset, 1, object_name.c_str() );MB_CHK_SET_ERR( rval, "Failed to set mesh set name tag." );
00401 
00402     rval = MBI->tag_set_data( id_tag, &vol_meshset, 1, &( curr_object ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." );
00403 
00404     dim  = 3;
00405     rval = MBI->tag_set_data( geom_tag, &vol_meshset, 1, &( dim ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set dim tag." );
00406 
00407     rval = MBI->tag_set_data( name_tag, &vol_meshset, 1, geom_name[3] );MB_CHK_SET_ERR( rval, "Failed to set mesh set name tag." );
00408 
00409     rval = MBI->tag_set_data( category_tag, &vol_meshset, 1, geom_category[3] );MB_CHK_SET_ERR( rval, "Failed to set mesh set category tag." );
00410 
00411     rval = myGeomTool->set_sense( object_meshset, vol_meshset, SENSE_FORWARD );MB_CHK_SET_ERR( rval, "Failed to set surface sense." );
00412 
00413     return rval;
00414 }
00415 
00416 /*
00417  * The create_new_group function starts a new meshset for each group
00418  * that will contain all faces that make up the group
00419  */
00420 ErrorCode ReadOBJ::create_new_group( std::string group_name, int curr_group, EntityHandle& group_meshset )
00421 {
00422     ErrorCode rval;
00423 
00424     // Create meshset to store group
00425     rval = MBI->create_meshset( MESHSET_SET, group_meshset );MB_CHK_SET_ERR( rval, "Failed to generate group mesh set." );
00426 
00427     // Set meshset tags
00428     rval = MBI->tag_set_data( name_tag, &group_meshset, 1, group_name.c_str() );MB_CHK_SET_ERR( rval, "Failed to set mesh set name tag." );
00429 
00430     rval = MBI->tag_set_data( id_tag, &group_meshset, 1, &( curr_group ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." );
00431 
00432     return rval;
00433 }
00434 
00435 /* The create_new_vertex function converts a vector
00436    of tokens (v x y z) to
00437    the vertex format; a structure that has the three
00438    coordinates as members.
00439  */
00440 ErrorCode ReadOBJ::create_new_vertex( std::vector< std::string > v_tokens, EntityHandle& vertex_eh )
00441 {
00442     ErrorCode rval;
00443     vertex next_vertex;
00444 
00445     for( int i = 1; i < 4; i++ )
00446         next_vertex.coord[i - 1] = atof( v_tokens[i].c_str() );
00447 
00448     rval = MBI->create_vertex( next_vertex.coord, vertex_eh );MB_CHK_SET_ERR( rval, "Unbale to create vertex." );
00449 
00450     return rval;
00451 }
00452 
00453 /* The create_new_face function converts a vector
00454    of tokens ( f v1 v2 v3) ) to the face format;
00455    a structure that has the three
00456    connectivity points as members.
00457  */
00458 ErrorCode ReadOBJ::create_new_face( std::vector< std::string > f_tokens,
00459                                     const std::vector< EntityHandle >& vertex_list,
00460                                     EntityHandle& face_eh )
00461 {
00462     face next_face;
00463     ErrorCode rval;
00464 
00465     for( int i = 1; i < 4; i++ )
00466     {
00467         int vertex_id = atoi( f_tokens[i].c_str() );
00468 
00469         // Some faces contain format 'vertex/texture'
00470         // Remove the '/texture' and add the vertex to the list
00471         std::size_t slash = f_tokens[i].find( '/' );
00472         if( slash != std::string::npos )
00473         {
00474             std::string face = f_tokens[i].substr( 0, slash );
00475             vertex_id        = atoi( face.c_str() );
00476         }
00477 
00478         next_face.conn[i - 1] = vertex_list[vertex_id - 1];
00479     }
00480 
00481     rval = MBI->create_element( MBTRI, next_face.conn, 3, face_eh );MB_CHK_SET_ERR( rval, "Unable to create new face." );
00482 
00483     return rval;
00484 }
00485 
00486 // The split_quad function divides a quad face into 4 tri faces.
00487 ErrorCode ReadOBJ::split_quad( std::vector< std::string > f_tokens,
00488                                std::vector< EntityHandle >& vertex_list,
00489                                Range& face_eh )
00490 {
00491     ErrorCode rval;
00492     std::vector< EntityHandle > quad_vert_eh;
00493 
00494     // Loop over quad connectivity getting vertex EHs
00495     for( int i = 1; i < 5; i++ )
00496     {
00497         int vertex_id     = atoi( f_tokens[i].c_str() );
00498         std::size_t slash = f_tokens[i].find( '/' );
00499         if( slash != std::string::npos )
00500         {
00501             std::string face = f_tokens[i].substr( 0, slash );
00502             vertex_id        = atoi( face.c_str() );
00503         }
00504 
00505         quad_vert_eh.push_back( vertex_list[vertex_id - 1] );
00506     }
00507 
00508     // Create new tri faces
00509     rval = create_tri_faces( quad_vert_eh, face_eh );MB_CHK_SET_ERR( rval, "Failed to create triangles when splitting quad." );
00510 
00511     return rval;
00512 }
00513 
00514 ErrorCode ReadOBJ::create_tri_faces( std::vector< EntityHandle > quad_vert_eh,
00515                                      //                    EntityHandle center_vertex_eh,
00516                                      Range& face_eh )
00517 {
00518     ErrorCode rval;
00519     EntityHandle connectivity[3];
00520     EntityHandle new_face;
00521 
00522     connectivity[0] = quad_vert_eh[0];
00523     connectivity[1] = quad_vert_eh[1];
00524     connectivity[2] = quad_vert_eh[2];
00525     rval            = MBI->create_element( MBTRI, connectivity, 3, new_face );
00526     face_eh.insert( new_face );
00527 
00528     connectivity[0] = quad_vert_eh[2];
00529     connectivity[1] = quad_vert_eh[3];
00530     connectivity[2] = quad_vert_eh[0];
00531     rval            = MBI->create_element( MBTRI, connectivity, 3, new_face );
00532     face_eh.insert( new_face );
00533 
00534     return rval;
00535 }
00536 
00537 }  // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines