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