Loading [MathJax]/extensions/tex2jax.js
Mesh Oriented datABase  (version 5.5.1)
An array-based unstructured mesh library
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ReadOBJ.cpp
Go to the documentation of this file.
1 /** 2  * MOAB, a Mesh-Oriented datABase, is a software component for creating, 3  * storing and accessing finite element mesh data. 4  * 5  * Copyright 2004 Sandia Corporation. Under the terms of Contract 6  * DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government 7  * retains certain rights in this software. 8  * 9  * This library is free software; you can redistribute it and/or 10  * modify it under the terms of the GNU Lesser General Public 11  * License as published by the Free Software Foundation; either 12  * version 2.1 of the License, or (at your option) any later version. 13  * 14  */ 15  16 #include "ReadOBJ.hpp" 17 #include <iostream> 18 #include <sstream> 19 #include <fstream> 20 #include <vector> 21 #include <cstdlib> 22 #include <map> 23 #include <cassert> 24 #include <cmath> 25  26 #include "moab/Core.hpp" 27 #include "moab/Interface.hpp" 28 #include "moab/ReadUtilIface.hpp" 29 #include "Internals.hpp" 30 #include "moab/Range.hpp" 31 #include "moab/CartVect.hpp" 32 #include "moab/FileOptions.hpp" 33 #include "FileTokenizer.hpp" 34 #include "MBTagConventions.hpp" 35 #include "moab/CN.hpp" 36 #include "moab/GeomTopoTool.hpp" 37  38 namespace moab 39 { 40  41 ReaderIface* ReadOBJ::factory( Interface* iface ) 42 { 43  return new ReadOBJ( iface ); 44 } 45  46 // Subset of starting tokens currently supported 47 const char* ReadOBJ::delimiters = " "; 48 const char* object_start_token = "o"; 49 const char* group_start_token = "g"; 50 const char* vertex_start_token = "v"; 51 const char* face_start_token = "f"; 52  53 #define OBJ_AMBIGUOUS "AMBIGUOUS" 54 #define OBJ_UNDEFINED "UNDEFINED" 55  56 // Name of geometric entities 57 const char* const geom_name[] = { "Vertex\0", "Curve\0", "Surface\0", "Volume\0" }; 58  59 // Geometric Categories 60 const char geom_category[][CATEGORY_TAG_SIZE] = { "Vertex\0", "Curve\0", "Surface\0", "Volume\0", "Group\0" }; 61  62 // Constructor 63 ReadOBJ::ReadOBJ( Interface* impl ) 64  : MBI( impl ), geom_tag( 0 ), id_tag( 0 ), name_tag( 0 ), category_tag( 0 ), faceting_tol_tag( 0 ), 65  geometry_resabs_tag( 0 ), obj_name_tag( 0 ) 66 { 67  assert( NULL != impl ); 68  MBI->query_interface( readMeshIface ); 69  myGeomTool = new GeomTopoTool( impl ); 70  assert( NULL != readMeshIface ); 71  72  // Get all handles 73  int negone = -1; 74  ErrorCode rval; 75  rval = MBI->tag_get_handle( GEOM_DIMENSION_TAG_NAME, 1, MB_TYPE_INTEGER, geom_tag, MB_TAG_SPARSE | MB_TAG_CREAT, 76  &negone );MB_CHK_ERR_RET( rval ); 77  78  id_tag = MBI->globalId_tag(); 79  80  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 ); 81  82  rval = MBI->tag_get_handle( CATEGORY_TAG_NAME, CATEGORY_TAG_SIZE, MB_TYPE_OPAQUE, category_tag, 83  MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_ERR_RET( rval ); 84  85  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 ); 86  87  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 ); 88  89  rval = 90  MBI->tag_get_handle( "GEOMETRY_RESABS", 1, MB_TYPE_DOUBLE, geometry_resabs_tag, MB_TAG_SPARSE | MB_TAG_CREAT );MB_CHK_ERR_RET( rval ); 91 } 92  93 // Destructor 94 ReadOBJ::~ReadOBJ() 95 { 96  if( readMeshIface ) 97  { 98  MBI->release_interface( readMeshIface ); 99  readMeshIface = 0; 100  } 101  102  delete myGeomTool; 103 } 104  105 ErrorCode ReadOBJ::read_tag_values( const char* /*file_name*/, 106  const char* /*tag_name*/, 107  const FileOptions& /*opts*/, 108  std::vector< int >& /*tag_values_out*/, 109  const SubsetList* /*subset_list*/ ) 110 { 111  return MB_NOT_IMPLEMENTED; 112 } 113  114 // Load the file as called by the Interface function 115 ErrorCode ReadOBJ::load_file( const char* filename, 116  const EntityHandle*, 117  const FileOptions&, 118  const ReaderIface::SubsetList* subset_list, 119  const Tag* /*file_id_tag*/ ) 120 { 121  ErrorCode rval; 122  int ignored = 0; // Number of lines not beginning with o, v, or f 123  std::string line; // The current line being read 124  EntityHandle vert_meshset; 125  EntityHandle curr_meshset; // Current object meshset 126  std::string object_name; 127  std::vector< EntityHandle > vertex_list; 128  int object_id = 0, group_id = 0; // ID number for each volume/surface 129  int num_groups; 130  131  // At this time, there is no support for reading a subset of the file 132  if( subset_list ) 133  { 134  MB_SET_ERR( MB_UNSUPPORTED_OPERATION, "Reading subset of files not supported for OBJ." ); 135  } 136  137  std::ifstream input_file( filename ); // Filestream for OBJ file 138  139  // Check that the file can be read 140  if( !input_file.good() ) 141  { 142  std::cout << "Problems reading file = " << filename << std::endl; 143  return MB_FILE_DOES_NOT_EXIST; 144  } 145  146  // If the file can be read 147  if( input_file.is_open() ) 148  { 149  150  // create meshset for global vertices 151  rval = MBI->create_meshset( MESHSET_SET, vert_meshset );MB_CHK_SET_ERR( rval, "Failed to create global vert meshset." ); 152  153  while( std::getline( input_file, line ) ) 154  { 155  // Skip blank lines in file 156  if( line.length() == 0 ) continue; 157  158  // Tokenize the line 159  std::vector< std::string > tokens; 160  tokenize( line, tokens, delimiters ); 161  162  // Each group and object line must have a name, token size is at least 2 163  // Each vertex and face line should have token size of at least 4 164  if( tokens.size() < 2 ) continue; 165  166  switch( get_keyword( tokens ) ) 167  { 168  // Object line 169  case object_start: { 170  object_id++; 171  object_name = tokens[1]; // Get name of object 172  173  // Create new meshset for object 174  rval = create_new_object( object_name, object_id, curr_meshset );MB_CHK_ERR( rval ); 175  break; 176  } 177  178  // Group line 179  case group_start: { 180  group_id++; 181  num_groups = tokens.size() - 1; 182  std::string group_name = "Group"; 183  for( int i = 0; i < num_groups; i++ ) 184  { 185  group_name = group_name + '_' + tokens[i + 1]; 186  } 187  188  // Create new meshset for group 189  rval = create_new_group( group_name, group_id, curr_meshset );MB_CHK_ERR( rval ); 190  break; 191  } 192  193  // Vertex line 194  case vertex_start: { 195  // Read vertex and return EH 196  EntityHandle new_vertex_eh; 197  rval = create_new_vertex( tokens, new_vertex_eh );MB_CHK_ERR( rval ); 198  199  // Add new vertex EH to list 200  vertex_list.push_back( new_vertex_eh ); 201  202  // Add new vertex EH to the meshset 203  MBI->add_entities( vert_meshset, &new_vertex_eh, 1 );MB_CHK_SET_ERR( rval, "Failed to add vertex to global meshset." ); 204  break; 205  } 206  207  // Face line 208  case face_start: { 209  // Faces in .obj file can have 2, 3, or 4 vertices. If the face has 210  // 3 vertices, the EH will be immediately added to the meshset. 211  // If 4, face is split into triangles. Anything else is ignored. 212  EntityHandle new_face_eh; 213  214  if( tokens.size() == 4 ) 215  { 216  rval = create_new_face( tokens, vertex_list, new_face_eh );MB_CHK_ERR( rval ); 217  218  if( rval == MB_SUCCESS ) 219  { 220  // Add new face EH to the meshset 221  MBI->add_entities( curr_meshset, &new_face_eh, 1 ); 222  } 223  } 224  225  else if( tokens.size() == 5 ) 226  { 227  // Split_quad fxn will create 2 new triangles from 1 quad 228  Range new_faces_eh; 229  rval = split_quad( tokens, vertex_list, new_faces_eh );MB_CHK_ERR( rval ); 230  231  // Add new faces created by split quad to meshset 232  if( rval == MB_SUCCESS ) 233  { 234  MBI->add_entities( curr_meshset, new_faces_eh ); 235  } 236  } 237  238  else 239  { 240  std::cout << "Neither tri nor a quad: " << line << std::endl; 241  } 242  243  break; 244  } 245  246  case valid_unsupported: { 247  // First token is not recognized as a supported character 248  ++ignored; 249  break; 250  } 251  252  default: { 253  MB_SET_ERR( MB_FAILURE, "Invalid/unrecognized line" ); 254  } 255  } 256  } 257  } 258  259  // If no object lines are read (those beginning w/ 'o'), file is not obj type 260  if( object_id == 0 && group_id == 0 ) 261  { 262  MB_SET_ERR( MB_FAILURE, "This is not an obj file. " ); 263  } 264  265  std::cout << "There were " << ignored << " ignored lines in this file." << std::endl; 266  267  input_file.close(); 268  269  return MB_SUCCESS; 270 } 271  272 /* The tokenize function will split an input line 273  * into a vector of strings based upon the delimiter 274  */ 275 void ReadOBJ::tokenize( const std::string& str, std::vector< std::string >& tokens, const char* delimiters2 ) 276 { 277  tokens.clear(); 278  279  std::string::size_type next_token_end, next_token_start = str.find_first_not_of( delimiters2, 0 ); 280  281  while( std::string::npos != next_token_start ) 282  { 283  next_token_end = str.find_first_of( delimiters2, next_token_start ); 284  if( std::string::npos == next_token_end ) 285  { 286  tokens.push_back( str.substr( next_token_start ) ); 287  next_token_start = std::string::npos; 288  } 289  else 290  { 291  tokens.push_back( str.substr( next_token_start, next_token_end - next_token_start ) ); 292  next_token_start = str.find_first_not_of( delimiters2, next_token_end ); 293  } 294  } 295 } 296  297 keyword_type ReadOBJ::get_keyword( std::vector< std::string > tokens ) 298 { 299  std::map< std::string, keyword_type > keywords; 300  301  // currently supported 302  keywords["o"] = object_start; 303  keywords["g"] = group_start; 304  keywords["f"] = face_start; 305  keywords["v"] = vertex_start; 306  307  // not currently supported, will be ignored 308  keywords["vn"] = valid_unsupported; 309  keywords["vt"] = valid_unsupported; 310  keywords["vp"] = valid_unsupported; 311  keywords["s"] = valid_unsupported; 312  keywords["mtllib"] = valid_unsupported; 313  keywords["usemtl"] = valid_unsupported; 314  keywords["#"] = valid_unsupported; 315  keywords["cstype"] = valid_unsupported; 316  keywords["deg"] = valid_unsupported; 317  keywords["bmat"] = valid_unsupported; 318  keywords["step"] = valid_unsupported; 319  keywords["p"] = valid_unsupported; 320  keywords["l"] = valid_unsupported; 321  keywords["curv"] = valid_unsupported; 322  keywords["curv2"] = valid_unsupported; 323  keywords["surf"] = valid_unsupported; 324  keywords["parm"] = valid_unsupported; 325  keywords["trim"] = valid_unsupported; 326  keywords["hole"] = valid_unsupported; 327  keywords["scrv"] = valid_unsupported; 328  keywords["sp"] = valid_unsupported; 329  keywords["end"] = valid_unsupported; 330  keywords["mg"] = valid_unsupported; 331  keywords["bevel"] = valid_unsupported; 332  keywords["c_interp"] = valid_unsupported; 333  keywords["d_interp"] = valid_unsupported; 334  keywords["lod"] = valid_unsupported; 335  keywords["shadow_obj"] = valid_unsupported; 336  keywords["trace_obj"] = valid_unsupported; 337  keywords["ctech"] = valid_unsupported; 338  keywords["stech"] = valid_unsupported; 339  340  return keywords[match( tokens[0], keywords )]; 341 } 342  343 template < typename T > 344 std::string ReadOBJ::match( const std::string& token, std::map< std::string, T >& tokenList ) 345 { 346  // Initialize with no match and obj_undefined as return string 347  std::string best_match = OBJ_UNDEFINED; 348  349  // Search the map 350  for( typename std::map< std::string, T >::iterator thisToken = tokenList.begin(); thisToken != tokenList.end(); 351  ++thisToken ) 352  { 353  // If a perfect match break the loop (assume keyword list is unambiguous) 354  if( token == ( *thisToken ).first ) 355  { 356  best_match = token; 357  break; 358  } 359  } 360  361  // Possible return values: OBJ_UNDEFINED, keyword from list 362  return best_match; 363 } 364  365 /* 366  * The create_new_object function starts a new meshset for each object 367  * that will contain all faces that make up the object. 368  */ 369 ErrorCode ReadOBJ::create_new_object( std::string object_name, int curr_object, EntityHandle& object_meshset ) 370 { 371  ErrorCode rval; 372  373  // Create meshset to store object 374  // This is also referred to as the surface meshset 375  rval = MBI->create_meshset( MESHSET_SET, object_meshset );MB_CHK_SET_ERR( rval, "Failed to generate object mesh set." ); 376  377  // Set surface meshset tags 378  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." ); 379  380  rval = MBI->tag_set_data( id_tag, &object_meshset, 1, &( curr_object ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." ); 381  382  int dim = 2; 383  rval = MBI->tag_set_data( geom_tag, &object_meshset, 1, &( dim ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set dim tag." ); 384  385  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." ); 386  387  /* Create volume entity set corresponding to surface 388  The volume meshset will have one child-- 389  the meshset of the surface that bounds the object. 390  */ 391  EntityHandle vol_meshset; 392  rval = MBI->create_meshset( MESHSET_SET, vol_meshset );MB_CHK_SET_ERR( rval, "Failed to create volume mesh set." ); 393  394  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." ); 395  396  /* Set volume meshset tags 397  The volume meshset is tagged with the same name as the surface meshset 398  for each object because of the direct relation between these entities. 399  */ 400  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." ); 401  402  rval = MBI->tag_set_data( id_tag, &vol_meshset, 1, &( curr_object ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." ); 403  404  dim = 3; 405  rval = MBI->tag_set_data( geom_tag, &vol_meshset, 1, &( dim ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set dim tag." ); 406  407  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." ); 408  409  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." ); 410  411  rval = myGeomTool->set_sense( object_meshset, vol_meshset, SENSE_FORWARD );MB_CHK_SET_ERR( rval, "Failed to set surface sense." ); 412  413  return rval; 414 } 415  416 /* 417  * The create_new_group function starts a new meshset for each group 418  * that will contain all faces that make up the group 419  */ 420 ErrorCode ReadOBJ::create_new_group( std::string group_name, int curr_group, EntityHandle& group_meshset ) 421 { 422  ErrorCode rval; 423  424  // Create meshset to store group 425  rval = MBI->create_meshset( MESHSET_SET, group_meshset );MB_CHK_SET_ERR( rval, "Failed to generate group mesh set." ); 426  427  // Set meshset tags 428  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." ); 429  430  rval = MBI->tag_set_data( id_tag, &group_meshset, 1, &( curr_group ) );MB_CHK_SET_ERR( rval, "Failed to set mesh set ID tag." ); 431  432  return rval; 433 } 434  435 /* The create_new_vertex function converts a vector 436  of tokens (v x y z) to 437  the vertex format; a structure that has the three 438  coordinates as members. 439  */ 440 ErrorCode ReadOBJ::create_new_vertex( std::vector< std::string > v_tokens, EntityHandle& vertex_eh ) 441 { 442  ErrorCode rval; 443  vertex next_vertex; 444  445  for( int i = 1; i < 4; i++ ) 446  next_vertex.coord[i - 1] = atof( v_tokens[i].c_str() ); 447  448  rval = MBI->create_vertex( next_vertex.coord, vertex_eh );MB_CHK_SET_ERR( rval, "Unbale to create vertex." ); 449  450  return rval; 451 } 452  453 /* The create_new_face function converts a vector 454  of tokens ( f v1 v2 v3) ) to the face format; 455  a structure that has the three 456  connectivity points as members. 457  */ 458 ErrorCode ReadOBJ::create_new_face( std::vector< std::string > f_tokens, 459  const std::vector< EntityHandle >& vertex_list, 460  EntityHandle& face_eh ) 461 { 462  face next_face; 463  ErrorCode rval; 464  465  for( int i = 1; i < 4; i++ ) 466  { 467  int vertex_id = atoi( f_tokens[i].c_str() ); 468  469  // Some faces contain format 'vertex/texture' 470  // Remove the '/texture' and add the vertex to the list 471  std::size_t slash = f_tokens[i].find( '/' ); 472  if( slash != std::string::npos ) 473  { 474  std::string face = f_tokens[i].substr( 0, slash ); 475  vertex_id = atoi( face.c_str() ); 476  } 477  478  next_face.conn[i - 1] = vertex_list[vertex_id - 1]; 479  } 480  481  rval = MBI->create_element( MBTRI, next_face.conn, 3, face_eh );MB_CHK_SET_ERR( rval, "Unable to create new face." ); 482  483  return rval; 484 } 485  486 // The split_quad function divides a quad face into 4 tri faces. 487 ErrorCode ReadOBJ::split_quad( std::vector< std::string > f_tokens, 488  std::vector< EntityHandle >& vertex_list, 489  Range& face_eh ) 490 { 491  ErrorCode rval; 492  std::vector< EntityHandle > quad_vert_eh; 493  494  // Loop over quad connectivity getting vertex EHs 495  for( int i = 1; i < 5; i++ ) 496  { 497  int vertex_id = atoi( f_tokens[i].c_str() ); 498  std::size_t slash = f_tokens[i].find( '/' ); 499  if( slash != std::string::npos ) 500  { 501  std::string face = f_tokens[i].substr( 0, slash ); 502  vertex_id = atoi( face.c_str() ); 503  } 504  505  quad_vert_eh.push_back( vertex_list[vertex_id - 1] ); 506  } 507  508  // Create new tri faces 509  rval = create_tri_faces( quad_vert_eh, face_eh );MB_CHK_SET_ERR( rval, "Failed to create triangles when splitting quad." ); 510  511  return rval; 512 } 513  514 ErrorCode ReadOBJ::create_tri_faces( std::vector< EntityHandle > quad_vert_eh, 515  // EntityHandle center_vertex_eh, 516  Range& face_eh ) 517 { 518  ErrorCode rval; 519  EntityHandle connectivity[3]; 520  EntityHandle new_face; 521  522  connectivity[0] = quad_vert_eh[0]; 523  connectivity[1] = quad_vert_eh[1]; 524  connectivity[2] = quad_vert_eh[2]; 525  rval = MBI->create_element( MBTRI, connectivity, 3, new_face ); 526  face_eh.insert( new_face ); 527  528  connectivity[0] = quad_vert_eh[2]; 529  connectivity[1] = quad_vert_eh[3]; 530  connectivity[2] = quad_vert_eh[0]; 531  rval = MBI->create_element( MBTRI, connectivity, 3, new_face ); 532  face_eh.insert( new_face ); 533  534  return rval; 535 } 536  537 } // namespace moab