OMReader.cc 16.8 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-2012 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 50 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
 *                                                                           *
\*===========================================================================*/


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


//STL
#include <fstream>

// OpenMesh
#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/System/omstream.hh>
#include <OpenMesh/Core/Utils/Endian.hh>
#include <OpenMesh/Core/IO/OMFormat.hh>
#include <OpenMesh/Core/IO/reader/OMReader.hh>


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


namespace OpenMesh {
namespace IO {


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


// register the OMReader singleton with MeshReader
_OMReader_  __OMReaderInstance;
_OMReader_&  OMReader() { return __OMReaderInstance; }



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


_OMReader_::_OMReader_()
{
78
  IOManager().register_module(this);
Jan Möbius's avatar
Jan Möbius committed
79 80 81 82 83 84
}


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


85
bool _OMReader_::read(const std::string& _filename, BaseImporter& _bi, Options& _opt)
Jan Möbius's avatar
Jan Möbius committed
86 87
{
  // check whether importer can give us an OpenMesh BaseKernel
88 89
  if (!_bi.kernel())
    return false;
Jan Möbius's avatar
Jan Möbius committed
90 91

  _opt += Options::Binary; // only binary format supported!
92
  fileOptions_ = Options::Binary;
Jan Möbius's avatar
Jan Möbius committed
93 94

  // Open file
95 96 97
  std::ifstream ifs(_filename.c_str(), std::ios::binary);
  if (!ifs.is_open() || !ifs.good()) {
    omerr() << "[OMReader] : cannot not open file " << _filename << std::endl;
Jan Möbius's avatar
Jan Möbius committed
98 99
    return false;
  }
100

Jan Möbius's avatar
Jan Möbius committed
101 102
  // Pass stream to read method, remember result
  bool result = read(ifs, _bi, _opt);
103

Jan Möbius's avatar
Jan Möbius committed
104 105
  // close input stream
  ifs.close();
106

107 108
  _opt = _opt & fileOptions_;

Jan Möbius's avatar
Jan Möbius committed
109 110 111 112 113 114
  return result;
}

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


115
bool _OMReader_::read(std::istream& _is, BaseImporter& _bi, Options& _opt)
Jan Möbius's avatar
Jan Möbius committed
116
{
117
  // check whether importer can give us an OpenMesh BaseKernel
118 119
  if (!_bi.kernel())
    return false;
120 121

  _opt += Options::Binary; // only binary format supported!
122
  fileOptions_ = Options::Binary;
123

124 125
  if (!_is.good()) {
    omerr() << "[OMReader] : cannot read from stream " << std::endl;
126 127 128 129 130 131
    return false;
  }

  // Pass stream to read method, remember result
  bool result = read_binary(_is, _bi, _opt);

132 133
  if (result)
    _opt += Options::Binary;
Jan Möbius's avatar
Jan Möbius committed
134

135 136
  _opt = _opt & fileOptions_;

137
  return result;
Jan Möbius's avatar
Jan Möbius committed
138 139 140 141 142 143
}



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

144
bool _OMReader_::read_ascii(std::istream& /* _is */, BaseImporter& /* _bi */, Options& /* _opt */) const
Jan Möbius's avatar
Jan Möbius committed
145 146 147 148 149 150 151 152
{
  // not supported yet!
  return false;
}


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

153
bool _OMReader_::read_binary(std::istream& _is, BaseImporter& _bi, Options& _opt) const
Jan Möbius's avatar
Jan Möbius committed
154 155 156
{
  bool swap = _opt.check(Options::Swap) || (Endian::local() == Endian::MSB);

157
  // Initialize byte counter
158
  bytes_ = 0;
Jan Möbius's avatar
Jan Möbius committed
159

160
  bytes_ += restore(_is, header_, swap);
Jan Möbius's avatar
Jan Möbius committed
161 162 163

  size_t data_bytes;

164 165
  while (!_is.eof()) {
    bytes_ += restore(_is, chunk_header_, swap);
Jan Möbius's avatar
Jan Möbius committed
166

167
    if (_is.eof())
Jan Möbius's avatar
Jan Möbius committed
168 169 170
      break;

    // Is this a named property restore the name
171
    if (chunk_header_.name_) {
Jan Möbius's avatar
Jan Möbius committed
172
      OMFormat::Chunk::PropertyName pn;
173
      bytes_ += restore(_is, property_name_, swap);
Jan Möbius's avatar
Jan Möbius committed
174 175 176 177 178
    }

    // Read in the property data. If it is an anonymous or unknown named
    // property, then skip data.
    data_bytes = bytes_;
179
    switch (chunk_header_.entity_) {
180
      case OMFormat::Chunk::Entity_Vertex:
181 182 183
        if (!read_binary_vertex_chunk(_is, _bi, _opt, swap))
          return false;
        break;
Jan Möbius's avatar
Jan Möbius committed
184
      case OMFormat::Chunk::Entity_Face:
185 186 187
        if (!read_binary_face_chunk(_is, _bi, _opt, swap))
          return false;
        break;
Jan Möbius's avatar
Jan Möbius committed
188
      case OMFormat::Chunk::Entity_Edge:
189 190 191
        if (!read_binary_edge_chunk(_is, _bi, _opt, swap))
          return false;
        break;
Jan Möbius's avatar
Jan Möbius committed
192
      case OMFormat::Chunk::Entity_Halfedge:
193 194 195
        if (!read_binary_halfedge_chunk(_is, _bi, _opt, swap))
          return false;
        break;
Jan Möbius's avatar
Jan Möbius committed
196
      case OMFormat::Chunk::Entity_Mesh:
197 198 199
        if (!read_binary_mesh_chunk(_is, _bi, _opt, swap))
          return false;
        break;
Jan Möbius's avatar
Jan Möbius committed
200
      default:
201
        return false;
Jan Möbius's avatar
Jan Möbius committed
202 203 204 205 206 207 208 209 210 211 212
    }
    data_bytes = bytes_ - data_bytes;
  }

  // File was successfully parsed.
  return true;
}


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

213
bool _OMReader_::can_u_read(const std::string& _filename) const
Jan Möbius's avatar
Jan Möbius committed
214 215 216
{
  // !!! Assuming BaseReader::can_u_parse( std::string& )
  // does not call BaseReader::read_magic()!!!
217 218 219
  if (this->BaseReader::can_u_read(_filename)) {
    std::ifstream ifile(_filename.c_str());
    if (ifile && can_u_read(ifile))
220
      return true;
Jan Möbius's avatar
Jan Möbius committed
221 222 223 224 225
  }
  return false;
}

//-----------------------------------------------------------------------------
226

227
bool _OMReader_::can_u_read(std::istream& _is) const
Jan Möbius's avatar
Jan Möbius committed
228 229 230 231 232
{
  std::vector<char> evt;
  evt.reserve(20);

  // read first 4 characters into a buffer
233 234
  while (evt.size() < 4)
    evt.push_back(static_cast<char>(_is.get()));
Jan Möbius's avatar
Jan Möbius committed
235

236
  // put back all read characters
Jan Möbius's avatar
Jan Möbius committed
237
  std::vector<char>::reverse_iterator it = evt.rbegin();
238 239
  while (it != evt.rend())
    _is.putback(*it++);
Jan Möbius's avatar
Jan Möbius committed
240 241

  // evaluate header information
242
  OMFormat::Header *hdr = (OMFormat::Header*) &evt[0];
Jan Möbius's avatar
Jan Möbius committed
243 244 245 246 247 248

  // first two characters must be 'OM'
  if (hdr->magic_[0] != 'O' || hdr->magic_[1] != 'M')
    return false;

  // 3rd characters defines the mesh type:
249
  switch (hdr->mesh_) {
Jan Möbius's avatar
Jan Möbius committed
250 251 252 253 254 255 256
    case 'T': // Triangle Mesh
    case 'Q': // Quad Mesh
    case 'P': // Polygonal Mesh
      break;
    default:  // ?
      return false;
  }
257

Jan Möbius's avatar
Jan Möbius committed
258
  // 4th characters encodes the version
259
  return supports(hdr->version_);
Jan Möbius's avatar
Jan Möbius committed
260 261 262 263
}

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

264
bool _OMReader_::supports(const OMFormat::uint8 /* version */) const
Jan Möbius's avatar
Jan Möbius committed
265 266 267 268 269 270 271
{
  return true;
}


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

272
bool _OMReader_::read_binary_vertex_chunk(std::istream &_is, BaseImporter &_bi, Options &_opt, bool _swap) const
Jan Möbius's avatar
Jan Möbius committed
273 274 275
{
  using OMFormat::Chunk;

276
  assert( chunk_header_.entity_ == Chunk::Entity_Vertex);
277

278 279
  OpenMesh::Vec3f v3f;
  OpenMesh::Vec2f v2f;
Jan Möbius's avatar
Jan Möbius committed
280 281 282 283
  OpenMesh::Vec3uc v3uc; // rgb

  OMFormat::Chunk::PropertyName custom_prop;

284 285
  size_t vidx = 0;
  switch (chunk_header_.type_) {
Jan Möbius's avatar
Jan Möbius committed
286
    case Chunk::Type_Pos:
287
      assert( OMFormat::dimensions(chunk_header_) == size_t(OpenMesh::Vec3f::dim()));
Jan Möbius's avatar
Jan Möbius committed
288

289 290 291
      for (; vidx < header_.n_vertices_ && !_is.eof(); ++vidx) {
        bytes_ += vector_restore(_is, v3f, _swap);
        _bi.add_vertex(v3f);
Jan Möbius's avatar
Jan Möbius committed
292 293 294 295
      }
      break;

    case Chunk::Type_Normal:
296
      assert( OMFormat::dimensions(chunk_header_) == size_t(OpenMesh::Vec3f::dim()));
Jan Möbius's avatar
Jan Möbius committed
297

298
      fileOptions_ += Options::VertexNormal;
299 300
      for (; vidx < header_.n_vertices_ && !_is.eof(); ++vidx) {
        bytes_ += vector_restore(_is, v3f, _swap);
301
        if (fileOptions_.vertex_has_normal() && _opt.vertex_has_normal())
302
          _bi.set_normal(VertexHandle(int(vidx)), v3f);
Jan Möbius's avatar
Jan Möbius committed
303 304 305 306
      }
      break;

    case Chunk::Type_Texcoord:
307
      assert( OMFormat::dimensions(chunk_header_) == size_t(OpenMesh::Vec2f::dim()));
Jan Möbius's avatar
Jan Möbius committed
308

309
      fileOptions_ += Options::VertexTexCoord;
310 311
      for (; vidx < header_.n_vertices_ && !_is.eof(); ++vidx) {
        bytes_ += vector_restore(_is, v2f, _swap);
312
        if (fileOptions_.vertex_has_texcoord() && _opt.vertex_has_texcoord())
313
          _bi.set_texcoord(VertexHandle(int(vidx)), v2f);
Jan Möbius's avatar
Jan Möbius committed
314
      }
315
      break;
Jan Möbius's avatar
Jan Möbius committed
316 317 318

    case Chunk::Type_Color:

319
      assert( OMFormat::dimensions(chunk_header_) == 3);
Jan Möbius's avatar
Jan Möbius committed
320

321
      fileOptions_ += Options::VertexColor;
322

323 324
      for (; vidx < header_.n_vertices_ && !_is.eof(); ++vidx) {
        bytes_ += vector_restore(_is, v3uc, _swap);
325
        if (fileOptions_.vertex_has_color() && _opt.vertex_has_color())
326
          _bi.set_color(VertexHandle(int(vidx)), v3uc);
Jan Möbius's avatar
Jan Möbius committed
327 328 329 330
      }
      break;

    case Chunk::Type_Custom:
331

332
      bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_vprop(property_name_), header_.n_vertices_, _swap);
Jan Möbius's avatar
Jan Möbius committed
333 334 335 336 337 338 339 340

      vidx = header_.n_vertices_;

      break;

    default: // skip unknown chunks
    {
      omerr() << "Unknown chunk type ignored!\n";
341 342
      size_t size_of = header_.n_vertices_ * OMFormat::vector_size(chunk_header_);
      _is.ignore(size_of);
Jan Möbius's avatar
Jan Möbius committed
343 344 345 346 347 348 349 350 351 352 353
      bytes_ += size_of;
    }
  }

