STLReader.cc 12.5 KB
Newer Older
Jan Möbius's avatar
Jan Möbius committed
1
2
3
/*===========================================================================*\
 *                                                                           *
 *                               OpenMesh                                    *
Jan Möbius's avatar
Jan Möbius committed
4
 *      Copyright (C) 2001-2013 by Computer Graphics Group, RWTH Aachen      *
Jan Möbius's avatar
Jan Möbius committed
5
6
 *                           www.openmesh.org                                *
 *                                                                           *
7
 *---------------------------------------------------------------------------*
8
 *  This file is part of OpenMesh.                                           *
Jan Möbius's avatar
Jan Möbius committed
9
 *                                                                           *
10
 *  OpenMesh is free software: you can redistribute it and/or modify         *
11
12
13
14
 *  it under the terms of the GNU Lesser General Public License as           *
 *  published by the Free Software Foundation, either version 3 of           *
 *  the License, or (at your option) any later version with the              *
 *  following exceptions:                                                    *
Jan Möbius's avatar
Jan Möbius committed
15
 *                                                                           *
16
17
18
19
20
21
22
 *  If other files instantiate templates or use macros                       *
 *  or inline functions from this file, or you compile this file and         *
 *  link it with other files to produce an executable, this file does        *
 *  not by itself cause the resulting executable to be covered by the        *
 *  GNU Lesser General Public License. This exception does not however       *
 *  invalidate any other reasons why the executable file might be            *
 *  covered by the GNU Lesser General Public License.                        *
Jan Möbius's avatar
Jan Möbius committed
23
 *                                                                           *
24
25
26
27
 *  OpenMesh is distributed in the hope that it will be useful,              *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
 *  GNU Lesser General Public License for more details.                      *
Jan Möbius's avatar
Jan Möbius committed
28
 *                                                                           *
29
30
31
32
 *  You should have received a copy of the GNU LesserGeneral Public          *
 *  License along with OpenMesh.  If not,                                    *
 *  see <http://www.gnu.org/licenses/>.                                      *
 *                                                                           *
33
\*===========================================================================*/
34
35

/*===========================================================================*\
36
 *                                                                           *
37
38
 *   $Revision$                                                         *
 *   $Date$                   *
Jan Möbius's avatar
Jan Möbius committed
39
40
41
42
43
44
45
46
47
48
49
 *                                                                           *
\*===========================================================================*/


//== INCLUDES =================================================================


// STL
#include <map>

#include <float.h>
50
#include <fstream>
Jan Möbius's avatar
Jan Möbius committed
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

// OpenMesh
#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/IO/BinaryHelper.hh>
#include <OpenMesh/Core/IO/reader/STLReader.hh>
#include <OpenMesh/Core/IO/IOManager.hh>
#include <OpenMesh/Core/System/omstream.hh>
#include <OpenMesh/Core/IO/importer/BaseImporter.hh>


//=== NAMESPACES ==============================================================


