1 /* ========================================================================= *
2 * *
3 * OpenMesh *
4 * Copyright (c) 2001-2015, RWTH-Aachen University *
5 * Department of Computer Graphics and Multimedia *
6 * All rights reserved. *
7 * www.openmesh.org *
8 * *
9 *---------------------------------------------------------------------------*
10 * This file is part of OpenMesh. *
11 *---------------------------------------------------------------------------*
12 * *
13 * Redistribution and use in source and binary forms, with or without *
14 * modification, are permitted provided that the following conditions *
15 * are met: *
16 * *
17 * 1. Redistributions of source code must retain the above copyright notice, *
18 * this list of conditions and the following disclaimer. *
19 * *
20 * 2. Redistributions in binary form must reproduce the above copyright *
21 * notice, this list of conditions and the following disclaimer in the *
22 * documentation and/or other materials provided with the distribution. *
23 * *
24 * 3. Neither the name of the copyright holder nor the names of its *
25 * contributors may be used to endorse or promote products derived from *
26 * this software without specific prior written permission. *
27 * *
28 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
29 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
30 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
32 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
33 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
34 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
35 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
36 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
37 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
38 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
39 * *
40 * ========================================================================= */
41
42
43
44
45 //== INCLUDES =================================================================
46
47
48 // STL
49 #include <map>
50
51 #include <float.h>
52 #include <fstream>
53 #include <cstring>
54
55 // OpenMesh
56 #include <OpenMesh/Core/IO/BinaryHelper.hh>
57 #include <OpenMesh/Core/IO/reader/STLReader.hh>
58 #include <OpenMesh/Core/IO/IOManager.hh>
59
60 //comppare strings crossplatform ignorign case
61 #ifdef _WIN32
62 #define strnicmp _strnicmp
63 #else
64 #define strnicmp strncasecmp
65 #endif
66
67
68 //=== NAMESPACES ==============================================================
69
70
71 namespace OpenMesh {
72 namespace IO {
73
74
75 //=== INSTANCIATE =============================================================
76
77
78 // register the STLReader singleton with MeshReader
79 _STLReader_ __STLReaderInstance;
STLReader()80 _STLReader_& STLReader() { return __STLReaderInstance; }
81
82
83 //=== IMPLEMENTATION ==========================================================
84
85
86 _STLReader_::
_STLReader_()87 _STLReader_()
88 : eps_(FLT_MIN)
89 {
90 IOManager().register_module(this);
91 }
92
93
94 //-----------------------------------------------------------------------------
95
96
97 bool
98 _STLReader_::
read(const std::string & _filename,BaseImporter & _bi,Options & _opt)99 read(const std::string& _filename, BaseImporter& _bi, Options& _opt)
100 {
101 bool result = false;
102
103 STL_Type file_type = NONE;
104
105 if ( check_extension( _filename, "stla" ) )
106 {
107 file_type = STLA;
108 }
109
110 else if ( check_extension( _filename, "stlb" ) )
111 {
112 file_type = STLB;
113 }
114
115 else if ( check_extension( _filename, "stl" ) )
116 {
117 file_type = check_stl_type(_filename);
118 }
119
120
121 switch (file_type)
122 {
123 case STLA:
124 {
125 result = read_stla(_filename, _bi, _opt);
126 _opt -= Options::Binary;
127 break;
128 }
129
130 case STLB:
131 {
132 result = read_stlb(_filename, _bi, _opt);
133 _opt += Options::Binary;
134 break;
135 }
136
137 default:
138 {
139 result = false;
140 break;
141 }
142 }
143
144
145 return result;
146 }
147
148 bool
read(std::istream & _is,BaseImporter & _bi,Options & _opt)149 _STLReader_::read(std::istream& _is,
150 BaseImporter& _bi,
151 Options& _opt)
152 {
153
154 bool result = false;
155
156 if (_opt & Options::Binary)
157 result = read_stlb(_is, _bi, _opt);
158 else
159 result = read_stla(_is, _bi, _opt);
160
161 return result;
162 }
163
164
165 //-----------------------------------------------------------------------------
166
167
168 #ifndef DOXY_IGNORE_THIS
169
170 class CmpVec
171 {
172 public:
173
CmpVec(float _eps=FLT_MIN)174 explicit CmpVec(float _eps=FLT_MIN) : eps_(_eps) {}
175
operator ()(const Vec3f & _v0,const Vec3f & _v1) const176 bool operator()( const Vec3f& _v0, const Vec3f& _v1 ) const
177 {
178 if (fabs(_v0[0] - _v1[0]) <= eps_)
179 {
180 if (fabs(_v0[1] - _v1[1]) <= eps_)
181 {
182 return (_v0[2] < _v1[2] - eps_);
183 }
184 else return (_v0[1] < _v1[1] - eps_);
185 }
186 else return (_v0[0] < _v1[0] - eps_);
187 }
188
189 private:
190 float eps_;
191 };
192
193 #endif
194
195
196 //-----------------------------------------------------------------------------
197
trimStdString(std::string & _string)198 void trimStdString( std::string& _string) {
199 // Trim Both leading and trailing spaces
200
201 size_t start = _string.find_first_not_of(" \t\r\n");
202 size_t end = _string.find_last_not_of(" \t\r\n");
203
204 if(( std::string::npos == start ) || ( std::string::npos == end))
205 _string = "";
206 else
207 _string = _string.substr( start, end-start+1 );
208 }
209
210 //-----------------------------------------------------------------------------
211
212 bool
213 _STLReader_::
read_stla(const std::string & _filename,BaseImporter & _bi,Options & _opt) const214 read_stla(const std::string& _filename, BaseImporter& _bi, Options& _opt) const
215 {
216 std::fstream in( _filename.c_str(), std::ios_base::in );
217
218 if (!in)
219 {
220 omerr() << "[STLReader] : cannot not open file "
221 << _filename
222 << std::endl;
223 return false;
224 }
225
226 bool res = read_stla(in, _bi, _opt);
227
228 if (in)
229 in.close();
230
231 return res;
232 }
233
234 //-----------------------------------------------------------------------------
235
236 bool
237 _STLReader_::
read_stla(std::istream & _in,BaseImporter & _bi,Options & _opt) const238 read_stla(std::istream& _in, BaseImporter& _bi, Options& _opt) const
239 {
240
241 unsigned int i;
242 OpenMesh::Vec3f v;
243 OpenMesh::Vec3f n;
244 BaseImporter::VHandles vhandles;
245
246 CmpVec comp(eps_);
247 std::map<Vec3f, VertexHandle, CmpVec> vMap(comp);
248 std::map<Vec3f, VertexHandle, CmpVec>::iterator vMapIt;
249
250 std::string line;
251
252 std::string garbage;
253 std::stringstream strstream;
254
255 bool facet_normal(false);
256
257 while( _in && !_in.eof() ) {
258
259 // Get one line
260 std::getline(_in, line);
261 if ( _in.bad() ){
262 omerr() << " Warning! Could not read stream properly!\n";
263 return false;
264 }
265
266 // Trim Both leading and trailing spaces
267 trimStdString(line);
268
269 // Normal found?
270 if (line.find("facet normal") != std::string::npos) {
271 strstream.str(line);
272 strstream.clear();
273
274 // facet
275 strstream >> garbage;
276
277 // normal
278 strstream >> garbage;
279
280 strstream >> n[0];
281 strstream >> n[1];
282 strstream >> n[2];
283
284 facet_normal = true;
285 }
286
287 // Detected a triangle
288 if ( (line.find("outer") != std::string::npos) || (line.find("OUTER") != std::string::npos ) ) {
289
290 vhandles.clear();
291
292 for (i=0; i<3; ++i) {
293 // Get one vertex
294 std::getline(_in, line);
295 trimStdString(line);
296
297 strstream.str(line);
298 strstream.clear();
299
300 strstream >> garbage;
301
302 strstream >> v[0];
303 strstream >> v[1];
304 strstream >> v[2];
305
306 // has vector been referenced before?
307 if ((vMapIt=vMap.find(v)) == vMap.end())
308 {
309 // No : add vertex and remember idx/vector mapping
310 VertexHandle handle = _bi.add_vertex(v);
311 vhandles.push_back(handle);
312 vMap[v] = handle;
313 }
314 else
315 // Yes : get index from map
316 vhandles.push_back(vMapIt->second);
317
318 }
319
320 // Add face only if it is not degenerated
321 if ((vhandles[0] != vhandles[1]) &&
322 (vhandles[0] != vhandles[2]) &&
323 (vhandles[1] != vhandles[2])) {
324
325
326 FaceHandle fh = _bi.add_face(vhandles);
327
328 // set the normal if requested
329 // if a normal was requested but could not be found we unset the option
330 if (facet_normal) {
331 if (fh.is_valid() && _opt.face_has_normal())
332 _bi.set_normal(fh, n);
333 } else
334 _opt -= Options::FaceNormal;
335 }
336
337 facet_normal = false;
338 }
339 }
340
341 return true;
342 }
343
344 //-----------------------------------------------------------------------------
345
346 bool
347 _STLReader_::
read_stlb(const std::string & _filename,BaseImporter & _bi,Options & _opt) const348 read_stlb(const std::string& _filename, BaseImporter& _bi, Options& _opt) const
349 {
350 std::fstream in( _filename.c_str(), std::ios_base::in | std::ios_base::binary);
351
352 if (!in)
353 {
354 omerr() << "[STLReader] : cannot not open file "
355 << _filename
356 << std::endl;
357 return false;
358 }
359
360 bool res = read_stlb(in, _bi, _opt);
361
362 if (in)
363 in.close();
364
365 return res;
366 }
367
368 //-----------------------------------------------------------------------------
369
370 bool
371 _STLReader_::
read_stlb(std::istream & _in,BaseImporter & _bi,Options & _opt) const372 read_stlb(std::istream& _in, BaseImporter& _bi, Options& _opt) const
373 {
374 char dummy[100];
375 bool swapFlag;
376 unsigned int i, nT;
377 OpenMesh::Vec3f v, n;
378 BaseImporter::VHandles vhandles;
379
380 std::map<Vec3f, VertexHandle, CmpVec> vMap;
381 std::map<Vec3f, VertexHandle, CmpVec>::iterator vMapIt;
382
383
384 // check size of types
385 if ((sizeof(float) != 4) || (sizeof(int) != 4)) {
386 omerr() << "[STLReader] : wrong type size\n";
387 return false;
388 }
389
390 // determine endian mode
391 union { unsigned int i; unsigned char c[4]; } endian_test;
392 endian_test.i = 1;
393 swapFlag = (endian_test.c[3] == 1);
394
395 // read number of triangles
396 _in.read(dummy, 80);
397 nT = read_int(_in, swapFlag);
398
399 // read triangles
400 while (nT)
401 {
402 vhandles.clear();
403
404 // read triangle normal
405 n[0] = read_float(_in, swapFlag);
406 n[1] = read_float(_in, swapFlag);
407 n[2] = read_float(_in, swapFlag);
408
409 // triangle's vertices
410 for (i=0; i<3; ++i)
411 {
412 v[0] = read_float(_in, swapFlag);
413 v[1] = read_float(_in, swapFlag);
414 v[2] = read_float(_in, swapFlag);
415
416 // has vector been referenced before?
417 if ((vMapIt=vMap.find(v)) == vMap.end())
418 {
419 // No : add vertex and remember idx/vector mapping
420 VertexHandle handle = _bi.add_vertex(v);
421 vhandles.push_back(handle);
422 vMap[v] = handle;
423 }
424 else
425 // Yes : get index from map
426 vhandles.push_back(vMapIt->second);
427 }
428
429
430 // Add face only if it is not degenerated
431 if ((vhandles[0] != vhandles[1]) &&
432 (vhandles[0] != vhandles[2]) &&
433 (vhandles[1] != vhandles[2])) {
434 FaceHandle fh = _bi.add_face(vhandles);
435
436 if (fh.is_valid() && _opt.face_has_normal())
437 _bi.set_normal(fh, n);
438 }
439
440 _in.read(dummy, 2);
441 --nT;
442 }
443
444 return true;
445 }
446
447 //-----------------------------------------------------------------------------
448
449 _STLReader_::STL_Type
450 _STLReader_::
check_stl_type(const std::string & _filename) const451 check_stl_type(const std::string& _filename) const
452 {
453
454 // open file
455 std::ifstream ifs (_filename.c_str(), std::ifstream::binary);
456 if(!ifs.good())
457 {
458 omerr() << "could not open file" << _filename << std::endl;
459 return NONE;
460 }
461
462 //find first non whitespace character
463 std::string line = "";
464 std::size_t firstChar;
465 while(line.empty() && ifs.good())
466 {
467 std::getline(ifs,line);
468 firstChar = line.find_first_not_of("\t ");
469 }
470
471 //check for ascii keyword solid
472 if(strnicmp("solid",&line[firstChar],5) == 0)
473 {
474 return STLA;
475 }
476 ifs.close();
477
478 //if the file does not start with solid it is probably STLB
479 //check the file size to verify it.
480
481 //open the file
482 FILE* in = fopen(_filename.c_str(), "rb");
483 if (!in) return NONE;
484
485 // determine endian mode
486 union { unsigned int i; unsigned char c[4]; } endian_test;
487 endian_test.i = 1;
488 bool swapFlag = (endian_test.c[3] == 1);
489
490
491 // read number of triangles
492 char dummy[100];
493 fread(dummy, 1, 80, in);
494 size_t nT = read_int(in, swapFlag);
495
496
497 // compute file size from nT
498 size_t binary_size = 84 + nT*50;
499
500 // get actual file size
501 size_t file_size(0);
502 rewind(in);
503 while (!feof(in))
504 file_size += fread(dummy, 1, 100, in);
505 fclose(in);
506
507 // if sizes match -> it's STLB
508 return (binary_size == file_size ? STLB : NONE);
509 }
510
511
512 //=============================================================================
513 } // namespace IO
514 } // namespace OpenMesh
515 //=============================================================================
516