Developer Documentation

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

- File List
- File Members

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