#include <ReadOBJ.hpp>
Public Member Functions | |
ErrorCode | load_file (const char *file_name, const EntityHandle *file_set, const FileOptions &opts, const SubsetList *subset_list=0, const Tag *file_id_tag=0) |
Load mesh from a file. More... | |
ErrorCode | read_tag_values (const char *file_name, const char *tag_name, const FileOptions &opts, std::vector< int > &tag_values_out, const SubsetList *subset_list=0) |
Read tag values from a file. More... | |
ReadOBJ (Interface *impl=NULL) | |
Constructor. More... | |
virtual | ~ReadOBJ () |
Destructor. More... | |
![]() | |
virtual | ~ReaderIface () |
Static Public Member Functions | |
static ReaderIface * | factory (Interface *) |
factory method More... | |
Private Member Functions | |
keyword_type | get_keyword (std::vector< std::string > tokens) |
template<typename T > | |
std::string | match (const std::string &token, std::map< std::string, T > &tokenList) |
void | tokenize (const std::string &str, std::vector< std::string > &tokens, const char *delimiters) |
ErrorCode | create_new_object (std::string object_name, int object_id, EntityHandle &curr_obj_meshset) |
ErrorCode | create_new_group (std::string object_name, int curr_object, EntityHandle &object_meshset) |
ErrorCode | create_new_vertex (std::vector< std::string > v_tokens, EntityHandle &vertex_eh) |
ErrorCode | create_new_face (std::vector< std::string > f_tokens, const std::vector< EntityHandle > &vertex_list, EntityHandle &face_eh) |
ErrorCode | split_quad (std::vector< std::string > f_tokens, std::vector< EntityHandle > &vertex_list, Range &face_eh) |
ErrorCode | create_tri_faces (std::vector< EntityHandle > quad_vert_eh, Range &face_eh) |
Private Attributes | |
ReadUtilIface * | readMeshIface |
Interface * | MBI |
interface instance More... | |
GeomTopoTool * | myGeomTool |
Tag | geom_tag |
Tag | id_tag |
Tag | name_tag |
Tag | category_tag |
Tag | faceting_tol_tag |
Tag | geometry_resabs_tag |
Tag | obj_name_tag |
Static Private Attributes | |
static const char * | delimiters = " " |
Definition at line 119 of file ReadOBJ.hpp.
moab::ReadOBJ::ReadOBJ | ( | Interface * | impl = NULL | ) |
Constructor.
Definition at line 63 of file ReadOBJ.cpp.
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 }
References category_tag, CATEGORY_TAG_NAME, CATEGORY_TAG_SIZE, ErrorCode, faceting_tol_tag, GEOM_DIMENSION_TAG_NAME, geom_tag, geometry_resabs_tag, moab::Interface::globalId_tag(), id_tag, MB_CHK_ERR_RET, MB_TAG_CREAT, MB_TAG_SPARSE, MB_TYPE_DOUBLE, MB_TYPE_INTEGER, MB_TYPE_OPAQUE, MBI, myGeomTool, name_tag, NAME_TAG_NAME, NAME_TAG_SIZE, obj_name_tag, moab::Interface::query_interface(), readMeshIface, and moab::Interface::tag_get_handle().
Referenced by factory().
|
virtual |
Destructor.
Definition at line 94 of file ReadOBJ.cpp.
95 {
96 if( readMeshIface )
97 {
98 MBI->release_interface( readMeshIface );
99 readMeshIface = 0;
100 }
101
102 delete myGeomTool;
103 }
References MBI, myGeomTool, readMeshIface, and moab::Interface::release_interface().
|
private |
Definition at line 458 of file ReadOBJ.cpp.
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 }
References face::conn, moab::Interface::create_element(), ErrorCode, MB_CHK_SET_ERR, MBI, and MBTRI.
Referenced by load_file().
|
private |
Definition at line 420 of file ReadOBJ.cpp.
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 }
References moab::Interface::create_meshset(), ErrorCode, id_tag, MB_CHK_SET_ERR, MBI, MESHSET_SET, name_tag, and moab::Interface::tag_set_data().
Referenced by load_file().
|
private |
Definition at line 369 of file ReadOBJ.cpp.
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 }
References moab::Interface::add_parent_child(), category_tag, moab::Interface::create_meshset(), dim, ErrorCode, moab::geom_category, moab::geom_name, geom_tag, id_tag, MB_CHK_SET_ERR, MBI, MESHSET_SET, myGeomTool, name_tag, obj_name_tag, moab::GeomTopoTool::set_sense(), and moab::Interface::tag_set_data().
Referenced by load_file().
|
private |
Definition at line 440 of file ReadOBJ.cpp.
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 }
References vertex::coord, moab::Interface::create_vertex(), ErrorCode, MB_CHK_SET_ERR, and MBI.
Referenced by load_file().
|
private |
Definition at line 514 of file ReadOBJ.cpp.
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 }
References moab::Interface::create_element(), ErrorCode, moab::Range::insert(), MBI, and MBTRI.
Referenced by split_quad().
|
static |
factory method
Definition at line 41 of file ReadOBJ.cpp.
42 {
43 return new ReadOBJ( iface );
44 }
References iface, and ReadOBJ().
Referenced by moab::ReaderWriterSet::ReaderWriterSet().
|
private |
Definition at line 297 of file ReadOBJ.cpp.
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 }
References moab::face_start, moab::group_start, match(), moab::object_start, moab::valid_unsupported, and moab::vertex_start.
Referenced by load_file().
|
virtual |
Load mesh from a file.
Method all readers must provide to import a mesh.
file_name | The file to read. |
file_set | Optional pointer to entity set representing file. If this is not NULL, reader may optionally tag the pointed-to set with format-specific meta-data. |
subset_list | An optional struct pointer specifying the tags identifying entity sets to be read. |
file_id_tag | If specified, reader should store for each entity it reads, a unique integer ID for this tag. |
Implements moab::ReaderIface.
Definition at line 115 of file ReadOBJ.cpp.
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 }
References moab::Interface::add_entities(), moab::Interface::create_meshset(), create_new_face(), create_new_group(), create_new_object(), create_new_vertex(), delimiters, ErrorCode, moab::face_start, get_keyword(), moab::group_start, input_file, MB_CHK_ERR, MB_CHK_SET_ERR, MB_FILE_DOES_NOT_EXIST, MB_SET_ERR, MB_SUCCESS, MB_UNSUPPORTED_OPERATION, MBI, MESHSET_SET, moab::object_start, split_quad(), tokenize(), moab::valid_unsupported, and moab::vertex_start.
|
private |
Definition at line 344 of file ReadOBJ.cpp.
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 }
References OBJ_UNDEFINED.
Referenced by get_keyword().
|
virtual |
Read tag values from a file.
Read the list if all integer tag values from the file for a tag that is a single integer value per entity.
file_name | The file to read. |
tag_name | The tag for which to read values |
tag_values_out | Output: The list of tag values. |
subset_list | An array of tag name and value sets specifying the subset of the file to read. If multiple tags are specified, the sets that match all tags (intersection) should be read. |
subset_list_length | The length of the 'subset_list' array. |
Implements moab::ReaderIface.
Definition at line 105 of file ReadOBJ.cpp.
110 {
111 return MB_NOT_IMPLEMENTED;
112 }
References MB_NOT_IMPLEMENTED.
|
private |
Definition at line 487 of file ReadOBJ.cpp.
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 }
References create_tri_faces(), ErrorCode, and MB_CHK_SET_ERR.
Referenced by load_file().
|
private |
Definition at line 275 of file ReadOBJ.cpp.
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 }
Referenced by load_file().
|
private |
Definition at line 152 of file ReadOBJ.hpp.
Referenced by create_new_object(), and ReadOBJ().
|
staticprivate |
Definition at line 167 of file ReadOBJ.hpp.
Referenced by load_file().
|
private |
Definition at line 152 of file ReadOBJ.hpp.
Referenced by ReadOBJ().
|
private |
Definition at line 152 of file ReadOBJ.hpp.
Referenced by create_new_object(), and ReadOBJ().
|
private |
Definition at line 152 of file ReadOBJ.hpp.
Referenced by ReadOBJ().
|
private |
Definition at line 152 of file ReadOBJ.hpp.
Referenced by create_new_group(), create_new_object(), and ReadOBJ().
|
private |
interface instance
Definition at line 148 of file ReadOBJ.hpp.
Referenced by create_new_face(), create_new_group(), create_new_object(), create_new_vertex(), create_tri_faces(), load_file(), ReadOBJ(), and ~ReadOBJ().
|
private |
Definition at line 150 of file ReadOBJ.hpp.
Referenced by create_new_object(), ReadOBJ(), and ~ReadOBJ().
|
private |
Definition at line 152 of file ReadOBJ.hpp.
Referenced by create_new_group(), create_new_object(), and ReadOBJ().
|
private |
Definition at line 152 of file ReadOBJ.hpp.
Referenced by create_new_object(), and ReadOBJ().
|
private |
Definition at line 145 of file ReadOBJ.hpp.
Referenced by ReadOBJ(), and ~ReadOBJ().