Developer Documentation
Loading...
Searching...
No Matches
FileOBJ.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#include <ACG/GL/GLState.hh>
44
47
48#include <OpenMesh/Core/IO/IOManager.hh>
49
50#include <OpenFlipper/Utils/Memory/RAMInfo.hh>
51
52#include <QtWidgets>
53
54#include "FileOBJ.hh"
55
56#include <ACG/Utils/SmartPointer.hh>
58
59// Defines for the type handling drop down box
60#define TYPEAUTODETECT 0
61#define TYPEASK 1
62#define TYPEPOLY 2
63#define TYPETRIANGLE 3
64
65using namespace Utils;
66
67//-----------------------------------------------------------------------------
68// help functions
69
70void remove_duplicated_vertices(VHandles& _indices)
71{
72 VHandles::iterator endIter = _indices.end();
73 for (VHandles::iterator iter = _indices.begin(); iter != endIter; ++iter)
74 endIter = std::remove(iter+1, endIter, *(iter));
75
76 _indices.erase(endIter,_indices.end());
77}
78
79//-----------------------------------------------------------------------------
80
83: materialErrors_(0),
84 loadOptions_(0),
85 saveOptions_(0),
86 saveBinary_(0),
87 saveVertexColor_(0),
88 saveFaceColor_(0),
89 saveFaceColorOverride_(0),
90 saveAlpha_(0),
91 saveNormals_(0),
92 saveTexCoords_(0),
93 saveTextures_(0),
94 saveCopyTextures_(0),
95 saveCreateTexFolder_(0),
96 savePrecisionLabel_(0),
97 savePrecision_(0),
98 saveDefaultButton_(0),
99 triMeshHandling_(0),
100 loadVertexColor_(0),
101 loadFaceColor_(0),
102 loadAlpha_(0),
103 loadNormals_(0),
104 loadTexCoords_(0),
105 loadTextures_(0),
106 loadDefaultButton_(0),
107 forceTriangleMesh_(false),
108 forcePolyMesh_(false),
109 textureIndexPropFetched_(false),
110 trimeshOptions_(OBJImporter::NONE)
111{
112}
113
114//-----------------------------------------------------------------------------------------------------
115
118
119//-----------------------------------------------------------------------------------------------------
120
122 return QString( tr("Alias/Wavefront ( *.obj )") );
123};
124
125//-----------------------------------------------------------------------------------------------------
126
128 return QString( tr("Alias/Wavefront ( *.obj )") );
129};
130
131//-----------------------------------------------------------------------------------------------------
132
135
136 #ifdef ENABLE_BSPLINECURVE_SUPPORT
137 type |= DATA_BSPLINE_CURVE;
138 #endif
139
140 #ifdef ENABLE_BSPLINESURFACE_SUPPORT
141 type |= DATA_BSPLINE_SURFACE;
142 #endif
143
144 return type;
145}
146
147//-----------------------------------------------------------------------------
148
149bool FileOBJPlugin::readMaterial(QString _filename, OBJImporter& _importer)
150{
151 QString line;
152 QString keyWrd;
153 QString textureName;
154 line.clear();
155 keyWrd.clear();
156 textureName.clear();
157
158 QString matName;
159 matName.clear();
160 Material mat;
161 mat.cleanup();
162 double f1,f2,f3;
163 f1 = 0;
164 f2 = 0;
165 f3 = 0;
166 int i;
167 bool insideDefintion;
168 insideDefintion = false;
169 int textureId;
170 textureId = 1;
171
172
173 //open stream
174 QFile matFile(_filename);
175 if (!matFile.open(QFile::ReadOnly))
176 {
177 emit log(LOGERR, tr("readMaterial : cannot open file %1").arg(_filename));
178 return false;
179 }
180
181 QTextStream matStream(&matFile);
182 if ( matStream.status()!=QTextStream::Ok ){
183 emit log(LOGERR, tr("readMaterial : cannot open stream %1").arg(_filename) );
184 return false;
185 }
186
187 //clear temp material
188 mat.cleanup();
189
190 //parse material file
191 while( matStream.status() == QTextStream::Ok && !matStream.atEnd() )
192 {
193 line = matStream.readLine();
194 if ( matStream.status() != QTextStream::Ok ){
195 emit log(LOGERR, tr("readMaterial : Warning! Could not read file properly!"));
196 return false;
197 }
198
199 if ( line.isEmpty() )
200 continue;
201
202 QTextStream stream(&line);
203
204 stream >> keyWrd;
205
206 if( ( line[0].isSpace() && line[0] != QLatin1Char('\t') ) || line[0] == QLatin1Char('#') )
207 {
208 if (insideDefintion && !matName.isEmpty() && mat.is_valid())
209 {
210 _importer.materials()[matName.toStdString()] = mat;
211 mat.cleanup();
212 }
213 }
214
215 else if (keyWrd == QLatin1String("newmtl")) // begin new material definition
216 {
217 stream >> matName;
218 insideDefintion = true;
219 }
220
221 else if (keyWrd == QLatin1String("Kd")) // diffuse color
222 {
223 f1 = getFloat(stream);
224 f2 = getFloat(stream);
225 f3 = getFloat(stream);
226
227 if( stream.status()==QTextStream::Ok )
228 mat.set_Kd(f1,f2,f3);
229 }
230
231 else if (keyWrd == QLatin1String("Ka")) // ambient color
232 {
233 f1 = getFloat(stream);
234 f2 = getFloat(stream);
235 f3 = getFloat(stream);
236
237 if( stream.status()==QTextStream::Ok )
238 mat.set_Ka(f1,f2,f3);
239 }
240
241 else if (keyWrd == QLatin1String("Ks")) // specular color
242 {
243 f1 = getFloat(stream);
244 f2 = getFloat(stream);
245 f3 = getFloat(stream);
246
247 if( stream.status()==QTextStream::Ok )
248 mat.set_Ks(f1,f2,f3);
249 }
250
251 else if (keyWrd == QLatin1String("Ke")) // emission color
252 {
253 f1 = getFloat(stream);
254 f2 = getFloat(stream);
255 f3 = getFloat(stream);
256
257 if( stream.status()==QTextStream::Ok )
258 mat.set_Ke(f1,f2,f3);
259 }
260
261
262 else if (keyWrd == QLatin1String("illum")) // diffuse/specular shading model
263 {
264 stream >> i;
265
266 if(stream.status() == QTextStream::Ok)
267 mat.set_illum(i);
268 }
269
270 else if (keyWrd == QLatin1String("Ns")) // Shininess;
271 {
272 f1 = getFloat(stream);
273
274 if(stream.status() == QTextStream::Ok)
275 mat.set_Ns(f1);
276 }
277
278 else if (keyWrd == QLatin1String("Ni")) // Refractive index
279 {
280 f1 = getFloat(stream);
281
282 if(stream.status() == QTextStream::Ok)
283 mat.set_Ni(f1);
284 }
285
286 else if (keyWrd == QLatin1String("Tr")) // transparency value
287 {
288 f1 = getFloat(stream);
289
290 if(stream.status() == QTextStream::Ok)
291 mat.set_Tr(f1);
292 }
293
294 else if (keyWrd == QLatin1String("d")) // material dissolve. The result does not depend upon the thickness of the object, no real transparency
295 // ignored when Tr exists
296 {
297 f1 = getFloat(stream);
298
299 if(stream.status() == QTextStream::Ok && !mat.has_Tr())
300 mat.set_Tr(f1);
301 }
302#if 0
303 else if (keyWrd == QLatin1String("map_")) // map images
304 {
305 // map_Ks, specular map
306 // map_Ka, ambient map
307 // map_Bump, bump map
308 // map_d, opacity map
309 ; // just skip this
310 }
311#endif
312 else if (keyWrd == QLatin1String("map_Kd") ) {
313 // Get the rest of the line, removing leading or trailing spaces
314 // This will define the filename of the texture
315 textureName = stream.readLine();
316 textureName = textureName.trimmed();
317 if ( ! textureName.isEmpty() )
318 mat.set_map_Kd( textureName.toStdString(), textureId++ );
319 }
320
321 if ( matStream.status() == QTextStream::Ok && insideDefintion && mat.is_valid() && !matName.isEmpty())
322 _importer.materials()[matName.toStdString()] = mat;
323 }
324
325 emit log( tr("%1 materials loaded.").arg( _importer.materials().size() ) );
326
327 return true;
328}
329
330//-----------------------------------------------------------------------------
331
332void FileOBJPlugin::createAllGroupObjects(OBJImporter& _importer) {
333
334 for(unsigned int i = 0; i < _importer.numGroups(); ++i) {
335
336 // Get group name
337 QString name = _importer.groupName(i);
339
340 if ( _importer.isTriangleMesh( i ) ){
341
342 // add a triangle mesh
343 int id = -1;
344 emit addEmptyObject(DATA_TRIANGLE_MESH, id);
345
346 BaseObjectData* object(0);
347
348 if (PluginFunctions::getObject(id, object)) {
349
350 _importer.setObject(object, i);
351
352 object->setPath(_importer.path());
353 object->setName(name);
354 }
355
356 } else if (_importer.isPolyMesh( i )) {
357
358 int id = -1;
359 emit addEmptyObject(DATA_POLY_MESH, id);
360
361 BaseObjectData* object(0);
362
363 if (PluginFunctions::getObject(id, object)) {
364
365 _importer.setObject(object, i);
366
367 object->setPath(_importer.path());
368 object->setName(name);
369 }
370 }
371
372#ifdef ENABLE_BSPLINECURVE_SUPPORT
373
374 else if (_importer.isCurve( i )) {
375
376 int id = -1;
377 emit addEmptyObject(DATA_BSPLINE_CURVE, id);
378
379 BaseObjectData* object(0);
380
381 if (PluginFunctions::getObject(id, object)) {
382
383 _importer.setObject(object, i);
384
385 object->setPath(_importer.path());
386 object->setName(name);
387 }
388 }
389
390#endif
391
392#ifdef ENABLE_BSPLINESURFACE_SUPPORT
393
394 else if (_importer.isSurface( i )) {
395
396 int id = -1;
397 emit addEmptyObject(DATA_BSPLINE_SURFACE, id);
398
399 BaseObjectData* object(0);
400
401 if (PluginFunctions::getObject(id, object)) {
402
403 _importer.setObject(object, i);
404
405 object->setPath(_importer.path());
406 object->setName(name);
407 }
408 }
409
410#endif
411
412 //force gui settings
413 if (OpenFlipper::Options::gui() && loadOptions_ != 0) {
414
415 if (!loadFaceColor_->isChecked())
416 _importer.setOption(OBJImporter::FORCE_NOCOLOR, i);
417
418 if (!loadNormals_->isChecked())
419 _importer.setOption(OBJImporter::FORCE_NONORMALS, i);
420
421 if (!loadTexCoords_->isChecked() || !loadTextures_->isChecked())
422 _importer.setOption(OBJImporter::FORCE_NOTEXTURES, i);
423
424 }
425 }
426}
427
429
430 QFileInfo fi(_name);
431
432 QString n = fi.baseName();
433
434 _name = n.trimmed() + ".obj";
435}
436
438template <class MeshT>
440
441 // Create a backup of the original per Vertex texture Coordinates
442 if (_mesh.has_vertex_texcoords2D()) {
443
445 if (!_mesh.get_property_handle(oldVertexCoords, "Original Per Vertex Texture Coords"))
446 _mesh.add_property(oldVertexCoords, "Original Per Vertex Texture Coords");
447
448 for (auto v_it : _mesh.vertices())
449 _mesh.property(oldVertexCoords, v_it) = _mesh.texcoord2D(v_it);
450
451 }
452
453 // Create a backup of the original per Face texture Coordinates
454 if (_mesh.has_halfedge_texcoords2D()) {
455
457 if (!_mesh.get_property_handle(oldHalfedgeCoords,"Original Per Face Texture Coords"))
458 _mesh.add_property(oldHalfedgeCoords,"Original Per Face Texture Coords");
459
460 for (auto he_it : _mesh.halfedges())
461 _mesh.property(oldHalfedgeCoords, he_it) = _mesh.texcoord2D(he_it);
462
463 }
464}
465
466
467//add textures to the mesh
468void FileOBJPlugin::addTextures(OBJImporter& _importer, int _objectID ){
469
470 // TODO : If only one Texture, use single Texturing mode
471 if ( true ) {
472
473 BaseObject* object = _importer.object(_objectID);
474
475 if (!object)
476 return;
477
478 std::map< int,int > newMapping;
479 // zero ( no texture ) always maps to to zero
480 newMapping[0]=0;
481
482 const std::vector<std::string> matNames = _importer.usedMaterials( _objectID );
483
484 for (unsigned int i=0; i < matNames.size(); i++){
485
486 Material& material = _importer.materials()[ matNames[i] ];
487
488 int textureId = -1;
489
490 QString textureBlock = QString( material.map_Kd().c_str());
491
492
493
494#if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
495 QStringList options = textureBlock.split(" ",QString::SkipEmptyParts);
496#else
497 QStringList options = textureBlock.split(" ",Qt::SkipEmptyParts);
498#endif
499
500 while ( options.size() > 1 ) {
501 if ( options[0] == "-blendu" ) {
502 options.pop_front();
503 options.pop_front();
504 } else if ( options[0] == "-blendv" ) {
505 options.pop_front();
506 options.pop_front();
507 } else if ( options[0] == "-cc" ) {
508 options.pop_front();
509 options.pop_front();
510 } else if ( options[0] == "-clamp" ) {
511 options.pop_front();
512 options.pop_front();
513 } else if ( options[0] == "-mm" ) {
514 options.pop_front();
515 options.pop_front();
516 options.pop_front();
517 } else if ( options[0] == "-o" ) {
518 options.pop_front();
519 options.pop_front();
520 options.pop_front();
521 options.pop_front();
522 } else if ( options[0] == "-s" ) {
523 options.pop_front();
524 options.pop_front();
525 options.pop_front();
526 options.pop_front();
527 } else if ( options[0] == "-t" ) {
528 options.pop_front();
529 options.pop_front();
530 options.pop_front();
531 options.pop_front();
532 } else if ( options[0] == "-texres" ) {
533 options.pop_front();
534 options.pop_front();
535 } else {
536 break;
537 }
538 }
539
540 QString fullName = _importer.path() + QDir::separator() + options.join(" ");
541
542 QFileInfo info(fullName);
543 if ( info.exists() ) {
544 emit addMultiTexture("OBJ Data", info.baseName().trimmed(), fullName, object->id(), textureId );
545 } else {
546 emit log(LOGWARN, tr("Unable to load texture image %1").arg( QString(material.map_Kd().c_str()) ) );
547 addMultiTexture("OBJ Data","Unknown Texture image " + QString::number(textureId), "unknown.png", object->id(), textureId );
548 }
549
550 newMapping[ material.map_Kd_index() ] = textureId;
551 }
552
553 //now map all texture indices to the real texture indices used in OpenFlipper
554
555 OpenMesh::FPropHandleT< int > indexProperty;
556
557 //handle PolyMeshes
558 PolyMeshObject* polyMeshObj = dynamic_cast< PolyMeshObject* > (object);
559
560 if ( polyMeshObj ){
561
562 PolyMesh& mesh = *(polyMeshObj->mesh());
563
564 if (! mesh.get_property_handle(indexProperty,TEXTUREINDEX) )
565 return;
566
567 for (auto f_it : mesh.faces())
568 mesh.property(indexProperty, f_it) = newMapping[ mesh.property(indexProperty, f_it) ];
569
571
572 return;
573 }
574
575 //handle new TriMeshes
576 TriMeshObject* triMeshObj = dynamic_cast< TriMeshObject* > (object);
577
578 if ( triMeshObj ){
579
580 TriMesh& mesh = *(triMeshObj->mesh());
581
582 if (! mesh.get_property_handle(indexProperty,TEXTUREINDEX) )
583 return;
584
585 for (auto f_it : mesh.faces())
586 mesh.property(indexProperty, f_it) = newMapping[ mesh.property(indexProperty, f_it) ];
587
589
590 return;
591 }
592 }
593}
594
595void FileOBJPlugin::readOBJFile(QByteArray& _bufferedFile, QString _filename, OBJImporter& _importer)
596{
597 QString path = QFileInfo(_filename).absolutePath();
598 ptr::shared_ptr<QTextStream> streamPointer;
599 ptr::shared_ptr<QFile> sourceFile;
600
602 if (_bufferedFile.isNull())
603 {
604 sourceFile.reset(new QFile(_filename) );
605 if(!sourceFile->open(QFile::ReadOnly))
606 {
607 emit log(LOGERR, tr("readOBJFile : cannot open file %1").arg(_filename) );
608 return;
609 }
610 //use the QTextStream and QString objects, since they seem to be more efficient when parsing strings.
611 //especially regarding copy operations.
612 streamPointer.reset( new QTextStream(sourceFile.get()));
613 }
614 else
615 {
616 streamPointer.reset( new QTextStream(&_bufferedFile));
617 }
618 QTextStream input(streamPointer->device());
619 input.seek(0);
620 QTextStream stream;
621 QTextStream lineData;
622 QTextStream tmp;
623 if ( input.status() != QTextStream::Ok){
624 emit log(LOGERR, tr("readOBJFile : cannot read file %1 is the file corrupt?").arg(_filename) );
625 return;
626 }
627
628 QString currentFileName = QFileInfo(_filename).fileName() ;
629
630 ReaderMode mode = NONE;
631
632 QString line;
633 QString keyWrd;
634 QString nextKeyWrd = QLatin1String("");
635
636#ifdef ENABLE_BSPLINECURVE_SUPPORT
637 unsigned int curveCount = 0;
638#endif
639
640#ifdef ENABLE_BSPLINESURFACE_SUPPORT
641 unsigned int surfaceCount = 0;
642#endif
643
644 int deg;
645
646 std::vector<VertexHandle> vhandles;
647 std::vector<int> face_texcoords;
648 QString matname;
649 QString lastMaterial;
650
651#if defined (ENABLE_BSPLINECURVE_SUPPORT) || defined (ENABLE_BSPLINESURFACE_SUPPORT)
652 std::vector< int > cpIndices;
653 std::vector< double > knotsU,knotsV;
654#endif
655
656 int faceCount = 0;
657
658 // We have to keep track of the already read number of vertices to resolve relative (negative indices)
659 int currentVertexCount = 0;
660
661 // We have to keep track of the already read number of Texture coordinates to resolve relative (negative indices)
662 int currentTextureCoordCount = 0;
663
664 // We have to keep track of the already read number of normals to resolve relative (negative indices)
665 int currentNormalCount = 0;
666
667 // keeps track if faces belong to a group or the default group
668 bool inGroup = false;
669 // keeps track if the first face of a mesh has been read yet or not
670 bool firstFace = true;
671
672 _importer.setPath( path );
673
674 // Set filename for default mesh
675 _importer.setGroupName(0, currentFileName);
676
677 // Now add all meshes for every group (if exists)
678 createAllGroupObjects(_importer);
679
680 bool bSuppNoVertCoord = false;
681 bool bSuppTooManyTexCoord = false;
682 bool bSuppTooManyNormal = false;
683 bool bSuppErrorSettingTexCoord = false;
684
685 while( !input.atEnd() )
686 {
687 line=input.readLine();
688 if ( input.status() == QTextStream::ReadCorruptData ){
689 emit log(LOGERR, tr("readOBJFile : Warning! Could not read file properly!"));
690 return;
691 }
692
693 // Trim Both leading and trailing spaces
694 line = line.trimmed();
695
696 // comment
697 if ( line.isEmpty() || line[0] == QLatin1Char('#') || line[0].isSpace() ) {
698 continue;
699 }
700
701 stream.setString(&line,QIODevice::ReadOnly);
702
703 //unless the keyWrd for the new line is not determined by the previous line
704 //read it from stream
705 if (nextKeyWrd == QLatin1String(""))
706 stream >> keyWrd;
707 else {
708 keyWrd = nextKeyWrd;
709 nextKeyWrd = QLatin1String("");
710 }
711
712 // material file
713 if (mode == NONE && keyWrd == QLatin1String("mtllib"))
714 {
715 QString matString;
716
717 // This will define the filename of the texture
718 matString = stream.readLine();
719
720 QString matFile = path + QDir::separator() + matString.trimmed();
721
722 emit log( tr("Loading material file: %1").arg( matFile ) );
723
724 readMaterial( matFile, _importer );
725 }
726
727 // usemtl
728 else if (mode == NONE && keyWrd == QLatin1String("usemtl"))
729 {
730 stream >> matname;
731 if ( _importer.materials().find(matname.toStdString())==_importer.materials().end() )
732 {
733 emit log( LOGERR, tr("Warning! Material '%1' not defined in material file").arg( matname ) );
734 matname=QLatin1String("");
735
736 }else{
737
738 Material& mat = _importer.materials()[matname.toStdString()];
739
740 if ( mat.has_Texture() ){
741
742 //add object if not already there
743 _importer.useMaterial( matname.toStdString() );
744
745 }
746
747 lastMaterial = matname;
748 }
749 }
750 else if (mode == NONE && keyWrd == QLatin1String("v"))
751 {
752 if (!firstFace)
753 firstFace = true;
754
755 currentVertexCount++;
756 }
757 // texture coord
758 else if (mode == NONE && keyWrd == QLatin1String("vt"))
759 {
760 if (!firstFace)
761 firstFace = true;
762
763 // New texture coordinate read so increase counter
764 currentTextureCoordCount++;
765
766 auto u = getFloat(stream);
767 auto v = getFloat(stream);
768
769 if ( stream.status() == QTextStream::Ok ){
770
771 _importer.addTexCoord( OpenMesh::Vec2f(u, v) );
772
773 }else{
774
775 emit log( LOGERR, tr("Could not add TexCoord. Possible NaN or Inf?\nOnly single 2D texture coordinate per vertex allowed"));
776 }
777 }
778
779
780 // normal
781 else if (mode == NONE && keyWrd == QLatin1String("vn"))
782 {
783 if (!firstFace)
784 firstFace = true;
785
786 // New normal read so increase counter
787 currentNormalCount++;
788
789 auto x = getDouble(stream);
790 auto y = getDouble(stream);
791 auto z = getDouble(stream);
792
793 if ( stream.status() == QTextStream::Ok ){
794 _importer.addNormal( OpenMesh::Vec3d(x,y,z) );
795 }else{
796 emit log( LOGERR, tr("Could not read normal. Possible NaN or Inf?"));
797 }
798 }
799
800 // degree (for curves)
801 else if (mode == NONE && keyWrd == QLatin1String("deg"))
802 {
803 stream >> deg;
804
805 if ( stream.status() == QTextStream::Ok )
806 _importer.setDegreeU( deg );
807
808 stream >> deg;
809
810 if ( stream.status() == QTextStream::Ok )
811 _importer.setDegreeV( deg );
812 }
813
814 // group
815 else if (mode == NONE && keyWrd == QLatin1String("g")){
816 if (!firstFace)
817 firstFace = true;
818
819 QString groupName;
820 groupName = stream.readLine();
821
822 if(faceCount == 0) {
823 currentFileName = groupName;
824 }
825
826 int id = _importer.groupId(groupName);
827 if(id == -1) {
828 std::cerr << "Error: Group has not been added before!" << std::endl;
829 return;
830 }
831 _importer.setCurrentGroup(id);
832 inGroup = true;
833
834 faceCount = 0;
835
836 if (lastMaterial != "" ) {
837 _importer.useMaterial(lastMaterial.toStdString());
838 }
839 }
840
841 // face
842 else if (mode == NONE && keyWrd == QLatin1String("f"))
843 {
844 if (firstFace) {
845 // store faces in the default Group if we aren't in a group already
846 if (!inGroup)
847 _importer.setCurrentGroup(0);
848
849 firstFace = false;
850 }
851
852 int component(0), nV(0);
853 int value;
854
855 vhandles.clear();
856 face_texcoords.clear();
857
858 // read full line after detecting a face
859 QString faceLine;
860 faceLine = stream.readLine();
861 lineData.setString(&faceLine);
862
863 // work on the line until nothing left to read
864 while ( !lineData.atEnd() )
865 {
866 // read one block from the line ( vertex/texCoord/normal )
867 QString vertex;
868 lineData >> vertex;
869
870 do{
871
872 //get the component (vertex/texCoord/normal)
873 int found=vertex.indexOf(QLatin1String("/"));
874
875 // parts are seperated by '/' So if no '/' found its the last component
876 if( found != -1 ){
877
878 // read the index value
879 QString vertexEntry = vertex.left(found);
880 tmp.setString( &vertexEntry );
881
882 // If we get an empty string this property is undefined in the file
883 if ( vertexEntry.isEmpty() ) {
884 // Switch to next field
885 vertex = vertex.right(vertex.length()-(found+1));
886
887 // Now we are at the next component
888 ++component;
889
890 // Skip further processing of this component
891 continue;
892 }
893
894 // Read current value
895 tmp >> value;
896
897 // remove the read part from the string
898 vertex = vertex.right(vertex.length()-(found+1));
899
900 } else {
901
902 // last component of the vertex, read it.
903 tmp.setString( &vertex );
904 tmp >> value;
905
906 // Clear vertex after finished reading the line
907 vertex=QLatin1String("");
908
909 // Nothing to read here ( garbage at end of line )
910 if ( tmp.status() != QTextStream::Ok ) {
911 continue;
912 }
913 }
914
915 // store the component ( each component is referenced by the index here! )
916 switch (component)
917 {
918 case 0: // vertex
919 if ( value < 0 ) {
920 // Calculation of index :
921 // -1 is the last vertex in the list
922 // As obj counts from 1 and not zero add +1
923 value = currentVertexCount + value + 1;
924 }
925
926 // Obj counts from 1 and not zero .. array counts from zero therefore -1
927 vhandles.push_back( value-1 );
928 break;
929
930 case 1: // texture coord
931 if ( value < 0 ) {
932 // Calculation of index :
933 // -1 is the last vertex in the list
934 // As obj counts from 1 and not zero add +1
935 value = currentTextureCoordCount + value + 1;
936 }
937 if (vhandles.empty())
938 {
939 if(!bSuppNoVertCoord) {
940 emit log (LOGWARN, tr("Texture coordinates defined, but no vertex coordinates found!"));
941 bSuppNoVertCoord=true;
942 }
943 break;
944 }
945 if ((unsigned int)(value-1) >= _importer.n_texCoords())
946 {
947 if(!bSuppTooManyTexCoord) {
948 emit log(LOGWARN, tr("Too many texcoords defined, skipping the rest"));
949 bSuppTooManyTexCoord=true;
950 }
951 break;
952 }
953
954 if ( _importer.n_texCoords() > 0 ) {
955 // Obj counts from 1 and not zero .. array counts from zero therefore -1
956 _importer.setVertexTexCoord( vhandles.back(), value-1 );
957 face_texcoords.push_back( value-1 );
958 } else if(bSuppErrorSettingTexCoord){
959 emit log( LOGERR, tr("Error setting Texture coordinates") );
960 bSuppErrorSettingTexCoord=true;
961 }
962
963 break;
964
965 case 2: // normal
966 if ( value < 0 ) {
967 // Calculation of index :
968 // -1 is the last vertex in the list
969 // As obj counts from 1 and not zero add +1
970 value = currentNormalCount + value + 1;
971 }
972
973 if (vhandles.empty())
974 {
975 if(!bSuppNoVertCoord) {
976 emit log (LOGWARN, tr("Texture coordinates defined, but no vertex coordinates found!"));
977 bSuppNoVertCoord=true;
978 }
979 break;
980 }
981
982 if ((unsigned int)(value-1) >= _importer.n_normals())
983 {
984 if(!bSuppTooManyNormal)
985 {
986 emit log (LOGWARN, tr("Too many normals defined, skipping the rest"));
987 bSuppTooManyNormal=true;
988 }
989 break;
990 }
991
992 // Obj counts from 1 and not zero .. array counts from zero therefore -1
993 _importer.setNormal(vhandles.back(), value-1);
994 break;
995 }
996
997 // Prepare for reading next component
998 ++component;
999
1000 // Read until line does not contain any other info
1001 } while ( !vertex.isEmpty() );
1002
1003 component = 0;
1004 nV++;
1005 }
1006
1007 // remove vertices which can lead to degenerated faces
1008 remove_duplicated_vertices(vhandles);
1009
1010 // from spec: A minimum of three vertices are required.
1011 if( vhandles.size() > 2 ){
1012
1013 if ( !face_texcoords.empty() )
1014 //if we have texCoords add face+texCoords
1015 _importer.addFace(vhandles, face_texcoords );
1016 else
1017 //otherwise just add the face
1018 _importer.addFace(vhandles);
1019
1020 faceCount++;
1021 }
1022
1023
1024 //add material to the last added face(s)
1025 //if polygons get triangulated this can be more than one face
1026 _importer.addMaterial( matname.toStdString() );
1027 }
1028
1029#ifdef ENABLE_BSPLINECURVE_SUPPORT
1030 // param
1031 else if ( (mode == CURVE && keyWrd == QLatin1String("parm")) || (mode == CURVE && keyWrd == QLatin1String("parm_add")) ){
1032
1033 //get curve knots
1034 QString paramLine;
1035 QString tmp;
1036
1037 paramLine = stream.readLine();
1038
1039 // value may contain a / as line separator
1040 if ( paramLine.endsWith(QLatin1String("\\"))){
1041 paramLine = paramLine.left( paramLine.length()-1);
1042 nextKeyWrd = QLatin1String("parm_add");
1043 }
1044
1045 lineData.setString( &paramLine );
1046
1047 if ( keyWrd != QLatin1String("parm_add"))
1048 lineData >> tmp; //push the first u out
1049
1050 // work on the line until nothing left to read
1051 while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1052 {
1053
1054 double knot;
1055
1056 knot = getDouble(lineData);
1057
1058 if ( lineData.status() == QTextStream::Ok )
1059 knotsU.push_back( knot );
1060 }
1061 }
1062
1063 // curve
1064 else if ( (mode == NONE && keyWrd == QLatin1String("curv")) || (mode == CURVE && keyWrd == QLatin1String("curv_add")) ){
1065 if (!firstFace)
1066 firstFace = true;
1067
1068 inGroup = false;
1069
1070 mode = CURVE;
1071
1072 if ( keyWrd == QLatin1String("curv") )
1073 {
1074 int id = _importer.getCurveGroupId(curveCount);
1075 if(id == -1) {
1076 std::cerr << "Error: Group has not been added before!" << std::endl;
1077 return;
1078 }
1079 _importer.setCurrentGroup(id);
1080 curveCount++;
1081 }
1082
1083 //get curve control points
1084 QString curveLine;
1085
1086 curveLine = stream.readLine();
1087
1088 // value may contain a / as line separator
1089 if ( curveLine.endsWith(QLatin1String("\\"))){
1090 curveLine = curveLine.left(curveLine.length()-1);
1091 nextKeyWrd = QLatin1String("curv_add");
1092 }
1093
1094 lineData.setString( &curveLine );
1095
1096 // Read knots at the beginning before the indices
1097 if ( keyWrd == QLatin1String("curv") ) {
1098 // skip next two doubles in stream
1099 getDouble(lineData);
1100 getDouble(lineData);
1101 }
1102
1103
1104
1105 // work on the line until nothing left to read
1106 while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1107 {
1108 int index = 0;
1109
1110 lineData >> index;
1111
1112 if ( index < 0 ) {
1113 // Calculation of index :
1114 // -1 is the last vertex in the list
1115 // As obj counts from 1 and not zero add +1
1116 index = currentVertexCount + index + 1;
1117 }
1118
1119 if ( lineData.status()==QTextStream::Ok )
1120 cpIndices.push_back( index -1 );
1121 }
1122 }
1123
1124 // end
1125 else if (mode == CURVE && keyWrd == QLatin1String("end")){
1126
1127 if ( _importer.isCurve( _importer.currentGroup() ) ){
1128 // set up the spline curve
1129 _importer.currentCurve()->set_degree( _importer.degreeU() );
1130 _importer.currentCurve()->autocompute_knotvector(false);
1131
1132 // add the control points
1133 std::vector< ACG::Vec3d > controlPolygon;
1134
1135 for (unsigned int i = 0; i < cpIndices.size(); ++i)
1136 controlPolygon.push_back( (ACG::Vec3d) _importer.vertex( cpIndices[i] ) );
1137
1138 _importer.currentCurve()->set_control_polygon( controlPolygon );
1139
1140 _importer.currentCurve()->set_knots(knotsU);
1141 }
1142
1143 cpIndices.clear();
1144 knotsU.clear();
1145
1146 mode = NONE;
1147 }
1148#endif
1149
1150#ifdef ENABLE_BSPLINESURFACE_SUPPORT
1151 // param
1152 else if ( (mode == SURFACE && keyWrd == QLatin1String("parm")) || (mode == SURFACE && keyWrd == QLatin1String("parm_add")) ){
1153
1154 //get surface knots
1155 QString paramLine;
1156 QString tmp;
1157
1158 paramLine = stream.readLine();
1159
1160 // value may contain a / as line separator
1161 if ( paramLine.endsWith(QLatin1String("\\"))){
1162 paramLine = paramLine.left(paramLine.length()-1);
1163 nextKeyWrd = QLatin1String("parm_add");
1164 }
1165
1166 lineData.setString( &paramLine );
1167
1168 if ( keyWrd == QLatin1String("parm_add_u"))
1169 tmp = QLatin1String("u");
1170 else if ( keyWrd == QLatin1String("parm_add_v"))
1171 tmp = QLatin1String("v");
1172 else
1173 lineData >> tmp; //get the direction (u or v)
1174
1175 std::vector< double >* knots;
1176
1177 //Decide if these are knots in U or V direction
1178 if (tmp == QLatin1String("u"))
1179 knots = &knotsU;
1180 else
1181 knots = &knotsV;
1182
1183 if (nextKeyWrd != QLatin1String(""))
1184 nextKeyWrd += QLatin1String("_") + tmp;
1185
1186 // work on the line until nothing left to read
1187 while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1188 {
1189
1190 double knot;
1191
1192 knot = getDouble(lineData);
1193
1194 if ( lineData.status()==QTextStream::Ok ) {
1195 knots->push_back( knot );
1196 }
1197 }
1198 }
1199
1200 // surface
1201 else if ( (mode == NONE && keyWrd == QLatin1String("surf")) || (mode == SURFACE && keyWrd == QLatin1String("surf_add")) ){
1202 if (!firstFace)
1203 firstFace = true;
1204
1205 inGroup = false;
1206
1207 mode = SURFACE;
1208
1209 if ( keyWrd == QLatin1String("surf") )
1210 {
1211 int id = _importer.getSurfaceGroupId(surfaceCount);
1212 if(id == -1) {
1213 std::cerr << "Error: Group has not been added before!" << std::endl;
1214 return;
1215 }
1216 _importer.setCurrentGroup(id);
1217 surfaceCount++;
1218 }
1219
1220 //get surface control points
1221 QString surfLine;
1222
1223 surfLine = stream.readLine();
1224
1225 // value may contain a / as line separator
1226 if ( surfLine.endsWith(QLatin1String("\\"))){
1227 surfLine = surfLine.left(surfLine.length()-1);
1228 nextKeyWrd = QLatin1String("surf_add");
1229 }
1230
1231 lineData.setString( &surfLine );
1232
1233 // work on the line until nothing left to read
1234 while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1235 {
1236 int index = 0;
1237
1238 lineData >> index;
1239
1240 if ( index < 0 ) {
1241 // Calculation of index :
1242 // -1 is the last vertex in the list
1243 // As obj counts from 1 and not zero add +1
1244 index = currentVertexCount + index + 1;
1245 }
1246
1247 if ( lineData.status()==QTextStream::Ok )
1248 cpIndices.push_back( index -1 );
1249 }
1250 }
1251
1252 // end
1253 else if (mode == SURFACE && keyWrd == QLatin1String("end")){
1254
1255 if ( _importer.isSurface( _importer.currentGroup() ) ){
1256
1257 // remove first 4 entries since they are the first and last knot (for both direction)
1258 cpIndices.erase(cpIndices.begin());
1259 cpIndices.erase(cpIndices.begin());
1260 cpIndices.erase(cpIndices.begin());
1261 cpIndices.erase(cpIndices.begin());
1262
1263 // set up the spline surface
1264 _importer.currentSurface()->set_degree( _importer.degreeU(), _importer.degreeV() );
1265
1266 // compute number of control points in m and in n direction
1267 int dimU = knotsU.size() - _importer.degreeU() - 1;
1268 int dimV = knotsV.size() - _importer.degreeV() - 1;
1269
1270 // add the control points
1271 std::vector< ACG::Vec3d > controlPolygon;
1272
1273 for (int i = 0; i < dimU; ++i)
1274 {
1275 controlPolygon.clear();
1276
1277 for (int j = 0; j < dimV; ++j){
1278 controlPolygon.push_back( (ACG::Vec3d) _importer.vertex( cpIndices[dimU * j + i] ) );
1279 }
1280
1281 _importer.currentSurface()->add_vector_m(controlPolygon);
1282 }
1283
1284 _importer.currentSurface()->set_knots_m(knotsU);
1285 _importer.currentSurface()->set_knots_n(knotsV);
1286
1287
1288 }
1289
1290 cpIndices.clear();
1291 knotsU.clear();
1292 knotsV.clear();
1293
1294 mode = NONE;
1295 }
1296#endif
1297
1298 }
1299 //checks, if an object with a specified type was added. if not, point cloud was read
1300 bool isType = faceCount != 0;
1301
1302#ifdef ENABLE_BSPLINECURVE_SUPPORT
1303 isType = isType || curveCount != 0;
1304#endif
1305
1306#ifdef ENABLE_BSPLINESURFACE_SUPPORT
1307 isType = isType || surfaceCount != 0;
1308#endif
1309
1310 // we have only read points so far and no faces or modes
1311 // treat them as a polymesh
1312 if (!isType && currentVertexCount != 0 ) {
1313 _importer.forceMeshType( OBJImporter::POLYMESH ); //actually it is a pointcloud
1314 if (!inGroup)
1315 _importer.setCurrentGroup(0);
1316 }
1317
1318}
1319
1321void FileOBJPlugin::checkTypes(QByteArray& _bufferedFile, QString _filename, OBJImporter& _importer, QStringList& _includes)
1322{
1323 ptr::shared_ptr<QTextStream> streamPointer;
1324 ptr::shared_ptr<QFile> sourceFile;
1325 //setup filestream if not in memory
1326 if (_bufferedFile.isNull() || _bufferedFile.isEmpty())
1327 {
1328
1329 sourceFile.reset(new QFile(_filename));
1330 if(!sourceFile->open(QFile::ReadOnly))
1331 {
1332 emit log(LOGERR, tr("readOBJFile : cannot open file %1 while checking Types").arg(_filename) );
1333 return;
1334 }
1335 streamPointer.reset(new QTextStream(sourceFile.get()));
1336 }
1337 else
1338 {
1339 streamPointer.reset(new QTextStream(&_bufferedFile));
1340 }
1341 QTextStream input(streamPointer->device());
1342 QTextStream stream;
1343 QTextStream lineData;
1344 QTextStream tmp;
1345
1346 if ( input.status()!=QTextStream::Ok ){
1347 emit log(LOGERR, tr("readOBJFile : cannot read file %1 while checking Types (is the file corrupt?)").arg(_filename) );
1348 return;
1349 }
1350
1351 ReaderMode mode = NONE;
1352
1353 QString line;
1354 QString keyWrd;
1355 QString nextKeyWrd = QLatin1String("");
1356
1357#ifdef ENABLE_BSPLINECURVE_SUPPORT
1358 unsigned int curveCount = 0;
1359#endif
1360
1361#ifdef ENABLE_BSPLINESURFACE_SUPPORT
1362 unsigned int surfaceCount = 0;
1363#endif
1364
1365 double x, y, z;
1366 int faceCount = 0;
1367
1368 int PolyMeshCount = 0;
1369 int TriMeshCount = 0;
1370
1371 OBJImporter::ObjectOptions options = OBJImporter::NONE;
1372
1373 // keeps track if faces belong to a group or the default group
1374 bool inGroup = false;
1375 // keeps track if the first face of a mesh has been read yet or not
1376 bool firstFace = true;
1377
1378#if defined ENABLE_BSPLINECURVE_SUPPORT || defined ENABLE_BSPLINESURFACE_SUPPORT
1379 QString currentGroupName;
1380 int parentId = -1;
1381#endif
1382
1383 _importer.setObjectOptions(OBJImporter::NONE);
1384
1385 while( !input.atEnd())
1386 {
1387 line = input.readLine();
1388 if ( input.status()!=QTextStream::Ok ){
1389 emit log(LOGERR, tr("readOBJFile : Warning! Could not read file properly!"));
1390 return;
1391 }
1392
1393 // Trim Both leading and trailing spaces
1394 line = line.trimmed();
1395
1396 // comment
1397 if ( line.isEmpty() || line[0] == QLatin1Char('#') || line[0].isSpace() ) {
1398 continue;
1399 }
1400
1401 stream.setString(&line);
1402
1403 //unless the keyWrd for the new line is not determined by the previous line
1404 //read it from stream
1405 if (nextKeyWrd == QLatin1String(""))
1406 stream >> keyWrd;
1407 else {
1408 keyWrd = nextKeyWrd;
1409 nextKeyWrd = QLatin1String("");
1410 }
1411
1412 //call - included obj files
1413 if (mode == NONE && keyWrd == QLatin1String("call")){
1414 firstFace = true;
1415
1416 QString include;
1417 include =stream.readLine();
1418
1419 //replace relative path
1420 QString includeStr = include.trimmed();
1421
1422 if ( !includeStr.isEmpty() ){
1423
1424 if (includeStr[0] == QLatin1Char('.')){
1425 includeStr = includeStr.right( includeStr.length()-1 );
1426
1427 QFileInfo fi(_filename);
1428
1429 includeStr = fi.path() + QDir::separator() + includeStr;
1430 }
1431
1432 _includes.append( includeStr );
1433 }
1434
1435 _importer.setObjectOptions(OBJImporter::NONE);
1436 }
1437
1438 // vertex
1439 else if (mode == NONE && keyWrd == QLatin1String("v"))
1440 {
1441 if (!firstFace)
1442 firstFace = true;
1443
1444 x = getDouble(stream);
1445 y = getDouble(stream);
1446 z = getDouble(stream);
1447
1448 if ( stream.status()==QTextStream::Ok )
1449 _importer.addVertex( OpenMesh::Vec3d(x,y,z) );
1450 else
1451 emit log(LOGERR, tr("Could not add Vertex %1. Possible NaN or Inf?").arg(_importer.n_vertices()));
1452 }
1453
1454 // group
1455 else if (mode == NONE && keyWrd == QLatin1String("g")){
1456 if (!firstFace)
1457 firstFace = true;
1458
1459 //give options to importer and reinitialize
1460 //for next object
1461
1462 QString grpName;
1463 grpName = stream.readLine();
1464
1465 if ( options & OBJImporter::TRIMESH ) TriMeshCount++;
1466 if ( options & OBJImporter::POLYMESH ) PolyMeshCount++;
1467
1468 int id = _importer.addGroup(grpName);
1469#if defined ENABLE_BSPLINECURVE_SUPPORT || defined ENABLE_BSPLINESURFACE_SUPPORT
1470 parentId = id;
1471 currentGroupName = grpName;
1472 currentGroupName.remove(QLatin1String(".obj"));
1473#endif
1474 _importer.setCurrentGroup(id);
1475
1476 // all following elements are in this group until
1477 // a new group is created
1478 inGroup = true;
1479
1480 _importer.setObjectOptions( options );
1481 options = OBJImporter::NONE;
1482 faceCount = 0;
1483 }
1484
1485 // face
1486 else if (mode == NONE && keyWrd == QLatin1String("f")){
1487
1488 if (firstFace) {
1489 // store faces in the default group if we aren't in a group already
1490 if (!inGroup)
1491 _importer.setCurrentGroup(0);
1492 firstFace = false;
1493 }
1494
1495 faceCount++;
1496
1497 int verticesPerFace = 0;
1498 int value;
1499
1500 // read full line after detecting a face
1501 QString faceLine;
1502 faceLine = stream.readLine();
1503 lineData.setString( &faceLine );
1504
1505 // work on the line until nothing left to read
1506 while ( !lineData.atEnd() )
1507 {
1508 // read one block from the line ( vertex/texCoord/normal )
1509 QString vertex;
1510 lineData >> vertex;
1511
1512 verticesPerFace++;
1513
1514
1515 //get the vertex component (vertex/texCoord/normal)
1516 int found=vertex.indexOf(QLatin1String("/"));
1517
1518 // parts are seperated by '/' So if no '/' found its the last component
1519 if( found != -1 ){
1520
1521 // read the index value
1522 QString vertexEntry = vertex.left(found);
1523 tmp.setString( &vertexEntry );
1524
1525 // Read current value
1526 tmp >> value;
1527
1528 if ( tmp.status()!=QTextStream::Ok )
1529 emit log(LOGERR, tr("readOBJFile : Error reading vertex index!"));
1530
1531 } else {
1532
1533 // last component of the vertex, read it.
1534 tmp.setString( &vertex );
1535 tmp >> value;
1536
1537 if ( tmp.status()!=QTextStream::Ok )
1538 emit log(LOGERR, tr("readOBJFile : Error reading vertex index!"));
1539 }
1540
1541
1542 if ( value < 0 ) {
1543 // Calculation of index :
1544 // -1 is the last vertex in the list
1545 // As obj counts from 1 and not zero add +1
1546 value = _importer.n_vertices() + value + 1;
1547 }
1548
1549 // Obj counts from 1 and not zero .. array counts from zero therefore -1
1550 // the importer has to know which vertices are used by the object for correct vertex order
1551 _importer.useVertex( value -1 );
1552 }
1553
1554
1555 if( verticesPerFace > 3 ) {
1556 options = OBJImporter::POLYMESH;
1557 _importer.setObjectOptions(options);
1558 } else if ( verticesPerFace == 3 ) {
1559 options = OBJImporter::TRIMESH;
1560 _importer.setObjectOptions(options);
1561 }
1562 }
1563
1564#ifdef ENABLE_BSPLINECURVE_SUPPORT
1565
1566 // curve
1567 if ( (mode == NONE && keyWrd == QLatin1String("curv")) || (mode == CURVE && keyWrd == QLatin1String("curv_add")) ){
1568 if (!firstFace)
1569 firstFace = true;
1570
1571 inGroup = false;
1572
1573 mode = CURVE;
1574
1575 if ( keyWrd == QLatin1String("curv") ) {
1576
1577 //give options to importer and reinitialize
1578 //for next object
1579 if ( options & OBJImporter::TRIMESH ) TriMeshCount++;
1580 if ( options & OBJImporter::POLYMESH ) PolyMeshCount++;
1581
1582 QString name = currentGroupName;
1583 if (name.size() == 0)
1584 name = QLatin1String("DefaultGroup");
1585
1586 name.append(QString("_curve_%1").arg(curveCount));
1587 int id = _importer.addGroup(name);
1588
1589 if (_importer.numCurves() == 0) {
1590 if (currentGroupName.size() == 0)
1591 _importer.setGroupName(id, QString("DefaultGroup"));
1592 else
1593 _importer.setGroupName(id, currentGroupName);
1594 } else {
1595 if (curveCount == 1) {
1596 int first = _importer.getCurveGroupId(0);
1597 QString tmp = _importer.groupName(first);
1598 tmp.append(QString("_curve_0"));
1599 _importer.setGroupName(first, tmp);
1600 }
1601 _importer.setGroupName(id, name);
1602 }
1603 _importer.setCurveParentId(id, parentId);
1604 _importer.setCurrentGroup(id);
1605 _importer.setCurveGroupId(curveCount, id);
1606 curveCount++;
1607
1608 _importer.setObjectOptions( options );
1609
1610 options = OBJImporter::CURVE;
1611 }
1612
1613 //get curve control points
1614 QString curveLine;
1615
1616 curveLine=stream.readLine();
1617
1618 // value may contain a / as line separator
1619 if ( curveLine.endsWith(QLatin1String("\\"))){
1620 curveLine = curveLine.left( curveLine.length()-1);
1621 nextKeyWrd = QLatin1String("curv_add");
1622 }
1623
1624 lineData.setString( &curveLine );
1625
1626 // Read knots at the beginning before the indices
1627 if ( keyWrd == QLatin1String("curv") ) {
1628 // skip next two doubles in stream
1629 getDouble(lineData);
1630 getDouble(lineData);
1631 }
1632
1633 // work on the line until nothing left to read
1634 while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1635 {
1636 int index = 0;
1637
1638 lineData >> index;
1639
1640 if ( lineData.status()==QTextStream::Ok ){
1641 // the importer has to know which vertices are used by the object for correct vertex order
1642 _importer.useVertex( index -1 );
1643 }
1644 }
1645
1646 }
1647
1648 // end
1649 else if (mode == CURVE && keyWrd == QLatin1String("end")){
1650
1651 mode = NONE;
1652
1653 _importer.setObjectOptions( options );
1654 options = OBJImporter::TRIMESH;
1655 faceCount = 0;
1656 }
1657#endif
1658
1659#ifdef ENABLE_BSPLINESURFACE_SUPPORT
1660
1661 // surface
1662 if ( (mode == NONE && keyWrd == QLatin1String("surf")) || (mode == SURFACE && keyWrd == QLatin1String("surf_add")) ){
1663 if (!firstFace)
1664 firstFace = true;
1665
1666 inGroup = false;
1667
1668 mode = SURFACE;
1669
1670 if ( keyWrd == QLatin1String("surf") ){
1671
1672 //give options to importer and reinitialize
1673 //for next object
1674 if ( options & OBJImporter::TRIMESH ) TriMeshCount++;
1675 if ( options & OBJImporter::POLYMESH ) PolyMeshCount++;
1676
1677 QString name = currentGroupName;
1678 if (name.size() == 0)
1679 name = QLatin1String("DefaultGroup");
1680
1681 name.append(QString("_surface_%1").arg(surfaceCount));
1682 int id = _importer.addGroup(name);
1683
1684 if (_importer.numSurfaces() == 0) {
1685 if (currentGroupName.size() == 0)
1686 _importer.setGroupName(id, "DefaultGroup");
1687 else
1688 _importer.setGroupName(id, currentGroupName);
1689 } else {
1690 if (surfaceCount == 1) {
1691 int first = _importer.getSurfaceGroupId(0);
1692 QString tmp = _importer.groupName(first);
1693 tmp.append(QString("_surface_0"));
1694 _importer.setGroupName(first, tmp);
1695 }
1696 _importer.setGroupName(id, name);
1697 }
1698 _importer.setSurfaceParentId(id, parentId);
1699 _importer.setCurrentGroup(id);
1700 _importer.setSurfaceGroupId(surfaceCount, id);
1701 surfaceCount++;
1702
1703 _importer.setObjectOptions( options );
1704
1705 options = OBJImporter::SURFACE;
1706 }
1707
1708 //get surface control points
1709 QString surfLine;
1710
1711 surfLine = stream.readLine();
1712
1713 // value may contain a / as line separator
1714 if ( surfLine.endsWith(QLatin1String("\\"))){
1715 surfLine = surfLine.left(surfLine.length()-1);
1716 nextKeyWrd = QLatin1String("surf_add");
1717 }
1718
1719 lineData.setString( &surfLine );
1720
1721 // work on the line until nothing left to read
1722 while ( !lineData.atEnd() && lineData.status()==QTextStream::Ok )
1723 {
1724 int index = 0;
1725
1726 lineData >> index;
1727
1728 if ( lineData.status()==QTextStream::Ok ){
1729 // the importer has to know which vertices are used by the object for correct vertex order
1730 _importer.useVertex( index -1 );
1731 }
1732 }
1733 }
1734
1735 // end
1736 else if (mode == SURFACE && keyWrd == QLatin1String("end")){
1737
1738 mode = NONE;
1739
1740 _importer.setObjectOptions( options );
1741 options = OBJImporter::TRIMESH;
1742 faceCount = 0;
1743 }
1744#endif
1745
1746 }
1747
1748 if(faceCount > 0) {
1749 if ( options & OBJImporter::TRIMESH ) TriMeshCount++;
1750 if ( options & OBJImporter::POLYMESH ) PolyMeshCount++;
1751 _importer.setObjectOptions( options );
1752 } else {
1753 // Mesh does not contain any faces
1754 PolyMeshCount++;
1755 if (keyWrd != QLatin1String("call")) {
1756 // we only have vertices and no faces
1757 if (keyWrd == QLatin1String("v") && !inGroup) {
1758 _importer.setCurrentGroup(0);
1759 // treat the file as a polymesh
1760 forceTriangleMesh_ = false;
1761 forcePolyMesh_ = true;
1762 _importer.setObjectOptions(OBJImporter::POLYMESH);
1763 _importer.forceMeshType( OBJImporter::POLYMESH );
1764 for (unsigned int i = 0; i < _importer.n_vertices(); ++i)
1765 _importer.useVertex(i);
1766 } else {
1767 // this is only a triangle mesh if the object is not a curve and not a surface
1768 // also ignore if it is set to NONE
1769 if (!(_importer.isCurve(_importer.currentGroup())) &&
1770 !(_importer.isSurface(_importer.currentGroup())) &&
1771 (_importer.isNone(_importer.currentGroup())) )
1772 _importer.setObjectOptions(OBJImporter::TRIMESH);
1773 }
1774 }
1775 }
1776
1777 if (TriMeshCount == 0 && PolyMeshCount == 0)
1778 {
1779 return;
1780 }
1781
1782 if (forceTriangleMesh_){
1783 _importer.forceMeshType( OBJImporter::TRIMESH );
1784 return;
1785 }
1786
1787 if (forcePolyMesh_){
1788 _importer.forceMeshType( OBJImporter::POLYMESH );
1789 return;
1790 }
1791
1792 // If we do not have a gui, we will always use the last default
1793 // If we need a gui and the triMeshHandling box is not generated (==0) we also use the last default
1794 if ( OpenFlipper::Options::gui() && triMeshHandling_ != 0 ){
1795
1796 switch( triMeshHandling_->currentIndex() ){
1797 case TYPEAUTODETECT : //Detect
1798 break;
1799
1800 case TYPEASK: //ask
1801 QMetaObject::invokeMethod(this,"handleTrimeshDialog",Qt::BlockingQueuedConnection);
1802 if (trimeshOptions_ == OBJImporter::TRIMESH )
1803 _importer.forceMeshType( OBJImporter::TRIMESH );
1804 else if (trimeshOptions_ == OBJImporter::POLYMESH)
1805 _importer.forceMeshType( OBJImporter::POLYMESH );
1806
1807 break;
1808
1809 case TYPEPOLY : //polyMesh
1810 _importer.forceMeshType( OBJImporter::POLYMESH ); break;
1811
1812 case TYPETRIANGLE : //trimesh
1813 _importer.forceMeshType( OBJImporter::TRIMESH ); break;
1814
1815 default: break;
1816
1817 }
1818
1819 }
1820
1821}
1822
1823void FileOBJPlugin::handleTrimeshDialog()
1824{
1825 QMessageBox msgBox;
1826 QPushButton *detectButton = msgBox.addButton(tr("Auto-Detect"), QMessageBox::ActionRole);
1827 QPushButton *triButton = msgBox.addButton(tr("Open as triangle mesh"), QMessageBox::ActionRole);
1828 QPushButton *polyButton = msgBox.addButton(tr("Open as poly mesh"), QMessageBox::ActionRole);
1829 msgBox.setWindowTitle( tr("Mesh types in file") );
1830 msgBox.setText( tr("You are about to open a file containing one or more mesh types. \n\n Which mesh type should be used?") );
1831 msgBox.setDefaultButton( detectButton );
1832 msgBox.exec();
1833
1834
1835 if (msgBox.clickedButton() == triButton)
1836 trimeshOptions_ = OBJImporter::TRIMESH ;
1837 else if (msgBox.clickedButton() == polyButton)
1838 trimeshOptions_ = OBJImporter::POLYMESH ;
1839}
1840
1841//-----------------------------------------------------------------------------------------------------
1842
1843int FileOBJPlugin::loadObject(QString _filename) {
1844
1845 OBJImporter importer;
1846
1847 //included filenames
1848 QStringList includes;
1849
1850 QFile sourceFile(_filename);
1851 if (!sourceFile.open(QFile::ReadOnly))
1852 {
1853 emit log(LOGERR, tr("readOBJFile : cannot open file %1 while checking Types").arg(_filename));
1854 return -1;
1855 }
1856 QByteArray bufferedFile = QByteArray();
1857 //load the entire file to ram if we have at least double the size as free memory.
1858 //otherwise the bytearray stays null and the file is read when it is processed.
1859 unsigned long freeMem = Utils::Memory::queryFreeRAM();
1860 unsigned long fs = sourceFile.size() / 1024 / 1024;
1861 if (freeMem >= 2*fs)
1862 {
1863 bufferedFile = sourceFile.readAll();
1864 }
1865
1866 //preprocess file and store types in ObjectOptions
1867 checkTypes( bufferedFile, _filename, importer, includes );
1868
1869 IdList objIDs;
1870
1871 //load included obj files
1872 for (int i=0; i < includes.size(); i++){
1873
1874 //int id = loadObject( includes[i], importer );
1875 int id = loadObject( includes[i] );
1876
1877 if (id != -1)
1878 objIDs.push_back( id );
1879 }
1880
1881 //add a group if we have includes
1882 if ( ! includes.empty() )
1883 importer.addGroup( QFileInfo(_filename).fileName() );
1884
1885 //check if something was found
1886 if ( importer.noOptions() && objIDs.empty() ){
1887
1888 forceTriangleMesh_ = false;
1889 forcePolyMesh_ = false;
1890
1891 return -1;
1892 }
1893
1894 //then parse the obj
1895 readOBJFile( bufferedFile, _filename, importer );
1896
1897 // finish up
1898 importer.finish();
1899
1900 int returnID = -1;
1901
1902 //perhaps add group
1903 if ( importer.numGroups() > 1){
1904
1905 bool dataControlExists = false;
1906 pluginExists( "datacontrol", dataControlExists );
1907
1908 if ( dataControlExists ){
1909#if defined ENABLE_BSPLINECURVE_SUPPORT || defined ENABLE_BSPLINESURFACE_SUPPORT
1910 std::map<int, QString> groupNames;
1911#endif
1912
1913#ifdef ENABLE_BSPLINECURVE_SUPPORT
1914 std::vector< std::vector<int> > curveGroups;
1915 std::vector<int> curveIds;
1916 int lastCurveParent = -2;
1917#endif
1918
1919#ifdef ENABLE_BSPLINESURFACE_SUPPORT
1920 std::vector< std::vector<int> > surfaceGroups;
1921 std::vector<int> surfaceIds;
1922 int lastSurfaceParent = -2;
1923#endif
1924 for(unsigned int i = 0; i < importer.groupCount(); i++) {
1925 // skip the object if it has no option
1926 // this can happen if the object only included other objects
1927 if ( !importer.isNone(i) ) {
1928 BaseObject* obj = importer.object(i);
1929 if(obj) {
1930#ifdef ENABLE_BSPLINECURVE_SUPPORT
1931 if ( importer.isCurve(i) ) {
1932 // store the parent group name for later grouping
1933 groupNames[obj->id()] = importer.groupName(importer.getCurveParentId(i));
1934
1935 // first curve group
1936 if (lastCurveParent == -2) {
1937 lastCurveParent = importer.getCurveParentId(i);
1938 curveIds.push_back(obj->id());
1939 BaseObject* parent = importer.object(lastCurveParent);
1940 if (parent) {
1941 curveIds.push_back(parent->id());
1942 // don't group the parent in the objIDs group
1943 std::vector<int>::iterator pos = std::find(objIDs.begin(), objIDs.end(), parent->id());
1944 if (pos != objIDs.end())
1945 objIDs.erase(pos);
1946 }
1947 // new curve group
1948 } else if (lastCurveParent != importer.getCurveParentId(i)) {
1949 lastCurveParent = importer.getCurveParentId(i);
1950 curveGroups.push_back(curveIds);
1951 curveIds.clear();
1952 curveIds.push_back(obj->id());
1953 BaseObject* parent = importer.object(lastCurveParent);
1954 if (parent) {
1955 curveIds.push_back(parent->id());
1956 // don't group the parent in the objIDs group
1957 std::vector<int>::iterator pos = std::find(objIDs.begin(), objIDs.end(), parent->id());
1958 if (pos != objIDs.end())
1959 objIDs.erase(pos);
1960 }
1961 // add curves to group
1962 } else
1963 curveIds.push_back(obj->id());
1964 }
1965
1966#endif
1967#ifdef ENABLE_BSPLINESURFACE_SUPPORT
1968 if ( importer.isSurface(i)) {
1969 // store the parent group name for later grouping
1970 groupNames[obj->id()] = importer.groupName(importer.getSurfaceParentId(i));
1971
1972 // first surface group
1973 if (lastSurfaceParent == -2) {
1974 lastSurfaceParent = importer.getSurfaceParentId(i);
1975 surfaceIds.push_back(obj->id());
1976 BaseObject* parent = importer.object(lastSurfaceParent);
1977 if (parent) {
1978 surfaceIds.push_back(parent->id());
1979 std::vector<int>::iterator pos = std::find(objIDs.begin(), objIDs.end(), parent->id());
1980 if (pos != objIDs.end())
1981 objIDs.erase(pos);
1982 }
1983 // new surface group
1984 } else if (lastSurfaceParent != importer.getSurfaceParentId(i)) {
1985 lastSurfaceParent = importer.getSurfaceParentId(i);
1986 surfaceGroups.push_back(surfaceIds);
1987 surfaceIds.clear();
1988 surfaceIds.push_back(obj->id());
1989 BaseObject* parent = importer.object(lastSurfaceParent);
1990 if (parent) {
1991 surfaceIds.push_back(parent->id());
1992 std::vector<int>::iterator pos = std::find(objIDs.begin(), objIDs.end(), parent->id());
1993 if (pos != objIDs.end())
1994 objIDs.erase(pos);
1995 }
1996 // add surfaces to group
1997 } else
1998 surfaceIds.push_back(obj->id());
1999
2000 }
2001#endif
2002 if ( (importer.isTriangleMesh(i) || importer.isPolyMesh(i) ) )
2003 objIDs.push_back( obj->id() );
2004 } else {
2005 std::cerr << "Object is NULL!" << std::endl;
2006 }
2007 }
2008 }
2009
2010#ifdef ENABLE_BSPLINECURVE_SUPPORT
2011 // add last group
2012 curveGroups.push_back(curveIds);
2013 std::vector< std::vector<int> >::iterator it = curveGroups.begin();
2014 for (; it != curveGroups.end(); ++it) {
2015 // only group if we have more than one curve
2016 if (it->size() > 2) {
2017 if (groupNames[it->back()].size() == 0)
2018 RPC::callFunctionValue<int>("datacontrol","groupObjects", *it, QFileInfo(_filename).fileName());
2019 else
2020 RPC::callFunctionValue<int>("datacontrol","groupObjects", *it, groupNames[it->back()]);
2021 }
2022 }
2023#endif
2024
2025#ifdef ENABLE_BSPLINESURFACE_SUPPORT
2026 // add last group
2027 surfaceGroups.push_back(surfaceIds);
2028 std::vector< std::vector<int> >::iterator it2 = surfaceGroups.begin();
2029 for (; it2 != surfaceGroups.end(); ++it2) {
2030 // only group if we have more than one surface
2031 if (it2->size() > 2) {
2032 if (groupNames[it2->back()].size() == 0)
2033 RPC::callFunctionValue<int>("datacontrol","groupObjects", *it2, QFileInfo(_filename).fileName());
2034 else
2035 RPC::callFunctionValue<int>("datacontrol","groupObjects", *it2, groupNames[it2->back()]);
2036 }
2037 }
2038#endif
2039
2040 // only group if we have more than one object
2041 if (objIDs.size() > 1)
2042 returnID = RPC::callFunctionValue<int>("datacontrol","groupObjects", objIDs, importer.groupName(0));
2043 }
2044 }
2045
2046 //check all new objects which are created for each group
2047 for(unsigned int i=0; i < importer.groupCount(); i++){
2048
2049 BaseObject* object = importer.object(i);
2050 if(object == NULL) continue;
2051
2052 object->setFromFileName(_filename);
2053
2054 //remember the id of the first opened object
2055 if ( returnID == -1)
2056 returnID = object->id();
2057
2058 //handle new PolyMeshes
2059 PolyMeshObject* polyMeshObj = dynamic_cast< PolyMeshObject* > (object);
2060
2061 if ( polyMeshObj ){
2062
2063 if ( !importer.hasNormals(i) )
2064 polyMeshObj->mesh()->update_normals();
2065 else
2066 polyMeshObj->mesh()->update_face_normals();
2067 }
2068
2069 //handle new TriMeshes
2070 TriMeshObject* triMeshObj = dynamic_cast< TriMeshObject* > (object);
2071
2072 if ( triMeshObj ){
2073
2074 if ( !importer.hasNormals(i) || importer.hasOption( i, OBJImporter::FORCE_NONORMALS ) )
2075 triMeshObj->mesh()->update_normals();
2076 else
2077 triMeshObj->mesh()->update_face_normals();
2078 }
2079
2080#ifdef ENABLE_BSPLINECURVE_SUPPORT
2081 //handle new BSplineCurves
2082 BSplineCurveObject* bscObj = dynamic_cast< BSplineCurveObject* > (object);
2083
2084 if ( bscObj ){
2085 bscObj->splineCurveNode()->updateGeometry();
2086 }
2087#endif
2088
2089
2090 //textures
2091 if ( importer.hasTexture(i) && !importer.hasOption( i, OBJImporter::FORCE_NOTEXTURES ) ){
2092
2093 //add the textures to the object
2094 addTextures( importer, i );
2095
2096 //set the texture index property to be used
2097 emit setTextureMode("OBJ Data","indexProperty=OriginalTexIndexMapping", object->id() );
2098
2099 emit switchTexture("OBJ Data", object->id() );
2100
2102
2103 }
2104
2105 //general stuff
2106 emit updatedObject( object->id(), UPDATE_ALL );
2107 emit openedFile( object->id() );
2108 }
2109
2110 forceTriangleMesh_ = false;
2111 forcePolyMesh_ = false;
2112
2113// if ( topLevelObj )
2114// OpenFlipper::Options::loadingSettings(false);
2115 return returnID;
2116}
2117
2118//-----------------------------------------------------------------------------------------------------
2119
2121int FileOBJPlugin::loadObject(QString _filename, DataType _type){
2122
2123 if ( _type == DATA_TRIANGLE_MESH )
2124 forceTriangleMesh_ = true;
2125 else if ( _type == DATA_POLY_MESH )
2126 forcePolyMesh_ = true;
2127
2128 return loadObject(_filename);
2129}
2130
2131//-----------------------------------------------------------------------------------------------------
2132
2133bool FileOBJPlugin::saveObject(int _id, QString _filename)
2134{
2135 BaseObjectData* object;
2136 if ( !PluginFunctions::getObject(_id,object) ) {
2137 emit log(LOGERR, tr("saveObject : cannot get object id %1 for save name %2").arg(_id).arg(_filename) );
2138 return false;
2139 }
2140
2141 //open output stream
2142 std::string filename = std::string( _filename.toUtf8() );
2143
2144 std::fstream objStream( filename.c_str(), std::ios_base::out );
2145
2146 if ( !objStream ){
2147
2148 emit log(LOGERR, tr("saveObject : cannot not open file %1").arg(_filename) );
2149 return false;
2150 }
2151
2152 //write object
2153 if ( object->dataType( DATA_POLY_MESH ) ) {
2154
2155 object->setFromFileName(_filename);
2156 object->setName(object->filename());
2157
2158 PolyMeshObject* polyObj = dynamic_cast<PolyMeshObject* >( object );
2159
2160
2161 if ( writeMesh( objStream, _filename, *polyObj->mesh(), polyObj->id() ) ){
2162
2163 emit log(LOGINFO, tr("Saved object to ") + _filename );
2164 objStream.close();
2165 return true;
2166
2167 } else {
2168
2169 emit log(LOGERR, tr("Unable to save ") + _filename);
2170 objStream.close();
2171 return false;
2172 }
2173
2174 } else if ( object->dataType( DATA_TRIANGLE_MESH ) ) {
2175
2176 object->setFromFileName(_filename);
2177 object->setName(object->filename());
2178
2179 TriMeshObject* triObj = dynamic_cast<TriMeshObject* >( object );
2180
2181
2182 if ( writeMesh( objStream, _filename, *triObj->mesh(), triObj->id() )) {
2183
2184 emit log(LOGINFO, tr("Saved object to ") + _filename );
2185 objStream.close();
2186 return true;
2187
2188 } else {
2189
2190 emit log(LOGERR, tr("Unable to save ") + _filename );
2191 objStream.close();
2192 return false;
2193 }
2194
2195#ifdef ENABLE_BSPLINECURVE_SUPPORT
2196 } else if ( object->dataType( DATA_BSPLINE_CURVE ) ) {
2197
2198 object->setFromFileName(_filename);
2199 object->setName(object->filename());
2200
2201 BSplineCurveObject* bscObj = dynamic_cast<BSplineCurveObject* >( object );
2202
2203
2204 if ( writeCurve( objStream, _filename, bscObj->splineCurve()) ) {
2205
2206 emit log(LOGINFO, tr("Saved object to ") + _filename );
2207 objStream.close();
2208 return true;
2209
2210 } else {
2211
2212 emit log(LOGERR, tr("Unable to save ") + _filename );
2213 objStream.close();
2214 return false;
2215 }
2216#endif
2217
2218#ifdef ENABLE_BSPLINESURFACE_SUPPORT
2219 } else if ( object->dataType( DATA_BSPLINE_SURFACE ) ) {
2220
2221 object->setFromFileName(_filename);
2222 object->setName(object->filename());
2223
2224 BSplineSurfaceObject* bssObj = dynamic_cast<BSplineSurfaceObject* >( object );
2225
2226
2227 if ( writeSurface( objStream, _filename, bssObj->splineSurface()) ) {
2228
2229 emit log(LOGINFO, tr("Saved object to ") + _filename );
2230 objStream.close();
2231 return true;
2232
2233 } else {
2234
2235 emit log(LOGERR, tr("Unable to save ") + object->path() + OpenFlipper::Options::dirSeparator() + object->name());
2236 objStream.close();
2237 return false;
2238 }
2239#endif
2240
2241 } else {
2242
2243 emit log(LOGERR, tr("Unable to save (object is not a compatible mesh type)"));
2244 objStream.close();
2245 return false;
2246 }
2247}
2248
2249//-----------------------------------------------------------------------------------------------------
2250
2251void FileOBJPlugin::slotHandleCheckBoxes(bool _checked) {
2252
2253 if(saveCopyTextures_) {
2254 saveCreateTexFolder_->setEnabled(_checked);
2255 saveCreateTexFolder_->setChecked(_checked);
2256 }
2257}
2258
2259//-----------------------------------------------------------------------------------------------------
2260
2261QWidget* FileOBJPlugin::saveOptionsWidget(QString /*_currentFilter*/) {
2262
2263 if (saveOptions_ == 0){
2264 //generate widget
2265 saveOptions_ = new QWidget();
2266 QVBoxLayout* layout = new QVBoxLayout();
2267 layout->setAlignment(Qt::AlignTop);
2268
2269 saveFaceColor_ = new QCheckBox("Save Face Colors");
2270 layout->addWidget(saveFaceColor_);
2271
2272 saveFaceColorOverride_ = new QCheckBox("Save Face Colors Error Override (Save, even if colors contain errors!)");
2273 saveFaceColorOverride_->setToolTip("If color values of a mesh are not initialized, OpenFlipper will detect that and stop writing materials. This option will force it to write them anyway.");
2274 layout->addWidget(saveFaceColorOverride_);
2275
2276 saveAlpha_ = new QCheckBox("Save Color Alpha");
2277 layout->addWidget(saveAlpha_);
2278
2279 saveNormals_ = new QCheckBox("Save Normals");
2280 layout->addWidget(saveNormals_);
2281
2282 saveTexCoords_ = new QCheckBox("Save Texture Coordinates");
2283 layout->addWidget(saveTexCoords_);
2284
2285 saveTextures_ = new QCheckBox("Save Textures");
2286 layout->addWidget(saveTextures_);
2287
2288 saveCopyTextures_ = new QCheckBox("Copy Texture Files");
2289 layout->addWidget(saveCopyTextures_);
2290
2291 saveCreateTexFolder_ = new QCheckBox("Create Textures Folder");
2292 layout->addWidget(saveCreateTexFolder_);
2293
2294 savePrecisionLabel_ = new QLabel("Writer Precision");
2295 layout->addWidget(savePrecisionLabel_);
2296
2297 savePrecision_ = new QSpinBox();
2298 savePrecision_->setMinimum(1);
2299 savePrecision_->setMaximum(std::numeric_limits<double>::max_digits10);
2300 layout->addWidget(savePrecision_);
2301
2302 saveDefaultButton_ = new QPushButton("Make Default");
2303 layout->addWidget(saveDefaultButton_);
2304
2305 saveOptions_->setLayout(layout);
2306
2307 connect(saveDefaultButton_, SIGNAL(clicked()), this, SLOT(slotSaveDefault()));
2308 connect(saveCopyTextures_, SIGNAL(toggled(bool)), this, SLOT(slotHandleCheckBoxes(bool)));
2309
2310 saveFaceColor_->setChecked( OpenFlipperSettings().value("FileObj/Save/FaceColor",true).toBool() );
2311 saveFaceColorOverride_->setChecked( OpenFlipperSettings().value("FileObj/Save/FaceColorOverrise",false).toBool() );
2312 saveAlpha_->setChecked( OpenFlipperSettings().value("FileObj/Save/Alpha",true).toBool() );
2313 saveNormals_->setChecked( OpenFlipperSettings().value("FileObj/Save/Normals",true).toBool() );
2314 saveTexCoords_->setChecked( OpenFlipperSettings().value("FileObj/Save/TexCoords",true).toBool() );
2315 saveTextures_->setChecked( OpenFlipperSettings().value("FileObj/Save/Textures",true).toBool() );
2316 saveCopyTextures_->setChecked( OpenFlipperSettings().value("FileObj/Save/CopyTextures",true).toBool() );
2317 saveCreateTexFolder_->setChecked( OpenFlipperSettings().value("FileObj/Save/CreateTexFolder",true).toBool() );
2318 savePrecision_->setValue( OpenFlipperSettings().value("FileObj/Save/Precision",
2319 std::numeric_limits<double>::max_digits10
2320 ).toInt() );
2321
2322 slotHandleCheckBoxes(saveCopyTextures_->isChecked());
2323 }
2324
2325 return saveOptions_;
2326}
2327
2328//-----------------------------------------------------------------------------------------------------
2329
2330QWidget* FileOBJPlugin::loadOptionsWidget(QString /*_currentFilter*/) {
2331
2332 if (loadOptions_ == 0){
2333 //generate widget
2334 loadOptions_ = new QWidget();
2335 QVBoxLayout* layout = new QVBoxLayout();
2336 layout->setAlignment(Qt::AlignTop);
2337
2338 QLabel* label = new QLabel(tr("If file contains meshes:"));
2339
2340 layout->addWidget(label);
2341
2342 triMeshHandling_ = new QComboBox();
2343 triMeshHandling_->addItem( tr("Detect correct type") );
2344 triMeshHandling_->addItem( tr("Ask") );
2345 triMeshHandling_->addItem( tr("Open as PolyMesh") );
2346 triMeshHandling_->addItem( tr("Open as TriangleMesh") );
2347
2348 layout->addWidget(triMeshHandling_);
2349
2350 loadFaceColor_ = new QCheckBox("Load Face Colors");
2351 layout->addWidget(loadFaceColor_);
2352
2353 loadNormals_ = new QCheckBox("Load Normals");
2354 layout->addWidget(loadNormals_);
2355
2356 loadTexCoords_ = new QCheckBox("Load Texture Coordinates");
2357 layout->addWidget(loadTexCoords_);
2358
2359 loadTextures_ = new QCheckBox("Load Textures");
2360 layout->addWidget(loadTextures_);
2361
2362 loadDefaultButton_ = new QPushButton("Make Default");
2363 layout->addWidget(loadDefaultButton_);
2364
2365 loadOptions_->setLayout(layout);
2366
2367 connect(loadDefaultButton_, SIGNAL(clicked()), this, SLOT(slotLoadDefault()));
2368
2369
2370 triMeshHandling_->setCurrentIndex(OpenFlipperSettings().value("FileObj/Load/TriMeshHandling",TYPEAUTODETECT).toInt() );
2371
2372 loadFaceColor_->setChecked( OpenFlipperSettings().value("FileObj/Load/FaceColor",true).toBool() );
2373 loadNormals_->setChecked( OpenFlipperSettings().value("FileObj/Load/Normals",true).toBool() );
2374 loadTexCoords_->setChecked( OpenFlipperSettings().value("FileObj/Load/TexCoords",true).toBool() );
2375 loadTextures_->setChecked( OpenFlipperSettings().value("FileObj/Load/Textures",true).toBool() );
2376 }
2377
2378 return loadOptions_;
2379}
2380
2382 OpenFlipperSettings().setValue( "FileObj/Load/FaceColor", loadFaceColor_->isChecked() );
2383 OpenFlipperSettings().setValue( "FileObj/Load/Normals", loadNormals_->isChecked() );
2384 OpenFlipperSettings().setValue( "FileObj/Load/TexCoords", loadTexCoords_->isChecked() );
2385 OpenFlipperSettings().setValue( "FileObj/Load/Textures", loadTextures_->isChecked() );
2386 OpenFlipperSettings().setValue("FileObj/Load/TriMeshHandling", triMeshHandling_->currentIndex() );
2387
2388 OpenFlipperSettings().setValue( "Core/File/UseLoadDefaults", true );
2389}
2390
2391
2393 OpenFlipperSettings().setValue( "FileObj/Save/FaceColor", saveFaceColor_->isChecked() );
2394 OpenFlipperSettings().setValue( "FileObj/Save/FaceColorOverride", saveFaceColorOverride_->isChecked() );
2395 OpenFlipperSettings().setValue( "FileObj/Save/Normals", saveNormals_->isChecked() );
2396 OpenFlipperSettings().setValue( "FileObj/Save/TexCoords", saveTexCoords_->isChecked() );
2397 OpenFlipperSettings().setValue( "FileObj/Save/Textures", saveTextures_->isChecked() );
2398 OpenFlipperSettings().setValue( "FileObj/Save/CopyTextures", saveCopyTextures_->isChecked() );
2399 OpenFlipperSettings().setValue( "FileObj/Save/CreateTexFolder", saveCreateTexFolder_->isChecked() );
2400 OpenFlipperSettings().setValue( "FileObj/Save/Precision", savePrecision_->value() );
2401}
2402
2403
2404
#define DATA_BSPLINE_CURVE
#define DATA_BSPLINE_SURFACE
const DataType DATA_GROUP(1)
Items used for Grouping.
std::vector< int > IdList
Standard Type for id Lists used for scripting.
Definition DataTypes.hh:181
@ LOGERR
@ LOGWARN
@ LOGINFO
#define DATA_POLY_MESH
Definition PolyMesh.hh:59
#define DATA_TRIANGLE_MESH
ACG::SceneGraph::BSplineCurveNodeT< BSplineCurve > * splineCurveNode()
Get the scenegraph Node.
BSplineCurve * splineCurve()
return a pointer to the spline curve
BSplineSurface * splineSurface()
return a pointer to the spline curve
QString name() const
return the name of the object. The name defaults to NONAME if unset.
QString filename() const
return the filename of the object
bool dataType(DataType _type) const
QString path() const
return the path to the object ( defaults to "." if unset )
void setFromFileName(const QString &_filename)
int id() const
Predefined datatypes.
Definition DataTypes.hh:83
DataType supportedType()
Return your supported object type( e.g. DATA_TRIANGLE_MESH )
Definition FileOBJ.cc:133
void initializePlugin()
Initialize Plugin.
Definition FileOBJ.cc:116
QWidget * saveOptionsWidget(QString)
Definition FileOBJ.cc:2261
QString getSaveFilters()
Definition FileOBJ.cc:127
void slotLoadDefault()
Slot called when user wants to save the given Load options as default.
Definition FileOBJ.cc:2381
void convertToOBJName(QString &_name)
Convert non-valid filenames (e.g. of groups that end with .jpg) to valid .objs.
Definition FileOBJ.cc:428
void checkTypes(QByteArray &_bufferedFile, QString _filename, OBJImporter &_importer, QStringList &_includes)
Reader functions.
Definition FileOBJ.cc:1321
void slotSaveDefault()
Slot called when user wants to save the given Save options as default.
Definition FileOBJ.cc:2392
QString name()
Return a name for the plugin.
Definition FileOBJ.hh:158
int loadObject(QString _filename)
Loads Object and converts it to a triangle mesh if possible.
Definition FileOBJ.cc:1843
FileOBJPlugin()
Constructor.
Definition FileOBJ.cc:82
void backupTextureCoordinates(MeshT &_mesh)
creates a backup of the original per vertex/face texture coordinates
Definition FileOBJ.cc:439
QWidget * loadOptionsWidget(QString)
Definition FileOBJ.cc:2330
QString getLoadFilters()
Definition FileOBJ.cc:121
MeshT * mesh()
return a pointer to the mesh
unsigned int groupCount()
Number of groups currently stored in the importer.
Vec3d vertex(unsigned int _index)
get vertex with given index
void addMaterial(std::string _materialName)
Add a material.
void setDegreeV(int _degree)
set degree V direction
void setVertexTexCoord(VertexHandle _vh, int _texCoordID)
set vertex texture coordinate
unsigned int n_vertices()
Global Properties.
void useVertex(int _vertex_index)
used vertices
void setObjectOptions(ObjectOptions _options)
void addFace(const VHandles &_indices)
add a face with indices _indices refering to vertices
int addTexCoord(const Vec2f &_coord)
add texture coordinates
VertexHandle addVertex(const Vec3d &_point)
add a vertex with coordinate _point
int degreeU()
get current degree
void setDegreeU(int _degree)
set degree
bool noOptions()
Return true if the importer has no options stored.
QString path()
Path of the OBJ file.
void forceMeshType(ObjectOptions _meshType)
force all meshes to be opened with specific type
int addNormal(const Vec3d &_normal)
add a normal
void setNormal(int _index, int _normalID)
set vertex normal
bool hasOption(unsigned int _id, ObjectOptions _option)
check if object with given id has given option
BaseObject * object(int _groupId)
return object for the given group
void setOption(ObjectOptionsE _option)
Set Object Option.
void setObject(BaseObject *_object, int _groupId)
add an object
MaterialList & materials()
return all loaded materials
int degreeV()
get current degree
const std::vector< std::string > usedMaterials(unsigned int _objectID)
used materials
bool hasNormals(int _objectID)
Query Object Options.
int currentGroup()
Get the id of the current group.
void setValue(const QString &key, const QVariant &value)
Wrapper function which makes it possible to enable Debugging output with -DOPENFLIPPER_SETTINGS_DEBUG...
void update_face_normals()
Update normal vectors for all faces.
void update_normals()
Compute normals for all primitives.
Type for a Meshobject containing a poly mesh.
Definition PolyMesh.hh:65
Type for a MeshObject containing a triangle mesh.
const UpdateType UPDATE_ALL(UpdateTypeSet(1))
Identifier for all updates.
DrawMode SOLID_2DTEXTURED_FACE_SHADED
draw per halfedge textured faces
Definition DrawModes.cc:97
DrawMode NONE
not a valid draw mode
Definition DrawModes.cc:71
bool getObject(const int _identifier, BaseObject *&_object)
Get the object which has the given identifier.
void setDrawMode(const ACG::SceneGraph::DrawModes::DrawMode &_mode, int _viewer)
Set the draw Mode of a Viewer. .