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
47 #include <OpenFlipper/Utils/FileIO/NumberParsing.hh>
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
65 FileOFFPlugin::FileOFFPlugin()
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
96 void FileOFFPlugin::initializePlugin() {
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
127 QString FileOFFPlugin::getLoadFilters() {
128 return QString( tr("Object File Format files ( *.off )") );
129 };
130
131 //-----------------------------------------------------------------------------------------------------
132
133 QString FileOFFPlugin::getSaveFilters() {
134 return QString( tr("Object File Format files ( *.off )") );
135 };
136
137 //-----------------------------------------------------------------------------------------------------
138
139 DataType FileOFFPlugin::supportedType() {
140 DataType type = DATA_POLY_MESH | DATA_TRIANGLE_MESH;
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
197 void FileOFFPlugin::updateUserOptions() {
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
483 updateUserOptions();
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
519 DataType type = DATA_TRIANGLE_MESH;
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
564 void FileOFFPlugin::handleTrimeshDialog()
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
1294 updateUserOptions();
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>
1338 void FileOFFPlugin::backupTextureCoordinates(MeshT& _mesh) {
1339
1340 // Create a backup of the original per Vertex texture Coordinates
1341 if (_mesh.has_vertex_texcoords2D()) {
1342
1343 OpenMesh::VPropHandleT< typename MeshT::TexCoord2D > oldVertexCoords;
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
1355 OpenMesh::HPropHandleT< typename MeshT::TexCoord2D > oldHalfedgeCoords;
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
1481 void FileOFFPlugin::slotLoadDefault() {
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
1494 void FileOFFPlugin::slotSaveDefault() {
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
OpenFlipperSettings
DLLEXPORT OpenFlipperQSettings & OpenFlipperSettings()
QSettings object containing all program settings of OpenFlipper.
Definition: GlobalOptions.cc:1038
LOGINFO
Definition: LoggingInterface.hh:94
OFFImporter::finish
void finish()
Definition: OFFImporter.cc:329
UPDATE_ALL
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
FileOFFPlugin::getCleanLine
bool getCleanLine(std::istream &ifs, std::string &_string, bool _skipEmptyLines=true)
Function to retrieve next line.
Definition: FileOFF.cc:160
FileOFFPlugin::getSaveFilters
QString getSaveFilters()
Definition: FileOFF.cc:133
DATA_POLY_MESH
#define DATA_POLY_MESH
Definition: PolyMesh.hh:59
BaseObject::id
int id() const
Definition: BaseObject.cc:190
OFFImporter::getObject
BaseObject * getObject()
get BaseObject data of object
Definition: OFFImporter.cc:503
OFFImporter::hasVertexNormals
bool hasVertexNormals()
Query Object Options.
Definition: OFFImporter.cc:436
OFFImporter::setVertexTexCoord
void setVertexTexCoord(VertexHandle _vh, int _texCoordID)
set vertex texture coordinate
Definition: OFFImporter.cc:151
OFFImporter::addNormal
int addNormal(const Vec3f &_normal)
add a normal
Definition: OFFImporter.cc:113
OpenFlipperQSettings::setValue
void setValue(const QString &key, const QVariant &value)
Wrapper function which makes it possible to enable Debugging output with -DOPENFLIPPER_SETTINGS_DEBUG...
Definition: OpenFlipperQSettings.cc:61
BaseObject::dataType
bool dataType(DataType _type) const
Definition: BaseObject.cc:221
MeshObject::mesh
MeshT * mesh()
return a pointer to the mesh
Definition: MeshObjectT_impl.hh:350
FileOFFPlugin::parseASCII
bool parseASCII(std::istream &_in, OFFImporter &_importer, DataType _type, QString &_objectName)
Parse ascii OFF file.
Definition: FileOFF.cc:584
LOGERR
Definition: LoggingInterface.hh:96
FileOFFPlugin::readOFFFile
bool readOFFFile(QString _filename, OFFImporter &_importer)
Read OFF file and parse it.
Definition: FileOFF.cc:471
FileOFFPlugin::readFileOptions
bool readFileOptions(QString _filename, OFFImporter &_importer)
Before Parsing the actual file, read all features supported.
Definition: FileOFF.cc:256
PluginFunctions::getObject
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
Definition: PluginFunctions.cc:294
TriMeshObject
Type for a MeshObject containing a triangle mesh.
Definition: TriangleMesh.hh:67
OFFImporter::addObject
void addObject(BaseObject *_object)
add initial object
Definition: OFFImporter.cc:66
VectorT< signed int, 4 >
FileOFFPlugin::saveOptionsWidget
QWidget * saveOptionsWidget(QString)
Definition: FileOFF.cc:1367
BaseObject::filename
QString filename() const
return the filename of the object
Definition: BaseObject.cc:706
OFFImporter::setNormal
void setNormal(VertexHandle _vh, int _normalID)
set vertex normal
Definition: OFFImporter.cc:196
FileOFFPlugin::loadOptionsWidget
QWidget * loadOptionsWidget(QString)
Definition: FileOFF.cc:1424
FileOFFPlugin::loadObject
int loadObject(QString _filename)
Loads Object and converts it to a triangle mesh if possible.
Definition: FileOFF.cc:1194
FileOFFPlugin::slotSaveDefault
void slotSaveDefault()
Slot called when user wants to save the given Save options as default.
Definition: FileOFF.cc:1494
FileOFFPlugin::checkDegenerateFace
bool checkDegenerateFace(const std::vector< VertexHandle > &_v)
Check for degenerate faces before adding them.
Definition: FileOFF.cc:862
FileOFFPlugin::handleTrimeshDialog
void handleTrimeshDialog()
Displays a dialog to ask how to load the mesh (triangle, polymesh , autodetect)
Definition: FileOFF.cc:564
FileOFFPlugin::FileOFFPlugin
FileOFFPlugin()
Constructor.
Definition: FileOFF.cc:65
FileOFFPlugin::backupTextureCoordinates
void backupTextureCoordinates(MeshT &_mesh)
backup per vertex/face texture coordinates
Definition: FileOFF.cc:1338
OFFImporter::setVertexColor
void setVertexColor(VertexHandle _vh, int _colorIndex)
set vertex color
Definition: OFFImporter.cc:555
FileOFFPlugin::extendedFaceColorTest
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
FileOFFPlugin::writeMesh
bool writeMesh(std::ostream &_out, MeshT &_mesh, BaseObject &_baseObj)
Writer function.
Definition: FileOFFT_impl.hh:54
FileOFFPlugin::initializePlugin
void initializePlugin()
Initialize Plugin.
Definition: FileOFF.cc:96
Utils
Definition: NumberParsing.cc:46
OFFImporter
Definition: OFFImporter.hh:75
OFFImporter::addTexCoord
int addTexCoord(const Vec2f &_coord)
add texture coordinates
Definition: OFFImporter.cc:104
OpenMesh::HPropHandleT
Definition: Property.hh:496
FileOFFPlugin::getLoadFilters
QString getLoadFilters()
Definition: FileOFF.cc:127
FileOFFPlugin::parseBinary
bool parseBinary(std::istream &_in, OFFImporter &_importer, DataType _type, QString &_objectName)
Parse binary OFF file.
Definition: FileOFF.cc:926
DataType
Predefined datatypes.
Definition: DataTypes.hh:83
OFFImporter::addVertex
VertexHandle addVertex(const Vec3f &_point)
add a vertex with coordinate _point
Definition: OFFImporter.cc:236
BaseObject
Definition: BaseObject.hh:82
OpenMesh::VectorT< float, 3 >
OFFImporter::addColor
int addColor(const Vec4f &_color)
add a color
Definition: OFFImporter.cc:122
OpenMesh::PolyMeshT::update_face_normals
void update_face_normals()
Update normal vectors for all faces.
Definition: PolyMeshT_impl.hh:277
OFFImporter::addOption
void addOption(ObjectOptionsE _option)
add an option
Definition: OFFImporter.cc:528
NumberParsing.hh
DATA_TRIANGLE_MESH
#define DATA_TRIANGLE_MESH
Definition: TriangleMesh.hh:60
BaseObjectData
Definition: BaseObjectData.hh:102
OFFImporter::addFace
int addFace(const VHandles &_indices)
add a face with indices _indices refering to vertices
Definition: OFFImporter.cc:261
FileOFFPlugin::updateUserOptions
void updateUserOptions()
Definition: FileOFF.cc:197
FileOFFPlugin::slotLoadDefault
void slotLoadDefault()
Slot called when user wants to save the given Load options as default.
Definition: FileOFF.cc:1481
FileOFFPlugin::supportedType
DataType supportedType()
Return your supported object type( e.g. DATA_TRIANGLE_MESH )
Definition: FileOFF.cc:139
OFFImporter::removeOption
void removeOption(ObjectOptionsE _option)
remove an option
Definition: OFFImporter.cc:534
PolyMeshObject
Type for a Meshobject containing a poly mesh.
Definition: PolyMesh.hh:65
OpenMesh::PolyMeshT::update_normals
void update_normals()
Compute normals for all primitives.
Definition: PolyMeshT_impl.hh:259
OFFImporter::setFaceColor
void setFaceColor(FaceHandle _fh, int _colorIndex)
set face color
Definition: OFFImporter.cc:595
FileOFFPlugin::getColorType
int getColorType(std::string &_line, bool _texCoordsAvailable)
Get color type.
Definition: FileOFF.cc:877
OpenMesh::VPropHandleT
Definition: Property.hh:482