namespace OpenMesh {
namespace IO {


//=== INSTANCIATE =============================================================


// register the STLReader singleton with MeshReader
_STLReader_  __STLReaderInstance;
_STLReader_&  STLReader() { return __STLReaderInstance; }


//=== IMPLEMENTATION ==========================================================


_STLReader_::
80
_STLReader_()
Jan Möbius's avatar
Jan Möbius committed
81
82
  : eps_(FLT_MIN)
{
83
  IOManager().register_module(this);
Jan Möbius's avatar
Jan Möbius committed
84
85
86
87
88
89
}


//-----------------------------------------------------------------------------


90
bool
Jan Möbius's avatar
Jan Möbius committed
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
_STLReader_::
read(const std::string& _filename, BaseImporter& _bi, Options& _opt)
{
  bool result = false;

  STL_Type file_type = NONE;

  if ( check_extension( _filename, "stla" ) )
  {
    file_type = STLA;
  }

  else if ( check_extension( _filename, "stlb" ) )
  {
    file_type = STLB;
  }

  else if ( check_extension( _filename, "stl" ) )
  {
    file_type = check_stl_type(_filename);
  }


  switch (file_type)
  {
    case STLA:
    {
118
      result = read_stla(_filename, _bi, _opt);
Jan Möbius's avatar
Jan Möbius committed
119
120
121
      _opt -= Options::Binary;
      break;
    }
122

Jan Möbius's avatar
Jan Möbius committed
123
124
    case STLB:
    {
125
      result = read_stlb(_filename, _bi, _opt);
Jan Möbius's avatar
Jan Möbius committed
126
127
128
      _opt += Options::Binary;
      break;
    }
129
130

    default:
Jan Möbius's avatar
Jan Möbius committed
131
132
133
134
135
136
137
138
139
140
    {
      result = false;
      break;
    }
  }


  return result;
}

141
142
143
144
145
146
bool
_STLReader_::read(std::istream& _is,
		 BaseImporter& _bi,
		 Options& _opt)
{

147
148
149
  bool result = false;

  if (_opt & Options::Binary)
150
    result = read_stlb(_is, _bi, _opt);
151
  else
152
    result = read_stla(_is, _bi, _opt);
153

154
  return result;
155
156
}

Jan Möbius's avatar
Jan Möbius committed
157
158
159
160
161
162
163
164
165
166
167
168
169
170

//-----------------------------------------------------------------------------


#ifndef DOXY_IGNORE_THIS

class CmpVec
{
public:

  CmpVec(float _eps=FLT_MIN) : eps_(_eps) {}