  // all chunk data has been read..?!
  return vidx == header_.n_vertices_;
}


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

354
bool _OMReader_::read_binary_face_chunk(std::istream &_is, BaseImporter &_bi, Options &_opt, bool _swap) const
Jan Möbius's avatar
Jan Möbius committed
355 356 357
{
  using OMFormat::Chunk;

358
  assert( chunk_header_.entity_ == Chunk::Entity_Face);
Jan Möbius's avatar
Jan Möbius committed
359

360 361
  size_t fidx = 0;
  OpenMesh::Vec3f v3f;  // normal
Jan Möbius's avatar
Jan Möbius committed
362 363
  OpenMesh::Vec3uc v3uc; // rgb

364 365 366 367 368 369 370 371 372 373 374 375 376
  switch (chunk_header_.type_) {
    case Chunk::Type_Topology: {
      BaseImporter::VHandles vhandles;
      size_t nV = 0;
      size_t vidx = 0;

      switch (header_.mesh_) {
        case 'T':
          nV = 3;
          break;
        case 'Q':
          nV = 4;
          break;
Jan Möbius's avatar
Jan Möbius committed
377
      }
378

379 380 381
      for (; fidx < header_.n_faces_; ++fidx) {
        if (header_.mesh_ == 'P')
          bytes_ += restore(_is, nV, Chunk::Integer_16, _swap);
Jan Möbius's avatar
Jan Möbius committed
382

383 384 385
        vhandles.clear();
        for (size_t j = 0; j < nV; ++j) {
          bytes_ += restore(_is, vidx, Chunk::Integer_Size(chunk_header_.bits_), _swap);
Jan Möbius's avatar
Jan Möbius committed
386

387
          vhandles.push_back(VertexHandle(int(vidx)));
388
        }
Jan Möbius's avatar
Jan Möbius committed
389

390
        _bi.add_face(vhandles);
Jan Möbius's avatar
Jan Möbius committed
391 392
      }
    }
393
      break;
Jan Möbius's avatar
Jan Möbius committed
394 395

    case Chunk::Type_Normal:
396
      assert( OMFormat::dimensions(chunk_header_) == size_t(OpenMesh::Vec3f::dim()));
Jan Möbius's avatar
Jan Möbius committed
397

398
      fileOptions_ += Options::FaceNormal;
399 400
      for (; fidx < header_.n_faces_ && !_is.eof(); ++fidx) {
        bytes_ += vector_restore(_is, v3f, _swap);
401
        if( fileOptions_.face_has_normal() && _opt.face_has_normal())
402
          _bi.set_normal(FaceHandle(int(fidx)), v3f);
Jan Möbius's avatar
Jan Möbius committed
403 404 405 406 407
      }
      break;

    case Chunk::Type_Color:

408
      assert( OMFormat::dimensions(chunk_header_) == 3);
Jan Möbius's avatar
Jan Möbius committed
409

410
      fileOptions_ += Options::FaceColor;
411 412
      for (; fidx < header_.n_faces_ && !_is.eof(); ++fidx) {
        bytes_ += vector_restore(_is, v3uc, _swap);
413
        if( fileOptions_.face_has_color() && _opt.face_has_color())
414
          _bi.set_color(FaceHandle(int(fidx)), v3uc);
Jan Möbius's avatar
Jan Möbius committed
415 416 417 418
      }
      break;

    case Chunk::Type_Custom:
419

420
      bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_fprop(property_name_), header_.n_faces_, _swap);
Jan Möbius's avatar
Jan Möbius committed
421 422 423 424

      fidx = header_.n_faces_;

      break;
425

Jan Möbius's avatar
Jan Möbius committed
426 427 428 429
    default: // skip unknown chunks
    {
      omerr() << "Unknown chunk type ignore!\n";
      size_t size_of = OMFormat::chunk_data_size(header_, chunk_header_);
430
      _is.ignore(size_of);
431
      bytes_ += size_of;
Jan Möbius's avatar
Jan Möbius committed
432 433 434 435 436 437 438 439
    }
  }
  return fidx == header_.n_faces_;
}


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

