MOAB: Mesh Oriented datABase  (version 5.3.1)
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*/, const char* /*tag_name*/, const FileOptions& /*opts*/,
00106                                     std::vector< int >& /*tag_values_out*/, const SubsetList* /*subset_list*/ )
00107 {
00108     return MB_NOT_IMPLEMENTED;
00109 }
00110 
00111 // Load the file as called by the Interface function
00112 ErrorCode ReadOBJ::load_file( const char* filename, const EntityHandle*, const FileOptions&,
00113                               const ReaderIface::SubsetList* subset_list, const Tag* /*file_id_tag*/ )
00114 {
00115     ErrorCode rval;
00116     int ignored = 0;   // Number of lines not beginning with o, v, or f
00117     std::string line;  // The current line being read
00118     EntityHandle vert_meshset;
00119     EntityHandle curr_meshset;  // Current object meshset
00120     std::string object_name;
00121     std::vector< EntityHandle > vertex_list;
00122     int object_id = 0, group_id = 0;  // ID number for each volume/surface
00123     int num_groups;
00124 
00125     // At this time, there is no support for reading a subset of the file
00126     if( subset_list ) { MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for OBJ." ); }
00127 
00128     std::ifstream input_file( filename );  // Filestream for OBJ file
00129 
00130     // Check that the file can be read
00131     if( !input_file.good() )
00132     {
00133         std::cout << "Problems reading file = " << filename << std::endl;
00134         return MB_FILE_DOES_NOT_EXIST;
00135     }
00136 
00137     // If the file can be read
00138     if( input_file.is_open() )
00139     {
00140 
00141         // create meshset for global vertices
00142         rval = MBI->create_meshset( MESHSET_SET, vert_meshset );MB_CHK_SET_ERR( rval, "Failed to create global vert meshset." );
00143 
00144         while( std::getline( input_file, line ) )
00145         {
00146             // Skip blank lines in file
00147             if( line.length() == 0 ) continue;
00148 
00149             // Tokenize the line
00150             std::vector< std::string > tokens;
00151             tokenize( line, tokens, delimiters );
00152 
00153             // Each group and object line must have a name, token size is at least 2
00154             // Each vertex and face line should have token size of at least 4
00155             if( tokens.size() < 2 ) continue;
00156 
00157             switch( get_keyword( tokens ) )
00158             {
00159                 // Object line
00160                 case object_start: {
00161                     object_id++;
00162                     object_name = tokens[1];  // Get name of object
00163 
00164                     // Create new meshset for object
00165                     rval = create_new_object( object_name, object_id, curr_meshset );MB_CHK_ERR( rval );
00166                     break;
00167                 }
00168 
00169                 // Group line
00170                 case group_start: {
00171                     group_id++;
00172                     num_groups             = tokens.size() - 1;
00173                     std::string group_name = "Group";
00174                     for( int i = 0; i < num_groups; i++ )
00175                     {
00176                         group_name = group_name + '_' + tokens[i + 1];
00177                     }
00178 
00179                     // Create new meshset for group
00180                     rval = create_new_group( group_name, group_id, curr_meshset );MB_CHK_ERR( rval );
00181                     break;
00182                 }
00183 
00184                 // Vertex line
00185                 case vertex_start: {
00186                     // Read vertex and return EH
00187                     EntityHandle new_vertex_eh;
00188                     rval = create_new_vertex( tokens, new_vertex_eh );MB_CHK_ERR( rval );
00189 
00190                     // Add new vertex EH to list
00191                     vertex_list.push_back( new_vertex_eh );
00192 
00193                     // Add new vertex EH to the meshset
00194                     MBI->add_entities( vert_meshset, &new_vertex_eh, 1 );MB_CHK_SET_ERR( rval, "Failed to add vertex to global meshset." );
00195                     break;
00196                 }
00197 
00198                 // Face line
00199                 case face_start: {
00200                     // Faces in .obj file can have 2, 3, or 4 vertices. If the face has
00201                     // 3 vertices, the EH will be immediately added to the meshset.
00202                     // If 4, face is split into triangles.  Anything else is ignored.
00203                     EntityHandle new_face_eh;
00204 
00205                     if( tokens.size() == 4 )
00206                     {
00207                         rval = create_new_face( tokens, vertex_list, new_face_eh );MB_CHK_ERR( rval );
00208 
00209                         if( rval == MB_SUCCESS )
00210                         {
00211                             // Add new face EH to the meshset
00212                             MBI->add_entities( curr_meshset, &new_face_eh, 1 );
00213                         }
00214                     }
00215 
00216                     else if( tokens.size() == 5 )
00217                     {
00218                         // Split_quad fxn will create 2 new triangles from 1 quad
00219                         Range new_faces_eh;
00220                         rval = split_quad( tokens, vertex_list, new_faces_eh );MB_CHK_ERR( rval );
00221 
00222                         // Add new faces created by split quad to meshset
00223                         if( rval == MB_SUCCESS ) { MBI->add_entities( curr_meshset, new_faces_eh ); }
00224                     }
00225 
00226                     else
00227                     {
00228                         std::cout << "Neither tri nor a quad: " << line << std::endl;
00229                     }
00230 
00231                     break;
00232                 }
00233 
00234                 case valid_unsupported: {
00235                     // First token is not recognized as a supported character
00236                     ++ignored;
00237                     break;
00238                 }
00239 
00240                 default: {
00241                     MB_SET_ERR( MB_FAILURE, "Invalid/unrecognized line" );
00242                 }
00243             }
00244         }
00245     }
00246 
00247     // If no object lines are read (those beginning w/ 'o'), file is not obj type
00248     if( object_id == 0 && group_id == 0 ) { MB_SET_ERR( MB_FAILURE, "This is not an obj file. " ); }
00249 
00250     std::cout << "There were " << ignored << " ignored lines in this file." << std::endl;
00251 
00252     input_file.close();
00253 
00254     return MB_SUCCESS;
00255 }
00256 
00257 /* The tokenize function will split an input line
00258  * into a vector of strings based upon the delimiter
00259  */
00260 void ReadOBJ::tokenize( const std::string& str, std::vector< std::string >& tokens, const char* delimiters2 )
00261 {
00262     tokens.clear();
00263 
00264     std::string::size_type next_token_end, next_token_start = str.find_first_not_of( delimiters2, 0 );
00265 
00266     while( std::string::npos != next_token_start )
00267     {
00268         next_token_end = str.find_first_of( delimiters2, next_token_start );
00269         if( std::string::npos == next_token_end )
00270         {
00271             tokens.push_back( str.substr( next_token_start ) );
00272             next_token_start = std::string::npos;
00273         }
00274         else
00275         {
00276             tokens.push_back( str.substr( next_token_start, next_token_end - next_token_start ) );
00277             next_token_start = str.find_first_not_of( delimiters2, next_token_end );
00278         }
00279     }
00280 }
00281 
00282 keyword_type ReadOBJ::get_keyword( std::vector< std::string > tokens )
00283 {
00284     std::map< std::string, keyword_type > keywords;
00285 
00286     // currently supported
00287     keywords["o"] = object_start;
00288     keywords["g"] = group_start;
00289     keywords["f"] = face_start;
00290     keywords["v"] = vertex_start;
00291 
00292     // not currently supported, will be ignored
00293     keywords["vn"]         = valid_unsupported;
00294     keywords["vt"]         = valid_unsupported;
00295     keywords["vp"]         = valid_unsupported;
00296     keywords["s"]          = valid_unsupported;
00297     keywords["mtllib"]     = valid_unsupported;
00298     keywords["usemtl"]     = valid_unsupported;
00299     keywords["#"]          = valid_unsupported;
00300     keywords["cstype"]     = valid_unsupported;
00301     keywords["deg"]        = valid_unsupported;
00302     keywords["bmat"]       = valid_unsupported;
00303     keywords["step"]       = valid_unsupported;
00304     keywords["p"]          = valid_unsupported;
00305     keywords["l"]          = valid_unsupported;
00306     keywords["curv"]       = valid_unsupported;
00307     keywords["curv2"]      = valid_unsupported;
00308     keywords["surf"]       = valid_unsupported;
00309     keywords["parm"]       = valid_unsupported;
00310     keywords["trim"]       = valid_unsupported;
00311     keywords["hole"]       = valid_unsupported;
00312     keywords["scrv"]       = valid_unsupported;
00313     keywords["sp"]         = valid_unsupported;
00314     keywords["end"]        = valid_unsupported;
00315     keywords["mg"]         = valid_unsupported;
00316     keywords["bevel"]      = valid_unsupported;
00317     keywords["c_interp"]   = valid_unsupported;
00318     keywords["d_interp"]   = valid_unsupported;
00319     keywords["lod"]        = valid_unsupported;
00320     keywords["shadow_obj"] = valid_unsupported;
00321     keywords["trace_obj"]  = valid_unsupported;
00322     keywords["ctech"]      = valid_unsupported;
00323     keywords["stech"]      = valid_unsupported;
00324 
00325     return keywords[match( tokens[0], keywords )];
00326 }
00327 
00328 template < typename T >
00329 std::string ReadOBJ::match( const std::string& token, std::map< std::string, T >& tokenList )
00330 {
00331     // Initialize with no match and obj_undefined as return string
00332     std::string best_match = OBJ_UNDEFINED;
00333 
00334     // Search the map
00335     for( typename std::map< std::string, T >::iterator thisToken = tokenList.begin(); thisToken != tokenList.end();
00336          ++thisToken )
00337     {
00338         // If a perfect match break the loop (assume keyword list is unambiguous)
00339         if( token == ( *thisToken ).first )
00340         {
00341             best_match = token;
00342             break;
00343         }
00344     }
00345 
00346     // Possible return values: OBJ_UNDEFINED, keyword from list
00347     return best_match;
00348 }
00349 
00350 /*
00351  * The create_new_object function starts a new meshset for each object
00352  * that will contain all faces that make up the object.
00353  */
00354 ErrorCode ReadOBJ::create_new_object( std::string object_name, int curr_object, EntityHandle& object_meshset )
00355 {
00356     ErrorCode rval;
00357 
00358     // Create meshset to store object
00359     // This is also referred to as the surface meshset
00360     rval = MBI->create_meshset( MESHSET_SET, object_meshset );MB_CHK_SET_ERR( rval, "Failed to generate object mesh set." );
00361 
00362     // Set surface meshset tags
00363     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." );
00364 
00365     rval = MBI->tag_set_data( id_tag, &object_meshset, 1, &( curr_object ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." );
00366 
00367     int dim = 2;
00368     rval    = MBI->tag_set_data( geom_tag, &object_meshset, 1, &( dim ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set dim tag." );
00369 
00370     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." );
00371 
00372     /* Create volume entity set corresponding to surface
00373        The volume meshset will have one child--
00374        the meshset of the surface that bounds the object.
00375      */
00376     EntityHandle vol_meshset;
00377     rval = MBI->create_meshset( MESHSET_SET, vol_meshset );MB_CHK_SET_ERR( rval, "Failed to create volume mesh set." );
00378 
00379     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." );
00380 
00381     /* Set volume meshset tags
00382        The volume meshset is tagged with the same name as the surface meshset
00383        for each object because of the direct relation between these entities.
00384      */
00385     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." );
00386 
00387     rval = MBI->tag_set_data( id_tag, &vol_meshset, 1, &( curr_object ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." );
00388 
00389     dim  = 3;
00390     rval = MBI->tag_set_data( geom_tag, &vol_meshset, 1, &( dim ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set dim tag." );
00391 
00392     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." );
00393 
00394     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." );
00395 
00396     rval = myGeomTool->set_sense( object_meshset, vol_meshset, SENSE_FORWARD );MB_CHK_SET_ERR( rval, "Failed to set surface sense." );
00397 
00398     return rval;
00399 }
00400 
00401 /*
00402  * The create_new_group function starts a new meshset for each group
00403  * that will contain all faces that make up the group
00404  */
00405 ErrorCode ReadOBJ::create_new_group( std::string group_name, int curr_group, EntityHandle& group_meshset )
00406 {
00407     ErrorCode rval;
00408 
00409     // Create meshset to store group
00410     rval = MBI->create_meshset( MESHSET_SET, group_meshset );MB_CHK_SET_ERR( rval, "Failed to generate group mesh set." );
00411 
00412     // Set meshset tags
00413     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." );
00414 
00415     rval = MBI->tag_set_data( id_tag, &group_meshset, 1, &( curr_group ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." );
00416 
00417     return rval;
00418 }
00419 
00420 /* The create_new_vertex function converts a vector
00421    of tokens (v x y z) to
00422    the vertex format; a structure that has the three
00423    coordinates as members.
00424  */
00425 ErrorCode ReadOBJ::create_new_vertex( std::vector< std::string > v_tokens, EntityHandle& vertex_eh )
00426 {
00427     ErrorCode rval;
00428     vertex next_vertex;
00429 
00430     for( int i = 1; i < 4; i++ )
00431         next_vertex.coord[i - 1] = atof( v_tokens[i].c_str() );
00432 
00433     rval = MBI->create_vertex( next_vertex.coord, vertex_eh );MB_CHK_SET_ERR( rval, "Unbale to create vertex." );
00434 
00435     return rval;
00436 }
00437 
00438 /* The create_new_face function converts a vector
00439    of tokens ( f v1 v2 v3) ) to the face format;
00440    a structure that has the three
00441    connectivity points as members.
00442  */
00443 ErrorCode ReadOBJ::create_new_face( std::vector< std::string > f_tokens, const std::vector< EntityHandle >& vertex_list,
00444                                     EntityHandle& face_eh )
00445 {
00446     face next_face;
00447     ErrorCode rval;
00448 
00449     for( int i = 1; i < 4; i++ )
00450     {
00451         int vertex_id = atoi( f_tokens[i].c_str() );
00452 
00453         // Some faces contain format 'vertex/texture'
00454         // Remove the '/texture' and add the vertex to the list
00455         std::size_t slash = f_tokens[i].find( '/' );
00456         if( slash != std::string::npos )
00457         {
00458             std::string face = f_tokens[i].substr( 0, slash );
00459             vertex_id        = atoi( face.c_str() );
00460         }
00461 
00462         next_face.conn[i - 1] = vertex_list[vertex_id - 1];
00463     }
00464 
00465     rval = MBI->create_element( MBTRI, next_face.conn, 3, face_eh );MB_CHK_SET_ERR( rval, "Unable to create new face." );
00466 
00467     return rval;
00468 }
00469 
00470 // The split_quad function divides a quad face into 4 tri faces.
00471 ErrorCode ReadOBJ::split_quad( std::vector< std::string > f_tokens, std::vector< EntityHandle >& vertex_list,
00472                                Range& face_eh )
00473 {
00474     ErrorCode rval;
00475     std::vector< EntityHandle > quad_vert_eh;
00476 
00477     // Loop over quad connectivity getting vertex EHs
00478     for( int i = 1; i < 5; i++ )
00479     {
00480         int vertex_id     = atoi( f_tokens[i].c_str() );
00481         std::size_t slash = f_tokens[i].find( '/' );
00482         if( slash != std::string::npos )
00483         {
00484             std::string face = f_tokens[i].substr( 0, slash );
00485             vertex_id        = atoi( face.c_str() );
00486         }
00487 
00488         quad_vert_eh.push_back( vertex_list[vertex_id - 1] );
00489     }
00490 
00491     // Create new tri faces
00492     rval = create_tri_faces( quad_vert_eh, face_eh );MB_CHK_SET_ERR( rval, "Failed to create triangles when splitting quad." );
00493 
00494     return rval;
00495 }
00496 
00497 ErrorCode ReadOBJ::create_tri_faces( std::vector< EntityHandle > quad_vert_eh,
00498                                      //                    EntityHandle center_vertex_eh,
00499                                      Range& face_eh )
00500 {
00501     ErrorCode rval;
00502     EntityHandle connectivity[3];
00503     EntityHandle new_face;
00504 
00505     connectivity[0] = quad_vert_eh[0];
00506     connectivity[1] = quad_vert_eh[1];
00507     connectivity[2] = quad_vert_eh[2];
00508     rval            = MBI->create_element( MBTRI, connectivity, 3, new_face );
00509     face_eh.insert( new_face );
00510 
00511     connectivity[0] = quad_vert_eh[2];
00512     connectivity[1] = quad_vert_eh[3];
00513     connectivity[2] = quad_vert_eh[0];
00514     rval            = MBI->create_element( MBTRI, connectivity, 3, new_face );
00515     face_eh.insert( new_face );
00516 
00517     return rval;
00518 }
00519 
00520 }  // namespace moab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines