Developer Documentation
FileOFF.cc
1 /*===========================================================================*\
2 * *
3 * OpenFlipper *
4  * Copyright (c) 2001-2015, RWTH-Aachen University *
5  * Department of Computer Graphics and Multimedia *
6  * All rights reserved. *
7  * www.openflipper.org *
8  * *
9  *---------------------------------------------------------------------------*
10  * This file is part of OpenFlipper. *
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 #include "FileOFF.hh"
45 
46 
48 #include <OpenFlipper/Utils/Memory/RAMInfo.hh>
49 
50 #include <QMessageBox>
51 #include <QVBoxLayout>
52 
53 // Defines for the type handling drop down box
54 #define TYPEAUTODETECT 0
55 #define TYPEASK 1
56 #define TYPEPOLY 2
57 #define TYPETRIANGLE 3
58 
59 using namespace Utils;
60 
61 //-----------------------------------------------------------------------------
62 // help functions
63 
66 : loadOptions_(0),
67  saveOptions_(0),
68  saveBinary_(0),
69  saveVertexColor_(0),
70  saveFaceColor_(0),
71  saveAlpha_(0),
72  saveNormals_(0),
73  saveTexCoords_(0),
74  savePrecisionLabel_(0),
75  savePrecision_(0),
76  saveDefaultButton_(0),
77  triMeshHandling_(0),
78  loadVertexColor_(0),
79  loadFaceColor_(0),
80  loadAlpha_(0),
81  loadNormals_(0),
82  loadTexCoords_(0),
83  loadCheckManifold_(0),
84  loadDefaultButton_(0),
85  userReadOptions_(0),
86  userWriteOptions_(0),
87  forceTriangleMesh_(false),
88  forcePolyMesh_(false),
89  readColorComp_(false),
90  trimeshOptions_(OFFImporter::NONE)
91 {
92 }
93 
94 //-----------------------------------------------------------------------------------------------------
95 
97 
98  // Initialize standard options that can then be changed in the file dialogs
99  if(OpenFlipperSettings().value("FileOff/Load/VertexColor",true).toBool())
100  userReadOptions_ |= OFFImporter::VERTEXCOLOR;
101  if(OpenFlipperSettings().value("FileOff/Load/FaceColor",true).toBool())
102  userReadOptions_ |= OFFImporter::FACECOLOR;
103  if(OpenFlipperSettings().value("FileOff/Load/Alpha",true).toBool())
104  userReadOptions_ |= OFFImporter::COLORALPHA;
105  if(OpenFlipperSettings().value("FileOff/Load/Normal",true).toBool())
106  userReadOptions_ |= OFFImporter::VERTEXNORMAL;
107  if(OpenFlipperSettings().value("FileOff/Load/TexCoords",true).toBool())
108  userReadOptions_ |= OFFImporter::VERTEXTEXCOORDS;
109 
110  if(OpenFlipperSettings().value("FileOff/Save/Binary",true).toBool())
111  userWriteOptions_ |= OFFImporter::BINARY;
112  if(OpenFlipperSettings().value("FileOff/Save/VertexColor",true).toBool())
113  userWriteOptions_ |= OFFImporter::VERTEXCOLOR;
114  if(OpenFlipperSettings().value("FileOff/Save/FaceColor",true).toBool())
115  userWriteOptions_ |= OFFImporter::FACECOLOR;
116  if(OpenFlipperSettings().value("FileOff/Save/Alpha",true).toBool())
117  userWriteOptions_ |= OFFImporter::COLORALPHA;
118  if(OpenFlipperSettings().value("FileOff/Save/Normal",true).toBool())
119  userWriteOptions_ |= OFFImporter::VERTEXNORMAL;
120  if(OpenFlipperSettings().value("FileOff/Save/TexCoords",true).toBool())
121  userWriteOptions_ |= OFFImporter::VERTEXTEXCOORDS;
122 
123 }
124 
125 //-----------------------------------------------------------------------------------------------------
126 
128  return QString( tr("Object File Format files ( *.off )") );
129 };
130 
131 //-----------------------------------------------------------------------------------------------------
132 
134  return QString( tr("Object File Format files ( *.off )") );
135 };
136 
137 //-----------------------------------------------------------------------------------------------------
138 
141  return type;
142 }
143 
144 //-----------------------------------------------------------------------------------------------------
145 
146 void FileOFFPlugin::trimString( std::string& _string) {
147  // Trim Both leading and trailing spaces
148 
149  size_t start = _string.find_first_not_of(" \t\r\n");
150  size_t end = _string.find_last_not_of(" \t\r\n");
151 
152  if(( std::string::npos == start ) || ( std::string::npos == end))
153  _string = "";
154  else
155  _string = _string.substr( start, end-start+1 );
156 }
157 
158 //-----------------------------------------------------------------------------------------------------
159 
160 bool FileOFFPlugin::getCleanLine( std::istream& ifs , std::string& _string, bool _skipEmptyLines) {
161 
162  // while we are not at the end of the file
163  while (true) {
164 
165  // get the current line:
166  std::getline(ifs,_string);
167 
168  // Remove whitespace at beginning and end
169  trimString(_string);
170 
171  // Check if string is not empty ( otherwise we continue
172  if ( _string.size() != 0 ) {
173 
174  // Check if string is a comment ( starting with # )
175  if ( _string[0] != '#') {
176  return true;
177  }
178 
179  } else {
180  if ( !_skipEmptyLines )
181  return true;
182  }
183 
184  if ( ifs.eof() ) {
185  std::cerr << "End of file reached while searching for input!" << std::endl;
186  return false;
187  }
188 
189  }
190 
191  return false;
192 
193 }
194 
195 //-----------------------------------------------------------------------------------------------------
196 
198 
199  // If the options dialog has not been initialized, keep
200  // the initial values
201 
202  if( OpenFlipper::Options::nogui() )
203  return;
204 
205  // Load options
206  if(loadVertexColor_) {
207  if(loadVertexColor_->isChecked()) userReadOptions_ |= OFFImporter::VERTEXCOLOR;
208  else { if(userReadOptions_ & OFFImporter::VERTEXCOLOR) userReadOptions_ -= OFFImporter::VERTEXCOLOR; }
209  }
210  if(loadFaceColor_) {
211  if(loadFaceColor_->isChecked()) userReadOptions_ |= OFFImporter::FACECOLOR;
212  else { if(userReadOptions_ & OFFImporter::FACECOLOR) userReadOptions_ -= OFFImporter::FACECOLOR; }
213  }
214  if(loadAlpha_) {
215  if(loadAlpha_->isChecked()) userReadOptions_ |= OFFImporter::COLORALPHA;
216  else { if(userReadOptions_ & OFFImporter::COLORALPHA) userReadOptions_ -= OFFImporter::COLORALPHA; }
217  }
218  if(loadNormals_) {
219  if(loadNormals_->isChecked()) userReadOptions_ |= OFFImporter::VERTEXNORMAL;
220  else { if(userReadOptions_ & OFFImporter::VERTEXNORMAL) userReadOptions_ -= OFFImporter::VERTEXNORMAL; }
221  }
222  if(loadTexCoords_) {
223  if(loadTexCoords_->isChecked()) userReadOptions_ |= OFFImporter::VERTEXTEXCOORDS;
224  else { if(userReadOptions_ & OFFImporter::VERTEXTEXCOORDS) userReadOptions_ -= OFFImporter::VERTEXTEXCOORDS; }
225  }
226 
227  // Save options
228  if(saveBinary_) {
229  if(saveBinary_->isChecked()) userWriteOptions_ |= OFFImporter::BINARY;
230  else { if(userWriteOptions_ & OFFImporter::BINARY) userWriteOptions_ -= OFFImporter::BINARY; }
231  }
232  if(saveVertexColor_) {
233  if(saveVertexColor_->isChecked()) userWriteOptions_ |= OFFImporter::VERTEXCOLOR;
234  else { if(userWriteOptions_ & OFFImporter::VERTEXCOLOR) userWriteOptions_ -= OFFImporter::VERTEXCOLOR; }
235  }
236  if(saveFaceColor_) {
237  if(saveFaceColor_->isChecked()) userWriteOptions_ |= OFFImporter::FACECOLOR;
238  else { if(userWriteOptions_ & OFFImporter::FACECOLOR) userWriteOptions_ -= OFFImporter::FACECOLOR; }
239  }
240  if(saveAlpha_) {
241  if(saveAlpha_->isChecked()) userWriteOptions_ |= OFFImporter::COLORALPHA;
242  else { if(userWriteOptions_ & OFFImporter::COLORALPHA) userWriteOptions_ -= OFFImporter::COLORALPHA; }
243  }
244  if(saveNormals_) {
245  if(saveNormals_->isChecked()) userWriteOptions_ |= OFFImporter::VERTEXNORMAL;
246  else { if(userWriteOptions_ & OFFImporter::VERTEXNORMAL) userWriteOptions_ -= OFFImporter::VERTEXNORMAL; }
247  }
248  if(saveTexCoords_) {
249  if(saveTexCoords_->isChecked()) userWriteOptions_ |= OFFImporter::VERTEXTEXCOORDS;
250  else { if(userWriteOptions_ & OFFImporter::VERTEXTEXCOORDS) userWriteOptions_ -= OFFImporter::VERTEXTEXCOORDS; }
251  }
252 }
253 
254 //-----------------------------------------------------------------------------------------------------
255 
256 bool FileOFFPlugin::readFileOptions(QString _filename, OFFImporter& _importer) {
257 
258  /* Constitution of an OFF-file
259  ==================================================================
260  [ST] [C] [N] [4][n]OFF [BINARY] # comment
261  nV nF nE # number of vertices, faces and edges (edges are skipped)
262  v[0] v[1] v[2] [n[0] n[1] n[2]] [c[0] c[1] c[1]] [t[0] t[0]]
263  ...
264  faceValence vIdx[0] ... vIdx[faceValence-1] colorspec
265  ...
266  ==================================================================
267  */
268 
269  const unsigned int LINE_LEN = 4096;
270 
271 
272  std::ifstream ifs(_filename.toUtf8(), std::ios_base::binary);
273 
274  if ( (!ifs.is_open()) || (!ifs.good())) {
275 
276  emit log(LOGERR, tr("Error: Could not read file options of specified OFF-file! Aborting."));
277  return false;
278  }
279 
280  // read 1st line
281  char line[LINE_LEN], *p;
282  ifs.getline(line, LINE_LEN);
283  p = line;
284 
285  int remainingChars = ifs.gcount();
286 
287  // check header: [ST][C][N][4][n]OFF BINARY
288  while(remainingChars > 0) {
289 
290  if ( ( remainingChars > 1 ) && ( p[0] == 'S' && p[1] == 'T') ) {
291  _importer.addOption(OFFImporter::VERTEXTEXCOORDS);
292  p += 2;
293  remainingChars -= 2;
294  } else if ( ( remainingChars > 0 ) && ( p[0] == 'C') ) {
295  _importer.addOption(OFFImporter::VERTEXCOLOR);
296  ++p;
297  --remainingChars;
298  } else if ( ( remainingChars > 0 ) && ( p[0] == 'N') ) {
299  _importer.addOption(OFFImporter::VERTEXNORMAL);
300  ++p;
301  --remainingChars;
302  } else if ( ( remainingChars > 0 ) && (p[0] == '3' ) ) {
303  ++p;
304  --remainingChars;
305  } else if ( ( remainingChars > 0 ) && (p[0] == '4' ) ) {
307  std::cerr << "Error: Extended coordinates are currently not supported!" << std::endl;
308  ifs.close();
309  return false;
310  } else if ( ( remainingChars > 0 ) && (p[0] == 'n' ) ) {
312  std::cerr << "Error: n-dimensional coordinates are currently not supported!" << std::endl;
313  ifs.close();
314  return false;
315  } else if ( ( remainingChars >= 3 ) && (p[0] == 'O' && p[1] == 'F' && p[2] == 'F') ) {
316  // Skip "OFF " (plus space):
317  p += 4;
318  remainingChars -= 4;
319  } else if ( ( remainingChars >= 6 ) && ( strncmp(p, "BINARY", 6) == 0 ) ) {
320  _importer.addOption(OFFImporter::BINARY);
321  p += 6;
322  remainingChars -= 6;
323  } else if ( ( remainingChars > 0 ) && ( p[0] == '#' ) ) {
324  // Skip the rest of the line since it's a comment
325  remainingChars = 0;
326  } else {
327  // Skip unknown character or space
328  ++p;
329  --remainingChars;
330  }
331  }
332 
333  // Now extract data type by iterating over
334  // the face valences
335 
336  unsigned int nV, nF, dummy_uint;
337  unsigned int vertexCount = 0;
338  unsigned int tmp_count = 0;
339  std::string trash;
340  std::string str;
341  std::istringstream sstr;
342 
343  if(_importer.isBinary()) {
344  // Parse BINARY file
345  float dummy_f;
346 
347  // + #Vertices, #Faces, #Edges
348  readValue(ifs, nV);
349  readValue(ifs, nF);
350  readValue(ifs, dummy_uint);
351 
352  for (unsigned int i=0; i<nV && !ifs.eof(); ++i) {
353  // Skip vertices
354  for(int index = 0; index < 3; ++index) readValue(ifs, dummy_f);
355 
356  if ( _importer.hasVertexNormals() ) {
357  for(int index = 0; index < 3; ++index) readValue(ifs, dummy_f);
358  }
359 
360  if ( _importer.hasVertexColors() ) {
361  for(int index = 0; index < 3; ++index) readValue(ifs, dummy_f);
362  }
363 
364  if ( _importer.hasTextureCoords() ) {
365  for(int index = 0; index < 2; ++index) readValue(ifs, dummy_f);
366  }
367  }
368 
369  for (unsigned int i=0; i<nF; ++i) {
370  // Get valence of current face
371  readValue(ifs, tmp_count);
372 
373  if (ifs.eof())
374  break;
375 
376  if(tmp_count > vertexCount) vertexCount = tmp_count;
377 
378  // Skip the rest
379 
380  // Vertex indices
381  for(unsigned int count = 0; count < tmp_count; ++count) readValue(ifs, dummy_uint);
382 
383  // Get number of color components
384  readValue(ifs, tmp_count);
385 
386  if(!_importer.hasFaceColors() && tmp_count > 0) {
387  _importer.addOption(OFFImporter::FACECOLOR);
388  }
389 
390  // Face color
391  for (unsigned int count = 0; count < tmp_count; ++count) {
392  readValue(ifs, dummy_f);
393  }
394  }
395 
396  } else {
397  // Parse ASCII file
398 
399  // Get whole line since there could be comments in it
400  getCleanLine(ifs, str);
401  sstr.str(str);
402 
403  // check if #vertices, #faces and #edges follow
404  // on the next line
405  if ( str.compare("OFF") == 0 ) {
406  getCleanLine(ifs, str);
407  sstr.str(str);
408  }
409 
410  // + #Vertices, #Faces, #Edges
411  sstr >> nV;
412  sstr >> nF;
413  sstr >> dummy_uint;
414 
415  // Skip vertices
416  for(unsigned int i = 0; i < nV; ++i) {
417  getCleanLine(ifs, trash);
418  }
419 
420  trash = "";
421 
422  // Count vertices per face
423  for(unsigned int i = 0; i < nF; ++i) {
424  sstr.clear();
425  getCleanLine(ifs, trash);
426  sstr.str(trash);
427 
428  sstr >> tmp_count;
429 
430  if(tmp_count > vertexCount) vertexCount = tmp_count;
431 
432  // Skip vertex indices
433  for(unsigned int count = 0; count < tmp_count; ++count) {
434  if(sstr.eof())
435  {
436  emit log(LOGERR,"The OFF File is Malformatted! Aborting...");
437  return false;
438  }
439  sstr >> dummy_uint;
440  }
441 
442  // Look if there's at least one face color specified
443  // Note: Comments should not be here, so don't treat them
444  if(!_importer.hasFaceColors()) {
445  if(!sstr.eof()) {
446  _importer.addOption(OFFImporter::FACECOLOR);
447  }
448  }
449  }
450  }
451 
452  ifs.close();
453 
454  _importer.maxFaceValence(vertexCount);
455 
456  if(vertexCount == 3) {
457  _importer.addOption(OFFImporter::TRIMESH);
458  _importer.removeOption(OFFImporter::POLYMESH);
459  } else if (vertexCount == 0 && nF != 0) {
460  // Something went wrong
461  return false;
462  } else {
463  _importer.addOption(OFFImporter::POLYMESH);
464  _importer.removeOption(OFFImporter::TRIMESH);
465  }
466 
467  return true;
468 }
469 
470 //-----------------------------------------------------------------------------------------------------
471 
472 bool FileOFFPlugin::readOFFFile(QString _filename, OFFImporter& _importer) {
473  QFile theFile(_filename);
474  if ( !theFile.exists() ){
475  emit log(LOGERR, tr("Unable to load OFF file. File not found!"));
476  return false;
477  }
478 
479  if(!readFileOptions(_filename, _importer)) {
480  return false;
481  }
482 
483  // Let's see if the user has specified some options
485 
486  std::ifstream ifile(_filename.toUtf8(), (_importer.isBinary() ? std::ios::binary | std::ios::in
487  : std::ios::in) );
488 
489  unsigned long sz = theFile.size()/1024/1024;
490  //the file fits to memory and we still have enough space so pump it in the ram
491  if(sz <= 2 * Utils::Memory::queryFreeRAM())
492  {
493  ifile.rdbuf()->pubsetbuf(NULL,theFile.size());
494  }
495 
496  if (!ifile.is_open() || !ifile.good())
497  {
498  emit log(LOGERR, tr("Cannot open OFF file for reading!"));
499  return false;
500  }
501 
502  assert(ifile);
503 
504  int triMeshControl = TYPEAUTODETECT; // 0 == Auto-Detect
505 
506  if ( OpenFlipper::Options::gui() ){
507  if ( triMeshHandling_ != 0 ){
508  triMeshControl = triMeshHandling_->currentIndex();
509  } else {
510  triMeshControl = TYPEAUTODETECT;
511  }
512  }
513 
514  if ( forceTriangleMesh_ )
515  triMeshControl = TYPETRIANGLE;
516 
517  if ( forcePolyMesh_ )
518  triMeshControl = TYPEPOLY;
519 
521 
522  switch (triMeshControl) {
523  case TYPEAUTODETECT:
524  // Auto-detect
525  type = _importer.isTriangleMesh() ? DATA_TRIANGLE_MESH : DATA_POLY_MESH;
526  break;
527 
528  case TYPEASK:
529  if( !OpenFlipper::Options::nogui() ) {
530  // Create message box
531  QMetaObject::invokeMethod(this,"handleTrimeshDialog",Qt::BlockingQueuedConnection);
532 
533  if (trimeshOptions_ == OFFImporter::TRIMESH)
534  type = DATA_TRIANGLE_MESH;
535  else if (trimeshOptions_ == OFFImporter::POLYMESH)
536  type = DATA_POLY_MESH;
537  else
538  type = _importer.isTriangleMesh() ? DATA_TRIANGLE_MESH : DATA_POLY_MESH;
539 
540  } else {
541  // No gui mode
542  type = _importer.isTriangleMesh() ? DATA_TRIANGLE_MESH : DATA_POLY_MESH;
543  }
544 
545  break;
546 
547  case TYPEPOLY:
548  // Always load as PolyMesh
549  type = DATA_POLY_MESH;
550  break;
551 
552  case TYPETRIANGLE:
553  // Always load as TriangleMesh
554  type = DATA_TRIANGLE_MESH;
555  break;
556 
557  default:
558  break;
559 
560  }
561 
562  return _importer.isBinary() ? parseBinary(ifile, _importer, type, _filename) : parseASCII(ifile, _importer, type, _filename);
563 }
564 
566 {
567  QMessageBox msgBox;
568  QPushButton *detectButton = msgBox.addButton(tr("Auto-Detect"), QMessageBox::ActionRole);
569  QPushButton *triButton = msgBox.addButton(tr("Open as triangle mesh"), QMessageBox::ActionRole);
570  QPushButton *polyButton = msgBox.addButton(tr("Open as poly mesh"), QMessageBox::ActionRole);
571  msgBox.setWindowTitle( tr("Mesh types in file") );
572  msgBox.setText( tr("You are about to open a file containing one or more mesh types. \n\n Which mesh type should be used?") );
573  msgBox.setDefaultButton( detectButton );
574  msgBox.exec();
575 
576 
577  if (msgBox.clickedButton() == triButton)
578  trimeshOptions_ = OFFImporter::TRIMESH ;
579  else if (msgBox.clickedButton() == polyButton)
580  trimeshOptions_ = OFFImporter::POLYMESH ;
581 }
582 
583 //-----------------------------------------------------------------------------------------------------
584 
585 bool FileOFFPlugin::parseASCII(std::istream& _in, OFFImporter& _importer, DataType _type, QString& _objectName) {
586 
587  unsigned int idx;
588  unsigned int nV, nF, dummy;
589  OpenMesh::Vec3f v, n;
590  OpenMesh::Vec2f t;
591  OpenMesh::Vec3i c3;
592  OpenMesh::Vec3f c3f;
593  OpenMesh::Vec4i c4;
594  OpenMesh::Vec4f c4f;
595  std::vector<VertexHandle> vhandles;
596  FaceHandle fh;
597 
598  int objectId = -1;
599  emit addEmptyObject(_type, objectId);
600 
601  BaseObject* object(0);
602  if(!PluginFunctions::getObject( objectId, object )) {
603  emit log(LOGERR, tr("Could not create new object!"));
604  return false;
605  }
606 
607  // Set object's name to match file name
608  QFileInfo f(_objectName);
609  object->setName(f.fileName());
610 
611  // Set initial object
612  _importer.addObject(object);
613 
614  std::string line;
615  std::istringstream sstr;
616 
617  // read header line
618  getCleanLine(_in, line);
619 
620  // + #Vertices, #Faces, #Edges
621  // Note: We use a stringstream because there
622  // could be comments in the line
623  getCleanLine(_in, line);
624  sstr.str(line);
625  sstr >> nV;
626  sstr >> nF;
627  sstr >> dummy;
628 
629  // Reserve memory
630  _importer.reserve(nV, nF * _importer.maxFaceValence() /*Upper bound*/, nF);
631 
632  // skip empty lines and comments
633  std::string tmp;
634  while (true) {
635  char c = _in.peek();
636  if ( (c == '\n') || (c == '#') )
637  std::getline(_in, tmp);
638  else
639  break;
640  }
641 
642  // read vertices: coord [hcoord] [normal] [color] [texcoord]
643  for (uint i=0; i<nV && !_in.eof(); ++i) {
644 
645  // Always read VERTEX
646  v[0] = getFloat(_in);
647  v[1] = getFloat(_in);
648  v[2] = getFloat(_in);
649 
650  const VertexHandle vh = _importer.addVertex(v);
651 
652  // perhaps read NORMAL
653  if ( _importer.hasVertexNormals() ){
654 
655  n[0] = getFloat(_in);
656  n[1] = getFloat(_in);
657  n[2] = getFloat(_in);
658 
659  if(userReadOptions_ & OFFImporter::VERTEXNORMAL) {
660  int nid = _importer.addNormal(n);
661  _importer.setNormal(vh, nid);
662  }
663  }
664 
665  sstr.clear();
666  getCleanLine(_in, line, false);
667  sstr.str(line);
668 
669  int colorType = getColorType(line, _importer.hasTextureCoords() );
670 
671  //perhaps read COLOR
672  if ( _importer.hasVertexColors() ){
673 
674  std::string trash;
675 
676  switch (colorType){
677  case 0 : break; //no color
678  case 1 : sstr >> trash; break; //one int (isn't handled atm)
679  case 2 : sstr >> trash; sstr >> trash; break; //corrupt format (ignore)
680  // rgb int
681  case 3 :
682  sstr >> c3[0];
683  sstr >> c3[1];
684  sstr >> c3[2];
685  if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
686  int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>( c3 ) );
687  _importer.setVertexColor(vh, cidx);
688  }
689  break;
690  // rgba int
691  case 4 :
692  sstr >> c4[0];
693  sstr >> c4[1];
694  sstr >> c4[2];
695  sstr >> c4[3];
696  if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
697  int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>( c4 ) );
698  _importer.setVertexColor(vh, cidx);
699  _importer.addOption(OFFImporter::COLORALPHA);
700  }
701  break;
702  // rgb floats
703  case 5 :
704  c3f[0] = getFloat(sstr);
705  c3f[1] = getFloat(sstr);
706  c3f[2] = getFloat(sstr);
707  if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
708  int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>(c3f) );
709  _importer.setVertexColor(vh, cidx);
710  }
711  break;
712  // rgba floats
713  case 6 :
714  c4f[0] = getFloat(sstr);
715  c4f[1] = getFloat(sstr);
716  c4f[2] = getFloat(sstr);
717  c4f[3] = getFloat(sstr);
718  if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
719  int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>(c4f) );
720  _importer.setVertexColor(vh, cidx);
721  _importer.addOption(OFFImporter::COLORALPHA);
722  }
723  break;
724 
725  default:
726  std::cerr << "Error in file format (colorType = " << colorType << ")\n";
727  break;
728  }
729  }
730 
731  //perhaps read TEXTURE COORDS
732  if ( _importer.hasTextureCoords() ){
733  t[0] = getFloat(sstr);
734  t[1] = getFloat(sstr);
735  if ( userReadOptions_ & OFFImporter::VERTEXTEXCOORDS ) {
736  int tcidx = _importer.addTexCoord(t);
737  _importer.setVertexTexCoord(vh, tcidx);
738  }
739  }
740  }
741 
742  // skip empty lines and comments
743  while (true) {
744  char c = _in.peek();
745  if ( (c == '\n') || (c == '#') )
746  std::getline(_in, tmp);
747  else
748  break;
749  }
750 
751  // faces
752  // #N <v1> <v2> .. <v(n-1)> [color spec]
753  for (uint i=0; i<nF; ++i)
754  {
755  // nV = number of Vertices for current face
756  _in >> nV;
757 
758  // If number of faces < 3, we have a degenerated face
759  // which we don't allow and thus skip
760  if (nV < 3) {
761  // Read the rest of the line and dump it
762  getCleanLine(_in, line, false);
763  // Proceed reading
764  continue;
765  }
766 
767  vhandles.clear();
768  for (unsigned int count=0; count<nV; ++count) {
769  _in >> idx;
770  vhandles.push_back(VertexHandle(idx));
771  }
772 
773  bool checkManifold = true;
774  if(!OpenFlipper::Options::nogui() && loadCheckManifold_ != 0) {
775  checkManifold = loadCheckManifold_->isChecked();
776  }
777 
778  // Check for degenerate faces if specified in gui
779  if(checkManifold) {
780  if(checkDegenerateFace(vhandles)) {
781  fh = _importer.addFace(vhandles);
782  } else {
783  continue;
784  }
785  } else {
786  fh = _importer.addFace(vhandles);
787  }
788 
789  //perhaps read face COLOR
790  if ( _importer.hasFaceColors() ){
791 
792  //take the rest of the line and check how colors are defined
793  sstr.clear();
794  getCleanLine(_in, line, false);
795  sstr.str(line);
796 
797  int colorType = getColorType(line, false);
798 
799  std::string trash;
800 
801  switch (colorType){
802  case 0 : break; //no color
803  case 1 : sstr >> trash; break; //one int (isn't handled atm)
804  case 2 : sstr >> trash; sstr >> trash; break; //corrupt format (ignore)
805  // rgb int
806  case 3 :
807  sstr >> c3[0];
808  sstr >> c3[1];
809  sstr >> c3[2];
810  if ( userReadOptions_ & OFFImporter::FACECOLOR ) {
811  int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>( c3 ) );
812  _importer.setFaceColor(fh, cidx);
813  }
814  break;
815  // rgba int
816  case 4 :
817  sstr >> c4[0];
818  sstr >> c4[1];
819  sstr >> c4[2];
820  sstr >> c4[3];
821  if ( userReadOptions_ & OFFImporter::FACECOLOR ) {
822  int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>( c4 ) );
823  _importer.setFaceColor(fh, cidx);
824  _importer.addOption(OFFImporter::COLORALPHA);
825  }
826  break;
827  // rgb floats
828  case 5 :
829  c3f[0] = getFloat(sstr);
830  c3f[1] = getFloat(sstr);
831  c3f[2] = getFloat(sstr);
832  if ( userReadOptions_ & OFFImporter::FACECOLOR ) {
833  int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>(c3f) );
834  _importer.setFaceColor(fh, cidx);
835  }
836  break;
837  // rgba floats
838  case 6 :
839  c4f[0] = getFloat(sstr);
840  c4f[1] = getFloat(sstr);
841  c4f[2] = getFloat(sstr);
842  c4f[3] = getFloat(sstr);
843  if ( userReadOptions_ & OFFImporter::FACECOLOR ) {
844  int cidx = _importer.addColor( OpenMesh::color_cast<PolyMesh::Color>(c4f) );
845  _importer.setFaceColor(fh, cidx);
846  _importer.addOption(OFFImporter::COLORALPHA);
847  }
848  break;
849 
850  default:
851  std::cerr << "Error in file format (colorType = " << colorType << ")\n";
852  break;
853  }
854  }
855  }
856 
857  // File was successfully parsed.
858  return true;
859 }
860 
861 //-----------------------------------------------------------------------------------------------------
862 
863 bool FileOFFPlugin::checkDegenerateFace(const std::vector<VertexHandle>& _v) {
864 
865  bool check = true;
866  int size = _v.size();
867  // Check if at least two elements in the list have the same value
868  for(int i = 0; i < size; ++i) {
869  for(int j = i+1; j < size; ++j) {
870  if(_v[i] == _v[j]) check = false;
871  }
872  }
873  return check;
874 }
875 
876 //-----------------------------------------------------------------------------------------------------
877 
878 int FileOFFPlugin::getColorType(std::string& _line, bool _texCoordsAvailable) {
879  /*
880  0 : no Color
881  1 : one int (e.g colormap index)
882  2 : two items (error!)
883  3 : 3 ints
884  4 : 4 ints
885  5 : 3 floats
886  6 : 4 floats
887  */
888 
889  // Check if we have any additional information here
890  if ( _line.size() < 1 )
891  return 0;
892 
893  //first remove spaces at start/end of the line
894  trimString(_line);
895 
896  //count the remaining items in the line
897  size_t found;
898  int count = 0;
899 
900  found=_line.find_first_of(" ");
901  while (found!=std::string::npos){
902  count++;
903  found=_line.find_first_of(" ",found+1);
904  }
905 
906  if (!_line.empty()) count++;
907 
908  if (_texCoordsAvailable) count -= 2;
909 
910  if (count == 3 || count == 4){
911  //get first item
912  found = _line.find(" ");
913  std::string c1 = _line.substr (0,found);
914 
915  if (c1.find(".") != std::string::npos){
916  if (count == 3)
917  count = 5;
918  else
919  count = 6;
920  }
921  }
922  return count;
923 }
924 
925 //-----------------------------------------------------------------------------------------------------
926 
927 bool FileOFFPlugin::parseBinary(std::istream& _in, OFFImporter& _importer, DataType _type, QString& _objectName) {
928 
929  unsigned int idx;
930  unsigned int nV, nF, dummy;
931  float dummy_f;
932  OpenMesh::Vec3f v, n;
933  OpenMesh::Vec4f c;
934  float alpha = 1.0f;
935  OpenMesh::Vec2f t;
936  std::vector<VertexHandle> vhandles;
937  FaceHandle fh;
938 
939  int objectId = -1;
940  emit addEmptyObject(_type, objectId);
941 
942  BaseObject* object(0);
943  if(!PluginFunctions::getObject( objectId, object )) {
944  emit log(LOGERR, tr("Could not create new object!"));
945  return false;
946  }
947 
948  // Set object's name to match file name
949  QFileInfo f(_objectName);
950  object->setName(f.fileName());
951 
952  // Set initial object
953  _importer.addObject(object);
954 
955  // read header line
956  std::string header;
957  getCleanLine(_in,header);
958 
959  // + #Vertices, #Faces, #Edges
960  readValue(_in, nV);
961  readValue(_in, nF);
962  readValue(_in, dummy);
963 
964  // Reserve memory
965  _importer.reserve(nV, nF * _importer.maxFaceValence() /*Upper bound*/, nF);
966 
967  // read vertices: coord [hcoord] [normal] [color] [texcoord]
968  for (uint i=0; i<nV && !_in.eof(); ++i)
969  {
970  // Always read Vertex
971  readValue(_in, v[0]);
972  readValue(_in, v[1]);
973  readValue(_in, v[2]);
974 
975  const VertexHandle vh = _importer.addVertex(v);
976 
977  if ( _importer.hasVertexNormals() ) {
978  readValue(_in, n[0]);
979  readValue(_in, n[1]);
980  readValue(_in, n[2]);
981 
982  if ( userReadOptions_ & OFFImporter::VERTEXNORMAL ) {
983  int nidx = _importer.addNormal(n);
984  _importer.setNormal(vh, nidx);
985  }
986  }
987 
988  if ( _importer.hasVertexColors() ) {
989  // Vertex colors are always without alpha
990  readValue(_in, c[0]);
991  readValue(_in, c[1]);
992  readValue(_in, c[2]);
993  c[3] = 1.0;
994 
995  if ( userReadOptions_ & OFFImporter::VERTEXCOLOR ) {
996  int cidx = _importer.addColor( c );
997  _importer.setVertexColor(vh, cidx);
998  }
999  }
1000 
1001  if ( _importer.hasTextureCoords() ) {
1002  readValue(_in, t[0]);
1003  readValue(_in, t[1]);
1004 
1005  if ( userReadOptions_ & OFFImporter::VERTEXTEXCOORDS ) {
1006  int tcidx = _importer.addTexCoord(t);
1007  _importer.setVertexTexCoord(vh, tcidx);
1008  }
1009  }
1010  }
1011 
1012  int pos = 0;
1013  int nB = 0;
1014 
1015  // faces
1016  // #N <v1> <v2> .. <v(n-1)> [color spec]
1017  for (uint i = 0; i<nF && !_in.eof(); ++i)
1018  {
1019  // Get bytes to be read from this point on
1020  if(i == 0) {
1021  pos = _in.tellg();
1022  _in.seekg(0, std::ios::end);
1023  nB = _in.tellg();
1024  nB -= pos;
1025  _in.seekg(pos);
1026  // nB now holds the total number of bytes to be read
1027  }
1028 
1029  readValue(_in, nV);
1030 
1031  // Now that we have the initial face valence
1032  // we check, if there could possibly be colors
1033  // after the face specs by checking if
1034  // the bytes to be read from this point on (nB)
1035  // equal (nF + nF*nV)*4 (each line of [nV V_1 ... V..nV]).
1036  // If not, we have more bytes to be read than
1037  // actual face definitions.
1038  // So if we have at least nF additional bytes
1039  // we can read the number of color components after each face
1040  // definition.
1041  if(i == 0) {
1042  // Always make sure that we only deal with
1043  // integers and floats/doubles
1044  if(nB % 4 == 0) {
1045  // Cut down number of bytes to number
1046  // of elements to be read
1047  nB /= 4;
1048 
1049  nB -= nF + nF*nV;
1050 
1051  if(nB <= 0) {
1052  // We don't have additional color components
1053  // Case nB < 0: Face valence is not constant
1054  // throughout the mesh
1055  readColorComp_ = false;
1056  } else {
1057  // Not enough additional elements to read
1058  // This should actually never happen...
1059  // or nB >= nF -> perform extended
1060  // face color component test
1061  readColorComp_ = extendedFaceColorTest(_in, nV, nF, nB);
1062  }
1063  }
1064  }
1065 
1066  // Check if the face has at least valence 3
1067  // if not, skip the current face
1068  if (nV < 3) {
1069  // Read in following vertex indices and dump them
1070  for (uint j = 0; j < nV; ++j) {
1071  readValue(_in, dummy);
1072  }
1073  // Read in color components if available
1074  // and dump them
1075  if (readColorComp_) {
1076  // Number of color components
1077  readValue(_in, nV);
1078  for (uint j = 0; j < nV; ++j) {
1079  readValue(_in, dummy_f);
1080  }
1081  }
1082  // Proceed reading
1083  continue;
1084  }
1085 
1086  // Read vertex indices of current face
1087  vhandles.clear();
1088  for (uint j = 0; j < nV; ++j) {
1089  readValue(_in, idx);
1090  vhandles.push_back(VertexHandle(idx));
1091  }
1092 
1093  fh = _importer.addFace(vhandles);
1094 
1095  if ( !readColorComp_ ) {
1096  // Some binary files that were created via an OFF writer
1097  // that doesn't comply with the OFF specification
1098  // don't specify the number of color components before
1099  // the face specs.
1100  nV = 0;
1101  } else {
1102  // nV now holds the number of color components
1103  readValue(_in, nV);
1104  }
1105 
1106  // valid face color:
1107  if ( nV == 3 || nV == 4 ) {
1108 
1109  // Read standard rgb color
1110  for(uint k = 0; k < 3; ++k) {
1111  readValue(_in, c[k]);
1112  --nV;
1113  }
1114 
1115  // Color has additional alpha value
1116  if(nV == 1) {
1117  readValue(_in, alpha);
1118  }
1119 
1120  if(userReadOptions_ & OFFImporter::FACECOLOR) {
1121  if(userReadOptions_ & OFFImporter::COLORALPHA) {
1122  int cidx = _importer.addColor(OpenMesh::Vec4f(c[0], c[1], c[2], alpha));
1123  _importer.setFaceColor( fh, cidx );
1124  _importer.addOption(OFFImporter::COLORALPHA);
1125  } else {
1126  int cidx = _importer.addColor(OpenMesh::color_cast<OpenMesh::Vec4f>(c));
1127  _importer.setFaceColor( fh, cidx );
1128  }
1129  }
1130  } else {
1131  // Skip face colors since they are not in a supported format
1132  for(unsigned int count = 0; count < nV; ++count) {
1133  readValue(_in, dummy_f);
1134  }
1135  }
1136  }
1137 
1138  // File was successfully parsed.
1139  return true;
1140 }
1141 
1142 //-----------------------------------------------------------------------------------------------------
1143 
1144 bool FileOFFPlugin::extendedFaceColorTest(std::istream& _in, uint _nV, uint _nF, int _nB) const {
1145 
1146  // Perform the extended and even more reliable color
1147  // component test. Read an integer n (starting with the face
1148  // valence) and skip n*4 bytes. Repeat this nF times.
1149  // After this we have two cases:
1150  //
1151  // Case 1: The file contains face color components
1152  // and we interpreted the number of face components as face
1153  // valence which results in a bunch of bytes that still are to be read
1154  // after nF cycles.
1155  //
1156  // Case 2: The mesh has varying face valences and has
1157  // therefor been wrongly detected as containing face color components.
1158  // If this has happened, the following test will result in
1159  // no bytes to be left for reading after nF reading cycles.
1160 
1161  uint nV = _nV;
1162  uint dummy = 0;
1163 
1164  // Get current file pointer
1165  int pos = _in.tellg();
1166 
1167  for(uint k = 0; k < _nF; ++k) {
1168  // Remember: The first nV has already been read
1169  if(k != 0)
1170  readValue(_in, nV);
1171 
1172  // Skip the following nV values
1173  for(uint z = 0; z < nV; ++z) {
1174  readValue(_in, dummy);
1175  }
1176  }
1177 
1178  // Get position after all the reading has been done
1179  int currPos = _in.tellg();
1180 
1181  // Reset read pointer to where we were
1182  _in.seekg(pos);
1183 
1184  if(_nB - currPos == 0) {
1185  // No additional face colors have been specified
1186  return false;
1187  }
1188 
1189  // We actually have face colors
1190  return true;
1191 }
1192 
1193 //-----------------------------------------------------------------------------------------------------
1194 
1195 int FileOFFPlugin::loadObject(QString _filename) {
1196  OFFImporter importer;
1197 
1198  // Parse file
1199  readOFFFile( _filename, importer );
1200 
1201  // Finish importing
1202  importer.finish();
1203 
1204  BaseObject* object = importer.getObject();
1205 
1206  if(!object){
1207 
1208  forceTriangleMesh_ = false;
1209  forcePolyMesh_ = false;
1210 
1211  return -1;
1212  }
1213 
1214  object->setFromFileName(_filename);
1215 
1216  // Handle new PolyMeshes
1217  PolyMeshObject* polyMeshObj = dynamic_cast< PolyMeshObject* > (object);
1218 
1219  if ( polyMeshObj ){
1220 
1221  if ( !importer.hasVertexNormals() || (userReadOptions_ & OFFImporter::FORCE_NONORMALS) ) {
1222  emit log(LOGINFO, tr("loadObject: Computing vertex and face normals.") );
1223  polyMeshObj->mesh()->update_normals();
1224  } else {
1225  emit log(LOGINFO, tr("loadObject: Computing face normals.") );
1226  polyMeshObj->mesh()->update_face_normals();
1227  }
1228 
1229  backupTextureCoordinates(*(polyMeshObj->mesh()));
1230  }
1231 
1232  // Handle new TriMeshes
1233  TriMeshObject* triMeshObj = dynamic_cast< TriMeshObject* > (object);
1234 
1235  if ( triMeshObj ){
1236 
1237  if ( !importer.hasVertexNormals() || (userReadOptions_ & OFFImporter::FORCE_NONORMALS) ) {
1238  emit log(LOGINFO, tr("loadObject: Computing vertex and face normals.") );
1239  triMeshObj->mesh()->update_normals();
1240  } else {
1241  emit log(LOGINFO, tr("loadObject: Computing face normals.") );
1242  triMeshObj->mesh()->update_face_normals();
1243  }
1244 
1245  backupTextureCoordinates(*(triMeshObj->mesh()));
1246  }
1247 
1248  //general stuff
1249  emit updatedObject(object->id(), UPDATE_ALL);
1250  emit openedFile( object->id() );
1251 
1252  forceTriangleMesh_ = false;
1253  forcePolyMesh_ = false;
1254  return object->id();
1255 }
1256 
1257 //-----------------------------------------------------------------------------------------------------
1258 
1259 int FileOFFPlugin::loadObject(QString _filename, DataType _type) {
1260 
1261  forceTriangleMesh_ = false;
1262  forcePolyMesh_ = false;
1263 
1264  if ( _type == DATA_TRIANGLE_MESH )
1265  forceTriangleMesh_ = true;
1266  else if ( _type == DATA_POLY_MESH )
1267  forcePolyMesh_ = true;
1268 
1269  return loadObject(_filename);
1270 
1271 }
1272 
1273 //-----------------------------------------------------------------------------------------------------
1274 
1275 bool FileOFFPlugin::saveObject(int _id, QString _filename)
1276 {
1277  BaseObjectData* object;
1278  if ( !PluginFunctions::getObject(_id,object) ) {
1279  emit log(LOGERR, tr("saveObject : cannot get object id %1 for save name %2").arg(_id).arg(_filename) );
1280  return false;
1281  }
1282 
1283  std::string filename = std::string( _filename.toUtf8() );
1284 
1285  bool binary = userWriteOptions_ & OFFImporter::BINARY;
1286  std::fstream ofs( filename.c_str(), (binary ? std::ios_base::out | std::ios_base::binary : std::ios_base::out));
1287 
1288  if (!ofs) {
1289 
1290  emit log(LOGERR, tr("saveObject : Cannot not open file %1 for writing!").arg(_filename) );
1291  return false;
1292  }
1293 
1294  // Get user specified options
1296 
1297  if ( object->dataType( DATA_POLY_MESH ) ) {
1298 
1299  object->setFromFileName(_filename);
1300  object->setName(object->filename());
1301 
1302  PolyMeshObject* polyObj = dynamic_cast<PolyMeshObject* >( object );
1303 
1304  if (writeMesh(ofs, *polyObj->mesh(), *polyObj)){
1305  emit log(LOGINFO, tr("Saved object to ") + _filename );
1306  ofs.close();
1307  return true;
1308  }else{
1309  emit log(LOGERR, tr("Unable to save ") + _filename);
1310  ofs.close();
1311  return false;
1312  }
1313  } else if ( object->dataType( DATA_TRIANGLE_MESH ) ) {
1314 
1315  object->setFromFileName(_filename);
1316  object->setName(object->filename());
1317 
1318  TriMeshObject* triObj = dynamic_cast<TriMeshObject* >( object );
1319 
1320  if (writeMesh(ofs, *triObj->mesh(), *triObj)) {
1321  emit log(LOGINFO, tr("Saved object to ") + _filename );
1322  ofs.close();
1323  return true;
1324  } else {
1325  emit log(LOGERR, tr("Unable to save ") + _filename );
1326  ofs.close();
1327  return false;
1328  }
1329  } else {
1330  emit log(LOGERR, tr("Unable to save (object is not a compatible mesh type)"));
1331  ofs.close();
1332  return false;
1333  }
1334 }
1335 
1336 //-----------------------------------------------------------------------------------------------------
1337 
1338 template <class MeshT>
1340 
1341  // Create a backup of the original per Vertex texture Coordinates
1342  if (_mesh.has_vertex_texcoords2D()) {
1343 
1345  if (!_mesh.get_property_handle(oldVertexCoords, "Original Per Vertex Texture Coords"))
1346  _mesh.add_property(oldVertexCoords, "Original Per Vertex Texture Coords");
1347 
1348  for (typename MeshT::VertexIter v_it = _mesh.vertices_begin(); v_it != _mesh.vertices_end(); ++v_it)
1349  _mesh.property(oldVertexCoords, *v_it) = _mesh.texcoord2D(*v_it);
1350 
1351  }
1352 
1353  // Create a backup of the original per Face texture Coordinates
1354  if (_mesh.has_halfedge_texcoords2D()) {
1355 
1357  if (!_mesh.get_property_handle(oldHalfedgeCoords,"Original Per Face Texture Coords"))
1358  _mesh.add_property(oldHalfedgeCoords,"Original Per Face Texture Coords");
1359 
1360  for (typename MeshT::HalfedgeIter he_it = _mesh.halfedges_begin(); he_it != _mesh.halfedges_end(); ++he_it)
1361  _mesh.property(oldHalfedgeCoords, *he_it) = _mesh.texcoord2D(*he_it);
1362 
1363  }
1364 }
1365 
1366 //-----------------------------------------------------------------------------------------------------
1367 
1368 QWidget* FileOFFPlugin::saveOptionsWidget(QString /*_currentFilter*/) {
1369 
1370  if (saveOptions_ == 0){
1371  //generate widget
1372  saveOptions_ = new QWidget();
1373  QVBoxLayout* layout = new QVBoxLayout();
1374  layout->setAlignment(Qt::AlignTop);
1375 
1376  saveBinary_ = new QCheckBox("Save Binary");
1377  layout->addWidget(saveBinary_);
1378 
1379  saveVertexColor_ = new QCheckBox("Save Vertex Colors");
1380  layout->addWidget(saveVertexColor_);
1381 
1382  saveFaceColor_ = new QCheckBox("Save Face Colors");
1383  layout->addWidget(saveFaceColor_);
1384 
1385  saveAlpha_ = new QCheckBox("Save Color Alpha");
1386  layout->addWidget(saveAlpha_);
1387 
1388  saveNormals_ = new QCheckBox("Save Normals");
1389  layout->addWidget(saveNormals_);
1390 
1391  saveTexCoords_ = new QCheckBox("Save TexCoords");
1392  layout->addWidget(saveTexCoords_);
1393 
1394  savePrecisionLabel_ = new QLabel("Writer Precision");
1395  layout->addWidget(savePrecisionLabel_);
1396 
1397  savePrecision_ = new QSpinBox();
1398  savePrecision_->setMinimum(1);
1399  savePrecision_->setMaximum(12);
1400  savePrecision_->setValue(6);
1401  layout->addWidget(savePrecision_);
1402 
1403  saveDefaultButton_ = new QPushButton("Make Default");
1404  layout->addWidget(saveDefaultButton_);
1405 
1406  saveOptions_->setLayout(layout);
1407 
1408  connect(saveBinary_, SIGNAL(clicked(bool)), savePrecision_, SLOT(setDisabled(bool)));
1409  connect(saveDefaultButton_, SIGNAL(clicked()), this, SLOT(slotSaveDefault()));
1410 
1411  saveBinary_->setChecked( OpenFlipperSettings().value("FileOff/Save/Binary",false).toBool() );
1412  saveVertexColor_->setChecked( OpenFlipperSettings().value("FileOff/Save/VertexColor",true).toBool() );
1413  saveFaceColor_->setChecked( OpenFlipperSettings().value("FileOff/Save/FaceColor",true).toBool() );
1414  saveAlpha_->setChecked( OpenFlipperSettings().value("FileOff/Save/Alpha",true).toBool() );
1415  saveNormals_->setChecked( OpenFlipperSettings().value("FileOff/Save/Normals",true).toBool() );
1416  saveTexCoords_->setChecked( OpenFlipperSettings().value("FileOff/Save/TexCoords",true).toBool() );
1417 
1418  }
1419 
1420  return saveOptions_;
1421 }
1422 
1423 //-----------------------------------------------------------------------------------------------------
1424 
1425 QWidget* FileOFFPlugin::loadOptionsWidget(QString /*_currentFilter*/) {
1426 
1427  if (loadOptions_ == 0){
1428  //generate widget
1429  loadOptions_ = new QWidget();
1430  QVBoxLayout* layout = new QVBoxLayout();
1431  layout->setAlignment(Qt::AlignTop);
1432 
1433  QLabel* label = new QLabel(tr("If PolyMesh is a Triangle Mesh:"));
1434 
1435  layout->addWidget(label);
1436 
1437  triMeshHandling_ = new QComboBox();
1438  triMeshHandling_->addItem( tr("Auto-Detect") );
1439  triMeshHandling_->addItem( tr("Ask") );
1440  triMeshHandling_->addItem( tr("Always open as PolyMesh") );
1441  triMeshHandling_->addItem( tr("Always open as TriangleMesh") );
1442 
1443  layout->addWidget(triMeshHandling_);
1444 
1445  loadVertexColor_ = new QCheckBox("Load Vertex Colors");
1446  layout->addWidget(loadVertexColor_);
1447 
1448  loadFaceColor_ = new QCheckBox("Load Face Colors");
1449  layout->addWidget(loadFaceColor_);
1450 
1451  loadAlpha_ = new QCheckBox("Load Color Alpha");
1452  layout->addWidget(loadAlpha_);
1453 
1454  loadNormals_ = new QCheckBox("Load Normals");
1455  layout->addWidget(loadNormals_);
1456 
1457  loadTexCoords_ = new QCheckBox("Load TexCoords");
1458  layout->addWidget(loadTexCoords_);
1459 
1460  loadCheckManifold_ = new QCheckBox("Check for manifold configurations");
1461  layout->addWidget(loadCheckManifold_);
1462 
1463  loadDefaultButton_ = new QPushButton("Make Default");
1464  layout->addWidget(loadDefaultButton_);
1465 
1466  loadOptions_->setLayout(layout);
1467 
1468  connect(loadDefaultButton_, SIGNAL(clicked()), this, SLOT(slotLoadDefault()));
1469 
1470  triMeshHandling_->setCurrentIndex(OpenFlipperSettings().value("FileOff/Load/TriMeshHandling",TYPEAUTODETECT ).toInt() );
1471 
1472  loadVertexColor_->setChecked( OpenFlipperSettings().value("FileOff/Load/VertexColor",true).toBool() );
1473  loadFaceColor_->setChecked( OpenFlipperSettings().value("FileOff/Load/FaceColor",true).toBool() );
1474  loadAlpha_->setChecked( OpenFlipperSettings().value("FileOff/Load/Alpha",true).toBool() );
1475  loadNormals_->setChecked( OpenFlipperSettings().value("FileOff/Load/Normals",true).toBool() );
1476  loadTexCoords_->setChecked( OpenFlipperSettings().value("FileOff/Load/TexCoords",true).toBool() );
1477  }
1478 
1479  return loadOptions_;
1480 }
1481 
1483  OpenFlipperSettings().setValue( "FileOff/Load/VertexColor", loadVertexColor_->isChecked() );
1484  OpenFlipperSettings().setValue( "FileOff/Load/FaceColor", loadFaceColor_->isChecked() );
1485  OpenFlipperSettings().setValue( "FileOff/Load/Alpha", loadAlpha_->isChecked() );
1486  OpenFlipperSettings().setValue( "FileOff/Load/Normals", loadNormals_->isChecked() );
1487  OpenFlipperSettings().setValue( "FileOff/Load/TexCoords", loadTexCoords_->isChecked() );
1488 
1489  OpenFlipperSettings().setValue("FileOff/Load/TriMeshHandling", triMeshHandling_->currentIndex() );
1490 
1491  OpenFlipperSettings().setValue( "Core/File/UseLoadDefaults", true );
1492 }
1493 
1494 
1496  OpenFlipperSettings().setValue( "FileOff/Save/Binary", saveBinary_->isChecked() );
1497  OpenFlipperSettings().setValue( "FileOff/Save/VertexColor", saveVertexColor_->isChecked() );
1498  OpenFlipperSettings().setValue( "FileOff/Save/FaceColor", saveFaceColor_->isChecked() );
1499  OpenFlipperSettings().setValue( "FileOff/Save/Alpha", saveAlpha_->isChecked() );
1500  OpenFlipperSettings().setValue( "FileOff/Save/Normals", saveNormals_->isChecked() );
1501  OpenFlipperSettings().setValue( "FileOff/Save/TexCoords", saveTexCoords_->isChecked() );
1502 }
1503 
1504 
1505 
void removeOption(ObjectOptionsE _option)
remove an option
Definition: OFFImporter.cc:534
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
DataType supportedType()
Return your supported object type( e.g. DATA_TRIANGLE_MESH )
Definition: FileOFF.cc:139
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
Type for a Meshobject containing a poly mesh.
Definition: PolyMesh.hh:65
int getColorType(std::string &_line, bool _texCoordsAvailable)
Get color type.
Definition: FileOFF.cc:878
void updateUserOptions()
Definition: FileOFF.cc:197
bool parseBinary(std::istream &_in, OFFImporter &_importer, DataType _type, QString &_objectName)
Parse binary OFF file.
Definition: FileOFF.cc:927
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
bool extendedFaceColorTest(std::istream &_in, uint _nV, uint _nF, int _nB) const
Test if there are face color components (_nV is the initial face valence)
Definition: FileOFF.cc:1144
BaseObject * getObject()
get BaseObject data of object
Definition: OFFImporter.cc:503
void initializePlugin()
Initialize Plugin.
Definition: FileOFF.cc:96
void finish()
Definition: OFFImporter.cc:329
void backupTextureCoordinates(MeshT &_mesh)
backup per vertex/face texture coordinates
Definition: FileOFF.cc:1339
void slotLoadDefault()
Slot called when user wants to save the given Load options as default.
Definition: FileOFF.cc:1482
QString getLoadFilters()
Definition: FileOFF.cc:127
Predefined datatypes.
Definition: DataTypes.hh:83
QString filename() const
return the filename of the object
Definition: BaseObject.cc:706
void setNormal(VertexHandle _vh, int _normalID)
set vertex normal
Definition: OFFImporter.cc:196
void setVertexColor(VertexHandle _vh, int _colorIndex)
set vertex color
Definition: OFFImporter.cc:555
QString getSaveFilters()
Definition: FileOFF.cc:133
int id() const
Definition: BaseObject.cc:190
MeshT * mesh()
return a pointer to the mesh
bool hasVertexNormals()
Query Object Options.
Definition: OFFImporter.cc:436
bool dataType(DataType _type) const
Definition: BaseObject.cc:221
bool parseASCII(std::istream &_in, OFFImporter &_importer, DataType _type, QString &_objectName)
Parse ascii OFF file.
Definition: FileOFF.cc:585
void slotSaveDefault()
Slot called when user wants to save the given Save options as default.
Definition: FileOFF.cc:1495
bool writeMesh(std::ostream &_out, MeshT &_mesh, BaseObject &_baseObj)
Writer function.
int loadObject(QString _filename)
Loads Object and converts it to a triangle mesh if possible.
Definition: FileOFF.cc:1195
void setVertexTexCoord(VertexHandle _vh, int _texCoordID)
set vertex texture coordinate
Definition: OFFImporter.cc:151
int addFace(const VHandles &_indices)
add a face with indices _indices refering to vertices
Definition: OFFImporter.cc:261
void addObject(BaseObject *_object)
add initial object
Definition: OFFImporter.cc:66
FileOFFPlugin()
Constructor.
Definition: FileOFF.cc:65
void addOption(ObjectOptionsE _option)
add an option
Definition: OFFImporter.cc:528
int addNormal(const Vec3f &_normal)
add a normal
Definition: OFFImporter.cc:113
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:67
void handleTrimeshDialog()
Displays a dialog to ask how to load the mesh (triangle, polymesh , autodetect)
Definition: FileOFF.cc:565
void update_normals()
Compute normals for all primitives.
QWidget * loadOptionsWidget(QString)
Definition: FileOFF.cc:1425
VertexHandle addVertex(const Vec3f &_point)
add a vertex with coordinate _point
Definition: OFFImporter.cc:236
QWidget * saveOptionsWidget(QString)
Definition: FileOFF.cc:1368
void update_face_normals()
Update normal vectors for all faces.
bool getCleanLine(std::istream &ifs, std::string &_string, bool _skipEmptyLines=true)
Function to retrieve next line.
Definition: FileOFF.cc:160
int addColor(const Vec4f &_color)
add a color
Definition: OFFImporter.cc:122
int addTexCoord(const Vec2f &_coord)
add texture coordinates
Definition: OFFImporter.cc:104
void setValue(const QString &key, const QVariant &value)
Wrapper function which makes it possible to enable Debugging output with -DOPENFLIPPER_SETTINGS_DEBUG...
bool readOFFFile(QString _filename, OFFImporter &_importer)
Read OFF file and parse it.
Definition: FileOFF.cc:472
DLLEXPORT OpenFlipperQSettings & OpenFlipperSettings()
QSettings object containing all program settings of OpenFlipper.
bool readFileOptions(QString _filename, OFFImporter &_importer)
Before Parsing the actual file, read all features supported.
Definition: FileOFF.cc:256
void setFaceColor(FaceHandle _fh, int _colorIndex)
set face color
Definition: OFFImporter.cc:595
bool checkDegenerateFace(const std::vector< VertexHandle > &_v)
Check for degenerate faces before adding them.
Definition: FileOFF.cc:863