440
bool _OMReader_::read_binary_edge_chunk(std::istream &_is, BaseImporter &_bi, Options &/*_opt */, bool _swap) const
Jan Möbius's avatar
Jan Möbius committed
441 442 443
{
  using OMFormat::Chunk;

444
  assert( chunk_header_.entity_ == Chunk::Entity_Edge);
Jan Möbius's avatar
Jan Möbius committed
445

446
  size_t b = bytes_;
Jan Möbius's avatar
Jan Möbius committed
447

448
  switch (chunk_header_.type_) {
449
    case Chunk::Type_Custom:
Jan Möbius's avatar
Jan Möbius committed
450

451
      bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_eprop(property_name_), header_.n_edges_, _swap);
Jan Möbius's avatar
Jan Möbius committed
452 453 454 455 456 457

      break;

    default:
      // skip unknown type
      size_t size_of = OMFormat::chunk_data_size(header_, chunk_header_);
458
      _is.ignore(size_of);
Jan Möbius's avatar
Jan Möbius committed
459 460 461 462 463 464 465 466 467
      bytes_ += size_of;
  }

  return b < bytes_;
}


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

468
bool _OMReader_::read_binary_halfedge_chunk(std::istream &_is, BaseImporter &_bi, Options &/* _opt */, bool _swap) const
Jan Möbius's avatar
Jan Möbius committed
469 470 471
{
  using OMFormat::Chunk;

472
  assert( chunk_header_.entity_ == Chunk::Entity_Halfedge);
Jan Möbius's avatar
Jan Möbius committed
473 474 475

  size_t b = bytes_;

476
  switch (chunk_header_.type_) {
Jan Möbius's avatar
Jan Möbius committed
477 478
    case Chunk::Type_Custom:

479
      bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_hprop(property_name_), 2 * header_.n_edges_, _swap);
Jan Möbius's avatar
Jan Möbius committed
480 481 482 483 484 485
      break;

    default:
      // skip unknown chunk
      omerr() << "Unknown chunk type ignored!\n";
      size_t size_of = OMFormat::chunk_data_size(header_, chunk_header_);
486
      _is.ignore(size_of);
Jan Möbius's avatar
Jan Möbius committed
487 488 489 490 491 492 493 494 495
      bytes_ += size_of;
  }

  return b < bytes_;
}


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

