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