MOAB: Mesh Oriented datABase
(version 5.4.1)
|
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