496
bool _OMReader_::read_binary_mesh_chunk(std::istream &_is, BaseImporter &_bi, Options & /* _opt */, bool _swap) const
Jan Möbius's avatar
Jan Möbius committed
497 498 499
{
  using OMFormat::Chunk;

500
  assert( chunk_header_.entity_ == Chunk::Entity_Mesh);
Jan Möbius's avatar
Jan Möbius committed
501 502 503

  size_t b = bytes_;

504
  switch (chunk_header_.type_) {
Jan Möbius's avatar
Jan Möbius committed
505
    case Chunk::Type_Custom:
506

507
      bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_mprop(property_name_), 1, _swap);
508

Jan Möbius's avatar
Jan Möbius committed
509 510 511 512 513
      break;

    default:
      // skip unknown chunk
      size_t size_of = OMFormat::chunk_data_size(header_, chunk_header_);
514
      _is.ignore(size_of);
Jan Möbius's avatar
Jan Möbius committed
515 516 517 518 519 520 521 522 523 524
      bytes_ += size_of;
  }

  return b < bytes_;
}


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


525
size_t _OMReader_::restore_binary_custom_data(std::istream& _is, BaseProperty* _bp, size_t _n_elem, bool _swap) const
526
{
527
  assert( !_bp || (_bp->name() == property_name_));
Jan Möbius's avatar
Jan Möbius committed
528 529 530

  using OMFormat::Chunk;

531 532
  size_t bytes = 0;
  Chunk::esize_t block_size;
Jan Möbius's avatar
Jan Möbius committed
533 534
  Chunk::PropertyName custom_prop;

535
  bytes += restore(_is, block_size, OMFormat::Chunk::Integer_32, _swap);
Jan Möbius's avatar
Jan Möbius committed
536

537 538
  if (_bp) {
    size_t n_bytes = _bp->size_of(_n_elem);
Jan Möbius's avatar
Jan Möbius committed
539

540 541
    if (((n_bytes == BaseProperty::UnknownSize) || (n_bytes == block_size))
        && (_bp->element_size() == BaseProperty::UnknownSize || (_n_elem * _bp->element_size() == block_size))) {
Jan Möbius's avatar
Jan Möbius committed
542
#if defined(OM_DEBUG)
Jan Möbius's avatar
Jan Möbius committed
543
      size_t b;
Jan Möbius's avatar
Jan Möbius committed
544 545
      bytes += (b=_bp->restore( _is, _swap ));
#else
546
      bytes += _bp->restore(_is, _swap);
Jan Möbius's avatar
Jan Möbius committed
547
#endif
548

Jan Möbius's avatar
Jan Möbius committed
549 550 551
#if defined(OM_DEBUG)
      assert( block_size == b );
#endif
552 553

      assert( block_size == _bp->size_of());
Jan Möbius's avatar
Jan Möbius committed
554 555

      block_size = 0;
556 557
    } else {
      omerr() << "Warning! Property " << _bp->name() << " not loaded: " << "Mismatching data sizes!n";
Jan Möbius's avatar
Jan Möbius committed
558 559 560
    }
  }

561 562 563
  if (block_size) {
    _is.ignore(block_size);
    bytes += block_size;
Jan Möbius's avatar
Jan Möbius committed
564 565 566 567 568 569 570 571 572 573 574 575
  }

  return bytes;
}


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

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