  bool operator()( const Vec3f& _v0, const Vec3f& _v1 ) const
  {
171
    if (fabs(_v0[0] - _v1[0]) <= eps_)
Jan Möbius's avatar
Jan Möbius committed
172
    {
173
      if (fabs(_v0[1] - _v1[1]) <= eps_)
Jan Möbius's avatar
Jan Möbius committed
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
      {
	return (_v0[2] < _v1[2] - eps_);
      }
      else return (_v0[1] < _v1[1] - eps_);
    }
    else return (_v0[0] < _v1[0] - eps_);
  }

private:
  float eps_;
};

#endif


//-----------------------------------------------------------------------------

191
192
193
194
195
196
197
198
199
200
201
202
203
void trimStdString( std::string& _string) {
  // Trim Both leading and trailing spaces

  size_t start = _string.find_first_not_of(" \t\r\n");
  size_t end   = _string.find_last_not_of(" \t\r\n");

  if(( std::string::npos == start ) || ( std::string::npos == end))
    _string = "";
  else
    _string = _string.substr( start, end-start+1 );
}

//-----------------------------------------------------------------------------
Jan Möbius's avatar
Jan Möbius committed
204

205
bool
Jan Möbius's avatar
Jan Möbius committed
206
_STLReader_::
207
read_stla(const std::string& _filename, BaseImporter& _bi, Options& _opt) const
Jan Möbius's avatar
Jan Möbius committed
208
209
210
{
  omlog() << "[STLReader] : read ascii file\n";

211
212
  std::fstream in( _filename.c_str(), std::ios_base::in );

Jan Möbius's avatar
Jan Möbius committed
213
214
  if (!in)
  {
215
    omerr() << "[STLReader] : cannot not open file "
Jan Möbius's avatar
Jan Möbius committed
216
217
218
219
220
	  << _filename
	  << std::endl;
    return false;
  }

221
  bool res = read_stla(in, _bi, _opt);
Jan Möbius's avatar
Jan Möbius committed
222

223
  if (in)
224
    in.close();
Jan Möbius's avatar
Jan Möbius committed
225

226
  return res;
Jan Möbius's avatar
Jan Möbius committed
227
228
229
230
}

//-----------------------------------------------------------------------------

231
232
bool
_STLReader_::
233
read_stla(std::istream& _in, BaseImporter& _bi, Options& _opt) const
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
{
  omlog() << "[STLReader] : read ascii stream\n";


  unsigned int               i;
  OpenMesh::Vec3f            v;
  OpenMesh::Vec3f            n;
  BaseImporter::VHandles     vhandles;

  CmpVec comp(eps_);
  std::map<Vec3f, VertexHandle, CmpVec>            vMap(comp);
  std::map<Vec3f, VertexHandle, CmpVec>::iterator  vMapIt;

  std::string line;

249
  bool facet_normal(false);
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278

  while( _in && !_in.eof() ) {

    // Get one line
    std::getline(_in, line);
    if ( _in.bad() ){
      omerr() << "  Warning! Could not read stream properly!\n";
      return false;
    }

    // Trim Both leading and trailing spaces
    trimStdString(line);

    // Normal found?
    if (line.find("facet normal") != std::string::npos) {
      std::stringstream strstream(line);

      std::string garbage;

      // facet
      strstream >> garbage;

      // normal
      strstream >> garbage;

      strstream >> n[0];
      strstream >> n[1];
      strstream >> n[2];

279
      facet_normal = true;
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
    }

    // Detected a triangle
    if ( (line.find("outer") != std::string::npos) ||  (line.find("OUTER") != std::string::npos ) ) {

      vhandles.clear();

      for (i=0; i<3; ++i) {
        // Get one vertex
        std::getline(_in, line);
        trimStdString(line);

        std::stringstream strstream(line);

        std::string garbage;
        strstream >> garbage;

        strstream >> v[0];
        strstream >> v[1];
        strstream >> v[2];

        // has vector been referenced before?
        if ((vMapIt=vMap.find(v)) == vMap.end())
        {
          // No : add vertex and remember idx/vector mapping
305
306
307
          VertexHandle handle = _bi.add_vertex(v);
          vhandles.push_back(handle);
          vMap[v] = handle;
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
        }
        else
          // Yes : get index from map
          vhandles.push_back(vMapIt->second);

      }

      // Add face only if it is not degenerated
      if ((vhandles[0] != vhandles[1]) &&
          (vhandles[0] != vhandles[2]) &&
          (vhandles[1] != vhandles[2])) {


        FaceHandle fh = _bi.add_face(vhandles);

323
324
325
        // set the normal if requested
        // if a normal was requested but could not be found we unset the option
        if (facet_normal) {
326
          if (fh.is_valid() && _opt.face_has_normal())
327
328
329
            _bi.set_normal(fh, n);
        } else
          _opt -= Options::FaceNormal;
330
331
      }

332
      facet_normal = false;
333
334
335
336
337
338
339
    }
  }

  return true;
}

//-----------------------------------------------------------------------------
Jan Möbius's avatar
Jan Möbius committed
340

341
bool
Jan Möbius's avatar
Jan Möbius committed
342
_STLReader_::
343
read_stlb(const std::string& _filename, BaseImporter& _bi, Options& _opt) const
Jan Möbius's avatar
Jan Möbius committed
344
345
346
{
  omlog() << "[STLReader] : read binary file\n";

347
348
  std::fstream in( _filename.c_str(), std::ios_base::in | std::ios_base::binary);

Jan Möbius's avatar
Jan Möbius committed
349
350
  if (!in)
  {
351
    omerr() << "[STLReader] : cannot not open file "
Jan Möbius's avatar
Jan Möbius committed
352
353
354
355
356
	  << _filename
	  << std::endl;
    return false;
  }

357
  bool res = read_stlb(in, _bi, _opt);
Jan Möbius's avatar
Jan Möbius committed
358

359
360
  if (in)
    in.close();
361

362
  return res;
Jan Möbius's avatar
Jan Möbius committed
363
364
365
366
}

//-----------------------------------------------------------------------------

367
368
bool
_STLReader_::
369
read_stlb(std::istream& _in, BaseImporter& _bi, Options& _opt) const
370
371
372
373
374
375
{
  omlog() << "[STLReader] : read binary stream\n";

  char                       dummy[100];
  bool                       swapFlag;
  unsigned int               i, nT;
376
  OpenMesh::Vec3f            v, n;
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
  BaseImporter::VHandles     vhandles;

  std::map<Vec3f, VertexHandle, CmpVec>  vMap;
  std::map<Vec3f, VertexHandle, CmpVec>::iterator vMapIt;


  // check size of types
  if ((sizeof(float) != 4) || (sizeof(int) != 4)) {
    omerr() << "[STLReader] : wrong type size\n";
    return false;
  }

  // determine endian mode
  union { unsigned int i; unsigned char c[4]; } endian_test;
  endian_test.i = 1;
  swapFlag = (endian_test.c[3] == 1);

  // read number of triangles
  _in.read(dummy, 80);
  nT = read_int(_in, swapFlag);

  // read triangles
  while (nT)
  {
    vhandles.clear();

403
404
405
406
    // read triangle normal
    n[0] = read_float(_in, swapFlag);
    n[1] = read_float(_in, swapFlag);
    n[2] = read_float(_in, swapFlag);
407
408
409
410
411
412
413
414
415
416
417

    // triangle's vertices
    for (i=0; i<3; ++i)
    {
      v[0] = read_float(_in, swapFlag);
      v[1] = read_float(_in, swapFlag);
      v[2] = read_float(_in, swapFlag);

      // has vector been referenced before?
      if ((vMapIt=vMap.find(v)) == vMap.end())
      {
418
419
420
421
        // No : add vertex and remember idx/vector mapping
        VertexHandle handle = _bi.add_vertex(v);
        vhandles.push_back(handle);
        vMap[v] = handle;
422
423
      }
      else
424
425
        // Yes : get index from map
        vhandles.push_back(vMapIt->second);
426
427
428
429
430
431
    }


    // Add face only if it is not degenerated
    if ((vhandles[0] != vhandles[1]) &&
	(vhandles[0] != vhandles[2]) &&
432
433
434
	(vhandles[1] != vhandles[2])) {
      FaceHandle fh = _bi.add_face(vhandles);

435
      if (fh.is_valid() && _opt.face_has_normal())
436
437
        _bi.set_normal(fh, n);
    }
438
439
440
441
442
443
444
445
446

    _in.read(dummy, 2);
    --nT;
  }

  return true;
}

//-----------------------------------------------------------------------------
Jan Möbius's avatar
Jan Möbius committed
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465

_STLReader_::STL_Type
_STLReader_::
check_stl_type(const std::string& _filename) const
{
  // assume it's binary stl, then file size is known from #triangles
  // if size matches, it's really binary


  // open file
  FILE* in = fopen(_filename.c_str(), "rb");
  if (!in) return NONE;


  // determine endian mode
  union { unsigned int i; unsigned char c[4]; } endian_test;
  endian_test.i = 1;
  bool swapFlag = (endian_test.c[3] == 1);

466

Jan Möbius's avatar
Jan Möbius committed
467
468
469
  // read number of triangles
  char dummy[100];
  fread(dummy, 1, 80, in);
Jan Möbius's avatar
Jan Möbius committed
470
  size_t nT = read_int(in, swapFlag);
Jan Möbius's avatar
Jan Möbius committed
471
472
473


  // compute file size from nT
Jan Möbius's avatar
Jan Möbius committed
474
  size_t binary_size = 84 + nT*50;
Jan Möbius's avatar
Jan Möbius committed
475
476
477


  // get actual file size
Jan Möbius's avatar
Jan Möbius committed
478
  size_t file_size(0);
Jan Möbius's avatar
Jan Möbius committed
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  rewind(in);
  while (!feof(in))
    file_size += fread(dummy, 1, 100, in);
  fclose(in);


  // if sizes match -> it's STLB
  return (binary_size == file_size ? STLB : STLA);
}


//=============================================================================
} // namespace IO
} // namespace OpenMesh
//=============================================================================