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